From 0de4b99bae9481754a0e6f00e5bcf8632405d217 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 12 Nov 2022 17:31:19 +0100 Subject: [PATCH 1/2] WIP --- .../components/outputs/DefaultImageOutput.tsx | 1 + src/renderer/components/outputs/GenericOutput.tsx | 8 +++++--- .../components/outputs/LargeImageOutput.tsx | 7 ++++--- src/renderer/hooks/useInputHashes.ts | 14 +++++++++++--- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/renderer/components/outputs/DefaultImageOutput.tsx b/src/renderer/components/outputs/DefaultImageOutput.tsx index cbef6cc1c..1ba3d9ab6 100644 --- a/src/renderer/components/outputs/DefaultImageOutput.tsx +++ b/src/renderer/components/outputs/DefaultImageOutput.tsx @@ -45,6 +45,7 @@ export const DefaultImageOutput = memo( const inputHash = useContextSelector(GlobalVolatileContext, (c) => c.inputHashes.get(id)); const [value, valueInputHash] = useOutputData(outputId); const sameHash = valueInputHash === inputHash; + useEffect(() => { if (value && sameHash) { setManualOutputType( diff --git a/src/renderer/components/outputs/GenericOutput.tsx b/src/renderer/components/outputs/GenericOutput.tsx index 3964106ec..344df6baa 100644 --- a/src/renderer/components/outputs/GenericOutput.tsx +++ b/src/renderer/components/outputs/GenericOutput.tsx @@ -19,11 +19,13 @@ export const GenericOutput = memo( const schema = schemata.get(schemaId); - const [value] = useOutputData(outputId); + const inputHash = useContextSelector(GlobalVolatileContext, (c) => c.inputHashes.get(id)); + const [value, valueInputHash] = useOutputData(outputId); + const sameHash = valueInputHash === inputHash; useEffect(() => { if (isStartingNode(schema)) { - if (value !== undefined) { + if (value !== undefined && sameHash) { if (kind === 'text') { setManualOutputType(id, outputId, literal(value as string)); } else if (kind === 'directory') { @@ -39,7 +41,7 @@ export const GenericOutput = memo( setManualOutputType(id, outputId, undefined); } } - }, [id, schemaId, value, kind, outputId, schema, setManualOutputType]); + }, [id, schemaId, value, sameHash, kind, outputId, schema, setManualOutputType]); return ( c.zoom); const [value, valueInputHash] = useOutputData(outputId); - const stale = value !== undefined && valueInputHash !== inputHash; + const sameHash = valueInputHash === inputHash; + const stale = value !== undefined && !sameHash; useEffect(() => { if (isStartingNode(schema)) { - if (value) { + if (value && sameHash) { setManualOutputType( id, outputId, @@ -44,7 +45,7 @@ export const LargeImageOutput = memo( setManualOutputType(id, outputId, undefined); } } - }, [id, schemaId, value, outputId, schema, setManualOutputType]); + }, [id, schemaId, value, sameHash, outputId, schema, setManualOutputType]); const imgBgColor = 'var(--node-image-preview-bg)'; const fontColor = 'var(--node-image-preview-color)'; diff --git a/src/renderer/hooks/useInputHashes.ts b/src/renderer/hooks/useInputHashes.ts index f8f086eb0..ab16463ff 100644 --- a/src/renderer/hooks/useInputHashes.ts +++ b/src/renderer/hooks/useInputHashes.ts @@ -31,9 +31,9 @@ const computeInputHashes = ( const schema = schemata.get(node.data.schemaId); const inputs: string[] = [node.data.schemaId]; - for (const { id: inputId } of schema.inputs) { + for (const input of schema.inputs) { const connectedEdge = byTargetHandle.get( - stringifyTargetHandle({ nodeId: node.id, inputId }) + stringifyTargetHandle({ nodeId: node.id, inputId: input.id }) ); if (connectedEdge) { const source = byId.get(connectedEdge.source); @@ -45,7 +45,15 @@ const computeInputHashes = ( } } - const value = node.data.inputData[inputId]; + const parent = byId.get(node.parentNode!); + if (input.kind === 'generic' && !input.hasHandle && parent) { + // Auto inputs of iterator helper nodes depend on the parent iterator + inputs.push(getInputHash(parent)); + // eslint-disable-next-line no-continue + continue; + } + + const value = node.data.inputData[input.id]; // eslint-disable-next-line eqeqeq if (value == undefined) { inputs.push(EMPTY); From d5f04d40a4b748753ea0f3716bce42051a7b34d3 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 12 Nov 2022 18:06:09 +0100 Subject: [PATCH 2/2] new useOutputData --- src/renderer/components/node/NodeOutputs.tsx | 20 ++++++------- .../components/outputs/DefaultImageOutput.tsx | 15 ++++------ .../components/outputs/GenericOutput.tsx | 13 ++++---- .../components/outputs/LargeImageOutput.tsx | 21 ++++++------- .../components/outputs/NcnnModelOutput.tsx | 26 ++++++++-------- .../components/outputs/PyTorchOutput.tsx | 30 +++++++++---------- src/renderer/components/outputs/props.ts | 13 ++++++-- 7 files changed, 67 insertions(+), 71 deletions(-) diff --git a/src/renderer/components/node/NodeOutputs.tsx b/src/renderer/components/node/NodeOutputs.tsx index f84f1de6c..92da19d62 100644 --- a/src/renderer/components/node/NodeOutputs.tsx +++ b/src/renderer/components/node/NodeOutputs.tsx @@ -11,7 +11,7 @@ import { GenericOutput } from '../outputs/GenericOutput'; import { LargeImageOutput } from '../outputs/LargeImageOutput'; import { NcnnModelOutput } from '../outputs/NcnnModelOutput'; import { OutputContainer } from '../outputs/OutputContainer'; -import { OutputProps } from '../outputs/props'; +import { OutputProps, UseOutputData } from '../outputs/props'; import { PyTorchOutput } from '../outputs/PyTorchOutput'; interface FullOutputProps extends Omit, OutputProps { @@ -57,7 +57,7 @@ const pickOutput = (kind: OutputKind, props: FullOutputProps) => { ); }; -const NO_OUTPUT_DATA = [undefined, undefined] as const; +const NO_OUTPUT_DATA: UseOutputData = { current: undefined, last: undefined, stale: false }; interface NodeOutputProps { outputs: readonly Output[]; @@ -71,23 +71,21 @@ export const NodeOutputs = memo(({ outputs, id, schemaId, animated = false }: No const outputDataEntry = useContextSelector(GlobalVolatileContext, (c) => c.outputDataMap.get(id) ); + const inputHash = useContextSelector(GlobalVolatileContext, (c) => c.inputHashes.get(id)); const useOutputData = useCallback( // eslint-disable-next-line prefer-arrow-functions/prefer-arrow-functions, func-names - function ( - outputId: OutputId - ): - | readonly [value: T, inputHash: string] - | readonly [value: undefined, inputHash: undefined] { + function (outputId: OutputId): UseOutputData { if (outputDataEntry) { - const value = outputDataEntry.data?.[outputId] as T | undefined; - if (value !== undefined) { - return [value, outputDataEntry.inputHash]; + const last = outputDataEntry.data?.[outputId] as T | undefined; + if (last !== undefined) { + const stale = inputHash !== outputDataEntry.inputHash; + return { current: stale ? undefined : last, last, stale }; } } return NO_OUTPUT_DATA; }, - [outputDataEntry] + [outputDataEntry, inputHash] ); const functions = functionDefinitions.get(schemaId)!.outputDefaults; diff --git a/src/renderer/components/outputs/DefaultImageOutput.tsx b/src/renderer/components/outputs/DefaultImageOutput.tsx index 1ba3d9ab6..fb1d6de1f 100644 --- a/src/renderer/components/outputs/DefaultImageOutput.tsx +++ b/src/renderer/components/outputs/DefaultImageOutput.tsx @@ -42,25 +42,22 @@ export const DefaultImageOutput = memo( c.schemata.get(schemaId).outputs.findIndex((o) => o.id === outputId) ); - const inputHash = useContextSelector(GlobalVolatileContext, (c) => c.inputHashes.get(id)); - const [value, valueInputHash] = useOutputData(outputId); - const sameHash = valueInputHash === inputHash; - + const { current } = useOutputData(outputId); useEffect(() => { - if (value && sameHash) { + if (current) { setManualOutputType( id, outputId, new NamedExpression('Image', [ - new NamedExpressionField('width', literal(value.width)), - new NamedExpressionField('height', literal(value.height)), - new NamedExpressionField('channels', literal(value.channels)), + new NamedExpressionField('width', literal(current.width)), + new NamedExpressionField('height', literal(current.height)), + new NamedExpressionField('channels', literal(current.channels)), ]) ); } else { setManualOutputType(id, outputId, undefined); } - }, [id, outputId, value, sameHash, setManualOutputType]); + }, [id, outputId, current, setManualOutputType]); const { getNodes, getEdges } = useReactFlow(); diff --git a/src/renderer/components/outputs/GenericOutput.tsx b/src/renderer/components/outputs/GenericOutput.tsx index 344df6baa..ba47cac2c 100644 --- a/src/renderer/components/outputs/GenericOutput.tsx +++ b/src/renderer/components/outputs/GenericOutput.tsx @@ -19,21 +19,18 @@ export const GenericOutput = memo( const schema = schemata.get(schemaId); - const inputHash = useContextSelector(GlobalVolatileContext, (c) => c.inputHashes.get(id)); - const [value, valueInputHash] = useOutputData(outputId); - const sameHash = valueInputHash === inputHash; - + const { current } = useOutputData(outputId); useEffect(() => { if (isStartingNode(schema)) { - if (value !== undefined && sameHash) { + if (current !== undefined) { if (kind === 'text') { - setManualOutputType(id, outputId, literal(value as string)); + setManualOutputType(id, outputId, literal(current as string)); } else if (kind === 'directory') { setManualOutputType( id, outputId, new NamedExpression('Directory', [ - new NamedExpressionField('path', literal(value as string)), + new NamedExpressionField('path', literal(current as string)), ]) ); } @@ -41,7 +38,7 @@ export const GenericOutput = memo( setManualOutputType(id, outputId, undefined); } } - }, [id, schemaId, value, sameHash, kind, outputId, schema, setManualOutputType]); + }, [id, schemaId, current, kind, outputId, schema, setManualOutputType]); return ( c.inputHashes.get(id)); const zoom = useContextSelector(GlobalVolatileContext, (c) => c.zoom); - const [value, valueInputHash] = useOutputData(outputId); - const sameHash = valueInputHash === inputHash; - const stale = value !== undefined && !sameHash; + const { current, last, stale } = useOutputData(outputId); useEffect(() => { if (isStartingNode(schema)) { - if (value && sameHash) { + if (current) { setManualOutputType( id, outputId, new NamedExpression('Image', [ - new NamedExpressionField('width', literal(value.width)), - new NamedExpressionField('height', literal(value.height)), - new NamedExpressionField('channels', literal(value.channels)), + new NamedExpressionField('width', literal(current.width)), + new NamedExpressionField('height', literal(current.height)), + new NamedExpressionField('channels', literal(current.channels)), ]) ); } else { setManualOutputType(id, outputId, undefined); } } - }, [id, schemaId, value, sameHash, outputId, schema, setManualOutputType]); + }, [id, schemaId, current, outputId, schema, setManualOutputType]); const imgBgColor = 'var(--node-image-preview-bg)'; const fontColor = 'var(--node-image-preview-color)'; @@ -108,7 +105,7 @@ export const LargeImageOutput = memo( overflow="hidden" w="200px" > - {value ? ( + {last ? (
2 ? 'pixelated' : 'auto', }} diff --git a/src/renderer/components/outputs/NcnnModelOutput.tsx b/src/renderer/components/outputs/NcnnModelOutput.tsx index 3e865de24..9e8386e6e 100644 --- a/src/renderer/components/outputs/NcnnModelOutput.tsx +++ b/src/renderer/components/outputs/NcnnModelOutput.tsx @@ -32,7 +32,7 @@ const getColorMode = (channels: number) => { export const NcnnModelOutput = memo( ({ id, outputId, useOutputData, animated, schemaId }: OutputProps) => { - const [value] = useOutputData(outputId); + const { current } = useOutputData(outputId); const { setManualOutputType } = useContext(GlobalContext); const { schemata } = useContext(BackendContext); @@ -41,23 +41,23 @@ export const NcnnModelOutput = memo( useEffect(() => { if (isStartingNode(schema)) { - if (value) { + if (current) { setManualOutputType( id, outputId, new NamedExpression('NcnnNetwork', [ - new NamedExpressionField('scale', literal(value.scale)), - new NamedExpressionField('inputChannels', literal(value.inNc)), - new NamedExpressionField('outputChannels', literal(value.outNc)), - new NamedExpressionField('nf', literal(value.nf)), - new NamedExpressionField('fp', literal(value.fp)), + new NamedExpressionField('scale', literal(current.scale)), + new NamedExpressionField('inputChannels', literal(current.inNc)), + new NamedExpressionField('outputChannels', literal(current.outNc)), + new NamedExpressionField('nf', literal(current.nf)), + new NamedExpressionField('fp', literal(current.fp)), ]) ); } else { setManualOutputType(id, outputId, undefined); } } - }, [id, schemaId, value, outputId, schema, setManualOutputType]); + }, [id, schemaId, current, outputId, schema, setManualOutputType]); const tagColor = 'var(--tag-bg)'; const fontColor = 'var(--tag-fg)'; @@ -70,7 +70,7 @@ export const NcnnModelOutput = memo( verticalAlign="middle" w="full" > - {value && !animated ? ( + {current && !animated ? (
- {value.scale}x + {current.scale}x @@ -90,7 +90,7 @@ export const NcnnModelOutput = memo( bgColor={tagColor} textColor={fontColor} > - {getColorMode(value.inNc)}→{getColorMode(value.outNc)} + {getColorMode(current.inNc)}→{getColorMode(current.outNc)} @@ -98,7 +98,7 @@ export const NcnnModelOutput = memo( bgColor={tagColor} textColor={fontColor} > - {value.nf}nf + {current.nf}nf @@ -106,7 +106,7 @@ export const NcnnModelOutput = memo( bgColor={tagColor} textColor={fontColor} > - {value.fp} + {current.fp} diff --git a/src/renderer/components/outputs/PyTorchOutput.tsx b/src/renderer/components/outputs/PyTorchOutput.tsx index b0c075889..4c2537e3a 100644 --- a/src/renderer/components/outputs/PyTorchOutput.tsx +++ b/src/renderer/components/outputs/PyTorchOutput.tsx @@ -33,7 +33,7 @@ const getColorMode = (channels: number) => { export const PyTorchOutput = memo( ({ id, outputId, useOutputData, animated, schemaId }: OutputProps) => { - const [value] = useOutputData(outputId); + const { current } = useOutputData(outputId); const { setManualOutputType } = useContext(GlobalContext); const { schemata } = useContext(BackendContext); @@ -42,24 +42,24 @@ export const PyTorchOutput = memo( useEffect(() => { if (isStartingNode(schema)) { - if (value) { + if (current) { setManualOutputType( id, outputId, new NamedExpression('PyTorchModel', [ - new NamedExpressionField('scale', literal(value.scale)), - new NamedExpressionField('inputChannels', literal(value.inNc)), - new NamedExpressionField('outputChannels', literal(value.outNc)), - new NamedExpressionField('arch', literal(value.arch)), - new NamedExpressionField('size', literal(value.size.join('x'))), - new NamedExpressionField('subType', literal(value.subType)), + new NamedExpressionField('scale', literal(current.scale)), + new NamedExpressionField('inputChannels', literal(current.inNc)), + new NamedExpressionField('outputChannels', literal(current.outNc)), + new NamedExpressionField('arch', literal(current.arch)), + new NamedExpressionField('size', literal(current.size.join('x'))), + new NamedExpressionField('subType', literal(current.subType)), ]) ); } else { setManualOutputType(id, outputId, undefined); } } - }, [id, schemaId, value, outputId, schema, setManualOutputType]); + }, [id, schemaId, current, outputId, schema, setManualOutputType]); const tagColor = 'var(--tag-bg)'; const fontColor = 'var(--tag-fg)'; @@ -72,7 +72,7 @@ export const PyTorchOutput = memo( verticalAlign="middle" w="full" > - {value && !animated ? ( + {current && !animated ? (
- {value.arch} + {current.arch} @@ -92,7 +92,7 @@ export const PyTorchOutput = memo( bgColor={tagColor} textColor={fontColor} > - {value.subType} + {current.subType} @@ -100,7 +100,7 @@ export const PyTorchOutput = memo( bgColor={tagColor} textColor={fontColor} > - {value.scale}x + {current.scale}x @@ -108,10 +108,10 @@ export const PyTorchOutput = memo( bgColor={tagColor} textColor={fontColor} > - {getColorMode(value.inNc)}→{getColorMode(value.outNc)} + {getColorMode(current.inNc)}→{getColorMode(current.outNc)} - {value.size.map((size) => ( + {current.size.map((size) => ( { + /** The current output data. Current here means most recent + up to date (= same input hash). */ + readonly current: T | undefined; + /** The most recent output data. */ + readonly last: T | undefined; + /** Whether the most recent output data ({@link last}) is not the current output data ({@link current}). */ + readonly stale: boolean; +} + export interface OutputProps { readonly id: string; readonly outputId: OutputId; @@ -8,9 +17,7 @@ export interface OutputProps { readonly schemaId: SchemaId; readonly definitionType: Type; readonly hasHandle: boolean; - readonly useOutputData: ( - outputId: OutputId - ) => readonly [value: T, inputHash: string] | readonly [value: undefined, inputHash: undefined]; + readonly useOutputData: (outputId: OutputId) => UseOutputData; readonly animated: boolean; readonly kind: OutputKind; }