perf: performance improvments
- switch to BTreeMap for consistant ordering of fields (less UI updates) - cache calls to expensive getters in the vm via witha Proxy on DeviceRefs - have DeviceMixin explicitly subscribe to device property changes to limit updates - split fields into seperate componate to avoid rerender of other components - speedup ic10emu_wasm DeviceRef::get_slots by only calling serde once. Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import type {
|
||||
Aliases,
|
||||
Defines,
|
||||
Pins,
|
||||
LogicType,
|
||||
} from "ic10emu_wasm";
|
||||
import { structuralEqual } from "utils";
|
||||
import { LitElement, PropertyValueMap } from "lit";
|
||||
@@ -21,6 +22,7 @@ type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export declare class VMDeviceMixinInterface {
|
||||
deviceID: number;
|
||||
activeICId: number;
|
||||
device: DeviceRef;
|
||||
name: string | null;
|
||||
nameHash: number | null;
|
||||
@@ -41,8 +43,24 @@ export declare class VMDeviceMixinInterface {
|
||||
_handleDeviceModified(e: CustomEvent): void;
|
||||
updateDevice(): void;
|
||||
updateIC(): void;
|
||||
subscribe(...sub: VMDeviceMixinSubscription[]): void;
|
||||
unsubscribe(filter: (sub: VMDeviceMixinSubscription) => boolean): void;
|
||||
}
|
||||
|
||||
export type VMDeviceMixinSubscription =
|
||||
| "name"
|
||||
| "nameHash"
|
||||
| "prefabName"
|
||||
| "fields"
|
||||
| "slots"
|
||||
| "slots-count"
|
||||
| "reagents"
|
||||
| "connections"
|
||||
| "ic"
|
||||
| "active-ic"
|
||||
| { field: LogicType }
|
||||
| { slot: number };
|
||||
|
||||
export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
@@ -57,8 +75,23 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
@state() private deviceSubscriptions: VMDeviceMixinSubscription[] = [];
|
||||
|
||||
subscribe(...sub: VMDeviceMixinSubscription[]) {
|
||||
this.deviceSubscriptions = this.deviceSubscriptions.concat(sub);
|
||||
}
|
||||
|
||||
// remove subscripotions matching the filter
|
||||
unsubscribe(filter: (sub: VMDeviceMixinSubscription) => boolean) {
|
||||
this.deviceSubscriptions = this.deviceSubscriptions.filter(
|
||||
(sub) => !filter(sub),
|
||||
);
|
||||
}
|
||||
|
||||
device: DeviceRef;
|
||||
|
||||
@state() activeICId: number;
|
||||
|
||||
@state() name: string | null = null;
|
||||
@state() nameHash: number | null = null;
|
||||
@state() prefabName: string | null;
|
||||
@@ -107,7 +140,6 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
this._handleDevicesModified.bind(this),
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
_handleDeviceModified(e: CustomEvent) {
|
||||
@@ -115,49 +147,93 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
const activeIc = window.VM.vm.activeIC;
|
||||
if (this.deviceID === id) {
|
||||
this.updateDevice();
|
||||
} else if (id === activeIc.id) {
|
||||
this.requestUpdate();
|
||||
} else if (id === activeIc.id && this.deviceSubscriptions.includes("active-ic")) {
|
||||
this.updateDevice();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDevicesModified(e: CustomEvent) {
|
||||
_handleDevicesModified(e: CustomEvent<number[]>) {
|
||||
const activeIc = window.VM.vm.activeIC;
|
||||
const ids = e.detail;
|
||||
this.requestUpdate();
|
||||
if (ids.includes(this.deviceID)) {
|
||||
this.updateDevice()
|
||||
} else if (ids.includes(activeIc.id) && this.deviceSubscriptions.includes("active-ic")) {
|
||||
this.updateDevice();
|
||||
}
|
||||
}
|
||||
|
||||
updateDevice() {
|
||||
this.device = window.VM.vm.devices.get(this.deviceID)!;
|
||||
|
||||
const name = this.device.name ?? null;
|
||||
if (this.name !== name) {
|
||||
this.name = name;
|
||||
}
|
||||
const nameHash = this.device.nameHash ?? null;
|
||||
if (this.nameHash !== nameHash) {
|
||||
this.nameHash = nameHash;
|
||||
}
|
||||
const prefabName = this.device.prefabName ?? null;
|
||||
if (this.prefabName !== prefabName) {
|
||||
this.prefabName = prefabName;
|
||||
}
|
||||
const fields = this.device.fields;
|
||||
if (!structuralEqual(this.fields, fields)) {
|
||||
this.fields = fields;
|
||||
}
|
||||
const slots = this.device.slots;
|
||||
if (!structuralEqual(this.slots, slots)) {
|
||||
this.slots = slots;
|
||||
}
|
||||
const reagents = this.device.reagents;
|
||||
if (!structuralEqual(this.reagents, reagents)) {
|
||||
this.reagents = reagents;
|
||||
}
|
||||
const connections = this.device.connections;
|
||||
if (!structuralEqual(this.connections, connections)) {
|
||||
this.connections = connections;
|
||||
}
|
||||
if (typeof this.device.ic !== "undefined") {
|
||||
this.updateIC();
|
||||
for (const sub of this.deviceSubscriptions) {
|
||||
if (typeof sub === "string") {
|
||||
if (sub == "name") {
|
||||
const name = this.device.name ?? null;
|
||||
if (this.name !== name) {
|
||||
this.name = name;
|
||||
}
|
||||
} else if (sub === "nameHash") {
|
||||
const nameHash = this.device.nameHash ?? null;
|
||||
if (this.nameHash !== nameHash) {
|
||||
this.nameHash = nameHash;
|
||||
}
|
||||
} else if (sub === "prefabName") {
|
||||
const prefabName = this.device.prefabName ?? null;
|
||||
if (this.prefabName !== prefabName) {
|
||||
this.prefabName = prefabName;
|
||||
}
|
||||
} else if (sub === "fields") {
|
||||
const fields = this.device.fields;
|
||||
if (!structuralEqual(this.fields, fields)) {
|
||||
this.fields = fields;
|
||||
}
|
||||
} else if (sub === "slots") {
|
||||
const slots = this.device.slots;
|
||||
if (!structuralEqual(this.slots, slots)) {
|
||||
this.slots = slots;
|
||||
}
|
||||
} else if (sub === "slots-count") {
|
||||
const slots = this.device.slots;
|
||||
if (typeof this.slots === "undefined") {
|
||||
this.slots = slots;
|
||||
} else if (this.slots.length !== slots.length) {
|
||||
this.slots = slots;
|
||||
}
|
||||
} else if (sub === "reagents") {
|
||||
const reagents = this.device.reagents;
|
||||
if (!structuralEqual(this.reagents, reagents)) {
|
||||
this.reagents = reagents;
|
||||
}
|
||||
} else if (sub === "connections") {
|
||||
const connections = this.device.connections;
|
||||
if (!structuralEqual(this.connections, connections)) {
|
||||
this.connections = connections;
|
||||
}
|
||||
} else if (sub === "ic") {
|
||||
if (typeof this.device.ic !== "undefined") {
|
||||
this.updateIC();
|
||||
}
|
||||
} else if (sub === "active-ic") {
|
||||
const activeIc = window.VM.vm?.activeIC;
|
||||
if (this.activeICId !== activeIc.id) {
|
||||
this.activeICId = activeIc.id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( "field" in sub ) {
|
||||
const fields = this.device.fields;
|
||||
if (this.fields.get(sub.field) !== fields.get(sub.field)) {
|
||||
this.fields = fields;
|
||||
}
|
||||
} else if ( "slot" in sub) {
|
||||
const slots = this.device.slots;
|
||||
if (typeof this.slots === "undefined" || this.slots.length < sub.slot) {
|
||||
this.slots = slots;
|
||||
} else if (!structuralEqual(this.slots[sub.slot], slots[sub.slot])) {
|
||||
this.slots = slots;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,10 +300,12 @@ export const VMActiveICMixin = <T extends Constructor<LitElement>>(
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
disconnectedCallback(): void {
|
||||
window.VM.get().then((vm) =>
|
||||
vm.removeEventListener("vm-run-ic", this._handleDeviceModified.bind(this)),
|
||||
vm.removeEventListener(
|
||||
"vm-run-ic",
|
||||
this._handleDeviceModified.bind(this),
|
||||
),
|
||||
);
|
||||
window.App.app.session.removeEventListener(
|
||||
"session-active-ic",
|
||||
@@ -274,7 +352,7 @@ export const VMDeviceDBMixin = <T extends Constructor<LitElement>>(
|
||||
window.VM.vm.removeEventListener(
|
||||
"vm-device-db-loaded",
|
||||
this._handleDeviceDBLoad.bind(this),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_handleDeviceDBLoad(e: CustomEvent) {
|
||||
|
||||
Reference in New Issue
Block a user