@babylonjs/smart-filters 0.4.3-alpha → 0.6.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blocks/baseBlock.d.ts +6 -0
- package/dist/blocks/baseBlock.d.ts.map +1 -1
- package/dist/blocks/baseBlock.js +8 -0
- package/dist/blocks/baseBlock.js.map +1 -1
- package/dist/blocks/customShaderBlock.d.ts +55 -0
- package/dist/blocks/customShaderBlock.d.ts.map +1 -0
- package/dist/blocks/customShaderBlock.js +139 -0
- package/dist/blocks/customShaderBlock.js.map +1 -0
- package/dist/blocks/inputBlock.deserializer.d.ts +1 -1
- package/dist/blocks/inputBlock.deserializer.d.ts.map +1 -1
- package/dist/blocks/inputBlock.serializer.d.ts +1 -1
- package/dist/blocks/inputBlock.serializer.d.ts.map +1 -1
- package/dist/blocks/inputBlock.serializer.js +3 -3
- package/dist/blocks/inputBlock.serializer.js.map +1 -1
- package/dist/blocks/shaderBlock.d.ts +3 -7
- package/dist/blocks/shaderBlock.d.ts.map +1 -1
- package/dist/blocks/shaderBlock.js +45 -15
- package/dist/blocks/shaderBlock.js.map +1 -1
- package/dist/blocks/textureOptions.d.ts +47 -0
- package/dist/blocks/textureOptions.d.ts.map +1 -0
- package/dist/blocks/textureOptions.js +37 -0
- package/dist/blocks/textureOptions.js.map +1 -0
- package/dist/command/command.d.ts +2 -3
- package/dist/command/command.d.ts.map +1 -1
- package/dist/command/command.js.map +1 -1
- package/dist/command/commandBufferDebugger.js +1 -1
- package/dist/command/commandBufferDebugger.js.map +1 -1
- package/dist/connection/connectionPointType.d.ts +4 -0
- package/dist/connection/connectionPointType.d.ts.map +1 -1
- package/dist/editorUtils/editableInPropertyPage.d.ts +4 -0
- package/dist/editorUtils/editableInPropertyPage.d.ts.map +1 -1
- package/dist/editorUtils/editableInPropertyPage.js +1 -0
- package/dist/editorUtils/editableInPropertyPage.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/optimization/smartFilterOptimizer.d.ts +1 -1
- package/dist/optimization/smartFilterOptimizer.d.ts.map +1 -1
- package/dist/optimization/smartFilterOptimizer.js +10 -9
- package/dist/optimization/smartFilterOptimizer.js.map +1 -1
- package/dist/runtime/renderTargetGenerator.d.ts +8 -1
- package/dist/runtime/renderTargetGenerator.d.ts.map +1 -1
- package/dist/runtime/renderTargetGenerator.js +37 -18
- package/dist/runtime/renderTargetGenerator.js.map +1 -1
- package/dist/serialization/importCustomShaderBlockDefinition.d.ts +11 -0
- package/dist/serialization/importCustomShaderBlockDefinition.d.ts.map +1 -0
- package/dist/serialization/importCustomShaderBlockDefinition.js +80 -0
- package/dist/serialization/importCustomShaderBlockDefinition.js.map +1 -0
- package/dist/serialization/index.d.ts +1 -0
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/index.js +1 -0
- package/dist/serialization/index.js.map +1 -1
- package/dist/serialization/serializedBlockDefinition.d.ts +7 -0
- package/dist/serialization/serializedBlockDefinition.d.ts.map +1 -0
- package/dist/serialization/serializedBlockDefinition.js +2 -0
- package/dist/serialization/serializedBlockDefinition.js.map +1 -0
- package/dist/serialization/serializedSmartFilter.d.ts +1 -1
- package/dist/serialization/serializedSmartFilter.d.ts.map +1 -1
- package/dist/serialization/smartFilterDeserializer.d.ts +12 -4
- package/dist/serialization/smartFilterDeserializer.d.ts.map +1 -1
- package/dist/serialization/smartFilterDeserializer.js +63 -34
- package/dist/serialization/smartFilterDeserializer.js.map +1 -1
- package/dist/serialization/smartFilterSerializer.d.ts +2 -2
- package/dist/serialization/smartFilterSerializer.d.ts.map +1 -1
- package/dist/serialization/smartFilterSerializer.js +9 -6
- package/dist/serialization/smartFilterSerializer.js.map +1 -1
- package/dist/serialization/v1/blockSerialization.types.d.ts +55 -0
- package/dist/serialization/v1/blockSerialization.types.d.ts.map +1 -0
- package/dist/serialization/v1/blockSerialization.types.js +7 -0
- package/dist/serialization/v1/blockSerialization.types.js.map +1 -0
- package/dist/serialization/v1/defaultBlockSerializer.d.ts +1 -1
- package/dist/serialization/v1/defaultBlockSerializer.d.ts.map +1 -1
- package/dist/serialization/v1/defaultBlockSerializer.js +1 -1
- package/dist/serialization/v1/defaultBlockSerializer.js.map +1 -1
- package/dist/serialization/v1/index.d.ts +1 -1
- package/dist/serialization/v1/index.d.ts.map +1 -1
- package/dist/serialization/v1/index.js +1 -1
- package/dist/serialization/v1/index.js.map +1 -1
- package/dist/serialization/v1/{serialization.types.d.ts → smartFilterSerialization.types.d.ts} +12 -11
- package/dist/serialization/v1/smartFilterSerialization.types.d.ts.map +1 -0
- package/dist/serialization/v1/smartFilterSerialization.types.js +2 -0
- package/dist/serialization/v1/smartFilterSerialization.types.js.map +1 -0
- package/dist/utils/buildTools/buildShaders.js +1 -1
- package/dist/utils/buildTools/buildShaders.js.map +1 -1
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.d.ts +13 -0
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.d.ts.map +1 -0
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.js +116 -0
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.js.map +1 -0
- package/dist/utils/buildTools/shaderCode.types.d.ts +43 -0
- package/dist/utils/buildTools/shaderCode.types.d.ts.map +1 -0
- package/dist/utils/buildTools/shaderCode.types.js +2 -0
- package/dist/utils/buildTools/shaderCode.types.js.map +1 -0
- package/dist/utils/buildTools/shaderConverter.d.ts +56 -8
- package/dist/utils/buildTools/shaderConverter.d.ts.map +1 -1
- package/dist/utils/buildTools/shaderConverter.js +87 -137
- package/dist/utils/buildTools/shaderConverter.js.map +1 -1
- package/dist/utils/buildTools/watchShaders.js +2 -2
- package/dist/utils/buildTools/watchShaders.js.map +1 -1
- package/dist/utils/renderTargetUtils.js +3 -3
- package/dist/utils/renderTargetUtils.js.map +1 -1
- package/dist/utils/shaderCodeUtils.d.ts +1 -42
- package/dist/utils/shaderCodeUtils.d.ts.map +1 -1
- package/dist/utils/shaderCodeUtils.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +19 -15
- package/src/blocks/baseBlock.ts +9 -0
- package/src/blocks/customShaderBlock.ts +217 -0
- package/src/blocks/inputBlock.deserializer.ts +1 -1
- package/src/blocks/inputBlock.serializer.ts +4 -4
- package/src/blocks/shaderBlock.ts +36 -15
- package/src/blocks/textureOptions.ts +57 -0
- package/src/command/command.ts +2 -3
- package/src/command/commandBufferDebugger.ts +1 -1
- package/src/connection/connectionPointType.ts +11 -0
- package/src/editorUtils/editableInPropertyPage.ts +5 -0
- package/src/index.ts +2 -0
- package/src/optimization/smartFilterOptimizer.ts +11 -10
- package/src/runtime/renderTargetGenerator.ts +55 -20
- package/src/serialization/importCustomShaderBlockDefinition.ts +85 -0
- package/src/serialization/index.ts +1 -0
- package/src/serialization/serializedBlockDefinition.ts +7 -0
- package/src/serialization/serializedSmartFilter.ts +1 -1
- package/src/serialization/smartFilterDeserializer.ts +106 -52
- package/src/serialization/smartFilterSerializer.ts +11 -7
- package/src/serialization/v1/blockSerialization.types.ts +63 -0
- package/src/serialization/v1/defaultBlockSerializer.ts +2 -2
- package/src/serialization/v1/index.ts +1 -1
- package/src/serialization/v1/{serialization.types.ts → smartFilterSerialization.types.ts} +11 -10
- package/src/utils/buildTools/buildShaders.ts +1 -1
- package/src/utils/buildTools/convertShaderForHardcodedBlock.ts +149 -0
- package/src/utils/buildTools/shaderCode.types.ts +49 -0
- package/src/utils/buildTools/shaderConverter.ts +158 -178
- package/src/utils/buildTools/watchShaders.ts +2 -2
- package/src/utils/renderTargetUtils.ts +3 -3
- package/src/utils/shaderCodeUtils.ts +1 -50
- package/dist/serialization/v1/serialization.types.d.ts.map +0 -1
- package/dist/serialization/v1/serialization.types.js +0 -2
- package/dist/serialization/v1/serialization.types.js.map +0 -1
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "../utils/shaderCodeUtils.js";
|
|
19
19
|
import { DependencyGraph } from "./dependencyGraph.js";
|
|
20
20
|
import { DisableableShaderBlock, BlockDisableStrategy } from "../blocks/disableableShaderBlock.js";
|
|
21
|
+
import { textureOptionsMatch, type OutputTextureOptions } from "../blocks/textureOptions.js";
|
|
21
22
|
|
|
22
23
|
const showDebugData = false;
|
|
23
24
|
|
|
@@ -104,7 +105,7 @@ export class SmartFilterOptimizer {
|
|
|
104
105
|
private _mainFunctionNameToCode: Map<string, string> = new Map();
|
|
105
106
|
private _dependencyGraph: DependencyGraph<string> = new DependencyGraph<string>();
|
|
106
107
|
private _vertexShaderCode: string | undefined;
|
|
107
|
-
private
|
|
108
|
+
private _currentOutputTextureOptions: OutputTextureOptions | undefined;
|
|
108
109
|
private _forceUnoptimized: boolean = false;
|
|
109
110
|
|
|
110
111
|
/**
|
|
@@ -224,7 +225,7 @@ export class SmartFilterOptimizer {
|
|
|
224
225
|
this._mainFunctionNameToCode = new Map();
|
|
225
226
|
this._dependencyGraph = new DependencyGraph();
|
|
226
227
|
this._vertexShaderCode = undefined;
|
|
227
|
-
this.
|
|
228
|
+
this._currentOutputTextureOptions = undefined;
|
|
228
229
|
this._forceUnoptimized = false;
|
|
229
230
|
}
|
|
230
231
|
|
|
@@ -266,7 +267,7 @@ export class SmartFilterOptimizer {
|
|
|
266
267
|
s.type === "function" &&
|
|
267
268
|
s.name === funcName &&
|
|
268
269
|
s.owners[0] &&
|
|
269
|
-
s.owners[0].
|
|
270
|
+
s.owners[0].blockType === block.blockType
|
|
270
271
|
);
|
|
271
272
|
|
|
272
273
|
const newVarName = existingRemapped?.remappedName ?? decorateSymbol(this._makeSymbolUnique(funcName));
|
|
@@ -334,7 +335,7 @@ export class SmartFilterOptimizer {
|
|
|
334
335
|
s.type === varDecl &&
|
|
335
336
|
s.name === varName &&
|
|
336
337
|
s.owners[0] &&
|
|
337
|
-
s.owners[0].
|
|
338
|
+
s.owners[0].blockType === block.blockType
|
|
338
339
|
);
|
|
339
340
|
if (existingRemapped && singleInstance) {
|
|
340
341
|
newVarName = existingRemapped.remappedName;
|
|
@@ -424,7 +425,7 @@ export class SmartFilterOptimizer {
|
|
|
424
425
|
return false;
|
|
425
426
|
}
|
|
426
427
|
|
|
427
|
-
if (block.
|
|
428
|
+
if (!textureOptionsMatch(block.outputTextureOptions, this._currentOutputTextureOptions)) {
|
|
428
429
|
return false;
|
|
429
430
|
}
|
|
430
431
|
}
|
|
@@ -442,8 +443,8 @@ export class SmartFilterOptimizer {
|
|
|
442
443
|
const block = outputConnectionPoint.ownerBlock;
|
|
443
444
|
|
|
444
445
|
if (block instanceof ShaderBlock) {
|
|
445
|
-
if (this.
|
|
446
|
-
this.
|
|
446
|
+
if (this._currentOutputTextureOptions === undefined) {
|
|
447
|
+
this._currentOutputTextureOptions = block.outputTextureOptions;
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
const shaderProgram = block.getShaderProgram();
|
|
@@ -590,7 +591,7 @@ export class SmartFilterOptimizer {
|
|
|
590
591
|
return newShaderFuncName;
|
|
591
592
|
}
|
|
592
593
|
|
|
593
|
-
throw `Unhandled block type!
|
|
594
|
+
throw `Unhandled block type! blockType=${block.blockType}`;
|
|
594
595
|
}
|
|
595
596
|
|
|
596
597
|
private _saveBlockStackState(): void {
|
|
@@ -712,8 +713,8 @@ export class SmartFilterOptimizer {
|
|
|
712
713
|
},
|
|
713
714
|
});
|
|
714
715
|
|
|
715
|
-
if (this.
|
|
716
|
-
optimizedBlock.
|
|
716
|
+
if (this._currentOutputTextureOptions !== undefined) {
|
|
717
|
+
optimizedBlock.outputTextureOptions = this._currentOutputTextureOptions;
|
|
717
718
|
}
|
|
718
719
|
|
|
719
720
|
optimizedBlock.setShaderBindings(Array.from(blockOwnerToShaderBinding.values()));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ThinTexture } from "@babylonjs/core/Materials/Textures/thinTexture";
|
|
2
2
|
import type { Nullable } from "@babylonjs/core/types";
|
|
3
3
|
import { ThinRenderTargetTexture } from "@babylonjs/core/Materials/Textures/thinRenderTargetTexture.js";
|
|
4
|
+
import type { RenderTargetCreationOptions } from "@babylonjs/core/Materials/Textures/textureCreationOptions";
|
|
4
5
|
|
|
5
6
|
import type { BaseBlock } from "../blocks/baseBlock";
|
|
6
7
|
import type { InitializationData, SmartFilter } from "../smartFilter";
|
|
@@ -8,6 +9,7 @@ import type { InternalSmartFilterRuntime } from "./smartFilterRuntime";
|
|
|
8
9
|
import { ShaderBlock } from "../blocks/shaderBlock.js";
|
|
9
10
|
import { createStrongRef } from "./strongRef.js";
|
|
10
11
|
import { ConnectionPointType } from "../connection/connectionPointType.js";
|
|
12
|
+
import type { OutputTextureOptions } from "../blocks/textureOptions";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* @internal
|
|
@@ -29,7 +31,9 @@ interface RefCountedTexture {
|
|
|
29
31
|
*/
|
|
30
32
|
export class RenderTargetGenerator {
|
|
31
33
|
private _optimize: boolean;
|
|
32
|
-
private _renderTargetPool: Map<
|
|
34
|
+
private _renderTargetPool: Map<string, Set<RefCountedTexture>>;
|
|
35
|
+
private _textureOptionsHashCache = new Map<ShaderBlock, string>();
|
|
36
|
+
|
|
33
37
|
private _numTargetsCreated;
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -63,11 +67,17 @@ export class RenderTargetGenerator {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
let refCountedTexture: Nullable<RefCountedTexture> = null;
|
|
70
|
+
const textureOptionsHash = this._getTextureOptionsHash(block);
|
|
66
71
|
|
|
67
72
|
// We assign a texture to the output of the block only if this is not the last block in the chain,
|
|
68
73
|
// i.e. not the block connected to the smart output block (in which case the output of the block is to the canvas and not a texture).
|
|
69
74
|
if (!block.output.endpoints.some((cp) => cp.ownerBlock === smartFilter.output.ownerBlock)) {
|
|
70
|
-
refCountedTexture = this._getTexture(
|
|
75
|
+
refCountedTexture = this._getTexture(
|
|
76
|
+
initializationData.runtime,
|
|
77
|
+
block.outputTextureOptions,
|
|
78
|
+
textureOptionsHash,
|
|
79
|
+
smartFilter
|
|
80
|
+
);
|
|
71
81
|
|
|
72
82
|
if (!block.output.runtimeData) {
|
|
73
83
|
const runtimeOutput = createStrongRef(refCountedTexture.texture);
|
|
@@ -98,17 +108,21 @@ export class RenderTargetGenerator {
|
|
|
98
108
|
connectedBlock.output.runtimeData &&
|
|
99
109
|
connectedBlock.output.runtimeData.value
|
|
100
110
|
) {
|
|
101
|
-
this._releaseTexture(
|
|
111
|
+
this._releaseTexture(
|
|
112
|
+
connectedBlock.output.runtimeData.value,
|
|
113
|
+
this._getTextureOptionsHash(connectedBlock)
|
|
114
|
+
);
|
|
102
115
|
}
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
}
|
|
106
119
|
);
|
|
107
120
|
this._renderTargetPool.clear();
|
|
121
|
+
this._textureOptionsHashCache.clear();
|
|
108
122
|
}
|
|
109
123
|
|
|
110
|
-
private _findAvailableTexture(
|
|
111
|
-
const refCountedTextures = this._renderTargetPool.get(
|
|
124
|
+
private _findAvailableTexture(textureOptionsHash: string): Nullable<RefCountedTexture> {
|
|
125
|
+
const refCountedTextures = this._renderTargetPool.get(textureOptionsHash);
|
|
112
126
|
if (!refCountedTextures) {
|
|
113
127
|
return null;
|
|
114
128
|
}
|
|
@@ -124,27 +138,28 @@ export class RenderTargetGenerator {
|
|
|
124
138
|
|
|
125
139
|
private _getTexture(
|
|
126
140
|
runtime: InternalSmartFilterRuntime,
|
|
127
|
-
|
|
141
|
+
textureOptions: OutputTextureOptions,
|
|
142
|
+
textureOptionsHash: string,
|
|
128
143
|
smartFilter: SmartFilter
|
|
129
144
|
): RefCountedTexture {
|
|
130
145
|
if (!this._optimize) {
|
|
131
146
|
this._numTargetsCreated++;
|
|
132
147
|
return {
|
|
133
|
-
texture: this._createTexture(runtime, smartFilter,
|
|
148
|
+
texture: this._createTexture(runtime, smartFilter, textureOptions),
|
|
134
149
|
refCount: 0,
|
|
135
150
|
};
|
|
136
151
|
}
|
|
137
152
|
|
|
138
|
-
let refCountedTextures = this._renderTargetPool.get(
|
|
153
|
+
let refCountedTextures = this._renderTargetPool.get(textureOptionsHash);
|
|
139
154
|
if (!refCountedTextures) {
|
|
140
155
|
refCountedTextures = new Set();
|
|
141
|
-
this._renderTargetPool.set(
|
|
156
|
+
this._renderTargetPool.set(textureOptionsHash, refCountedTextures);
|
|
142
157
|
}
|
|
143
158
|
|
|
144
|
-
let refCountedTexture = this._findAvailableTexture(
|
|
159
|
+
let refCountedTexture = this._findAvailableTexture(textureOptionsHash);
|
|
145
160
|
if (!refCountedTexture) {
|
|
146
161
|
refCountedTexture = {
|
|
147
|
-
texture: this._createTexture(runtime, smartFilter,
|
|
162
|
+
texture: this._createTexture(runtime, smartFilter, textureOptions),
|
|
148
163
|
refCount: 0,
|
|
149
164
|
};
|
|
150
165
|
refCountedTextures.add(refCountedTexture);
|
|
@@ -154,14 +169,16 @@ export class RenderTargetGenerator {
|
|
|
154
169
|
return refCountedTexture;
|
|
155
170
|
}
|
|
156
171
|
|
|
157
|
-
private _releaseTexture(texture: ThinTexture,
|
|
172
|
+
private _releaseTexture(texture: ThinTexture, textureOptionsHash: string) {
|
|
158
173
|
if (!this._optimize) {
|
|
159
174
|
return;
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
const refCountedTextures = this._renderTargetPool.get(
|
|
177
|
+
const refCountedTextures = this._renderTargetPool.get(textureOptionsHash);
|
|
163
178
|
if (!refCountedTextures) {
|
|
164
|
-
throw new Error(
|
|
179
|
+
throw new Error(
|
|
180
|
+
`_releaseTexture: Trying to release a texture from a non existing pool ${textureOptionsHash}!`
|
|
181
|
+
);
|
|
165
182
|
}
|
|
166
183
|
|
|
167
184
|
for (const refCountedTexture of refCountedTextures) {
|
|
@@ -171,29 +188,31 @@ export class RenderTargetGenerator {
|
|
|
171
188
|
}
|
|
172
189
|
}
|
|
173
190
|
|
|
174
|
-
throw new Error(`
|
|
191
|
+
throw new Error(`_releaseTexture: Can't find the texture in the pool ${textureOptionsHash}!`);
|
|
175
192
|
}
|
|
176
193
|
|
|
177
194
|
/**
|
|
178
195
|
* Creates an offscreen texture to hold on the result of the block rendering.
|
|
179
196
|
* @param runtime - The current runtime we create the texture for
|
|
180
197
|
* @param smartFilter - The smart filter the texture is created for
|
|
181
|
-
* @param
|
|
198
|
+
* @param textureOptions - The options to use to create the texture
|
|
182
199
|
* @returns The render target texture
|
|
183
200
|
*/
|
|
184
201
|
private _createTexture(
|
|
185
202
|
runtime: InternalSmartFilterRuntime,
|
|
186
203
|
smartFilter: SmartFilter,
|
|
187
|
-
|
|
204
|
+
textureOptions: OutputTextureOptions
|
|
188
205
|
): ThinRenderTargetTexture {
|
|
189
206
|
const engine = runtime.engine;
|
|
190
207
|
|
|
191
208
|
// We are only rendering full screen post process without depth or stencil information
|
|
192
|
-
const setup = {
|
|
209
|
+
const setup: RenderTargetCreationOptions = {
|
|
193
210
|
generateDepthBuffer: false,
|
|
194
211
|
generateStencilBuffer: false,
|
|
195
212
|
generateMipMaps: false,
|
|
196
213
|
samplingMode: 2, // Babylon Constants.TEXTURE_LINEAR_LINEAR,
|
|
214
|
+
format: textureOptions.format,
|
|
215
|
+
type: textureOptions.type,
|
|
197
216
|
};
|
|
198
217
|
|
|
199
218
|
// Get the smartFilter output size - either from the output block's renderTargetTexture or the engine's render size
|
|
@@ -207,8 +226,8 @@ export class RenderTargetGenerator {
|
|
|
207
226
|
outputHeight = engine.getRenderHeight(true);
|
|
208
227
|
}
|
|
209
228
|
const size = {
|
|
210
|
-
width: Math.floor(outputWidth * ratio),
|
|
211
|
-
height: Math.floor(outputHeight * ratio),
|
|
229
|
+
width: Math.floor(outputWidth * textureOptions.ratio),
|
|
230
|
+
height: Math.floor(outputHeight * textureOptions.ratio),
|
|
212
231
|
};
|
|
213
232
|
|
|
214
233
|
// Creates frame buffers for effects
|
|
@@ -219,6 +238,22 @@ export class RenderTargetGenerator {
|
|
|
219
238
|
finalRenderTarget.wrapU = 0;
|
|
220
239
|
finalRenderTarget.wrapV = 0;
|
|
221
240
|
|
|
241
|
+
finalRenderTarget.anisotropicFilteringLevel = 1;
|
|
242
|
+
|
|
222
243
|
return finalRenderTarget;
|
|
223
244
|
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Gets a textureOptionsHash for a block, using a cache to avoid recomputing it.
|
|
248
|
+
* @param block - The block to get the texture options hash for
|
|
249
|
+
* @returns The texture options hash for the block
|
|
250
|
+
*/
|
|
251
|
+
private _getTextureOptionsHash(block: ShaderBlock): string {
|
|
252
|
+
let textureOptionsHash = this._textureOptionsHashCache.get(block);
|
|
253
|
+
if (textureOptionsHash === undefined) {
|
|
254
|
+
textureOptionsHash = JSON.stringify(block.outputTextureOptions);
|
|
255
|
+
this._textureOptionsHashCache.set(block, textureOptionsHash);
|
|
256
|
+
}
|
|
257
|
+
return textureOptionsHash;
|
|
258
|
+
}
|
|
224
259
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ConnectionPointType } from "../connection/connectionPointType.js";
|
|
2
|
+
import { hasGlslHeader, parseFragmentShader } from "../utils/buildTools/shaderConverter.js";
|
|
3
|
+
import type { SerializedBlockDefinition } from "./serializedBlockDefinition.js";
|
|
4
|
+
import type { SerializedInputConnectionPointV1 } from "./v1/blockSerialization.types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Imports a serialized custom shader block definition. Supports importing a JSON string
|
|
8
|
+
* of an SerializedBlockDefinition object, or a glsl shader with the required annotations
|
|
9
|
+
* so it can be converted to a SerializedBlockDefinition object.
|
|
10
|
+
* See readme.md for more information.
|
|
11
|
+
* @param serializedBlockDefinition - The serialized block definition - either a SerializedBlockDefinition object in a JSON string, or an annotated glsl shader
|
|
12
|
+
* @returns The serialized block definition
|
|
13
|
+
*/
|
|
14
|
+
export function importCustomShaderBlockDefinition(serializedBlockDefinition: string): SerializedBlockDefinition {
|
|
15
|
+
if (hasGlslHeader(serializedBlockDefinition)) {
|
|
16
|
+
return importAnnotatedGlsl(serializedBlockDefinition);
|
|
17
|
+
} else {
|
|
18
|
+
// Assume this is a serialized SerializedBlockDefinition object
|
|
19
|
+
return JSON.parse(serializedBlockDefinition);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Converts a fragment shader .glsl file to an SerializedBlockDefinition instance for use
|
|
25
|
+
* as a CustomShaderBlock. The .glsl file must contain certain annotations to be imported.
|
|
26
|
+
* See readme.md for more information.
|
|
27
|
+
* @param fragmentShader - The contents of the .glsl fragment shader file
|
|
28
|
+
* @returns The serialized block definition
|
|
29
|
+
*/
|
|
30
|
+
function importAnnotatedGlsl(fragmentShader: string): SerializedBlockDefinition {
|
|
31
|
+
const fragmentShaderInfo = parseFragmentShader(fragmentShader);
|
|
32
|
+
|
|
33
|
+
if (!fragmentShaderInfo.blockType) {
|
|
34
|
+
throw new Error("blockType must be defined");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Calculate the input connection points
|
|
38
|
+
const inputConnectionPoints: SerializedInputConnectionPointV1[] = [];
|
|
39
|
+
for (const uniform of fragmentShaderInfo.uniforms) {
|
|
40
|
+
// Convert to ConnectionPointType
|
|
41
|
+
let type: ConnectionPointType;
|
|
42
|
+
switch (uniform.type) {
|
|
43
|
+
case "float":
|
|
44
|
+
type = ConnectionPointType.Float;
|
|
45
|
+
break;
|
|
46
|
+
case "sampler2D":
|
|
47
|
+
type = ConnectionPointType.Texture;
|
|
48
|
+
break;
|
|
49
|
+
case "vec3":
|
|
50
|
+
type = ConnectionPointType.Color3;
|
|
51
|
+
break;
|
|
52
|
+
case "vec4":
|
|
53
|
+
type = ConnectionPointType.Color4;
|
|
54
|
+
break;
|
|
55
|
+
case "bool":
|
|
56
|
+
type = ConnectionPointType.Boolean;
|
|
57
|
+
break;
|
|
58
|
+
case "vec2":
|
|
59
|
+
type = ConnectionPointType.Vector2;
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
throw new Error(`Unsupported uniform type: '${uniform.type}'`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Add to input connection point list
|
|
66
|
+
const inputConnectionPoint: SerializedInputConnectionPointV1 = {
|
|
67
|
+
name: uniform.name,
|
|
68
|
+
type,
|
|
69
|
+
};
|
|
70
|
+
if (inputConnectionPoint.type !== ConnectionPointType.Texture && uniform.properties?.default !== undefined) {
|
|
71
|
+
inputConnectionPoint.defaultValue = uniform.properties.default;
|
|
72
|
+
}
|
|
73
|
+
inputConnectionPoints.push(inputConnectionPoint);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
formatVersion: 1,
|
|
78
|
+
blockType: fragmentShaderInfo.blockType,
|
|
79
|
+
shaderProgram: {
|
|
80
|
+
fragment: fragmentShaderInfo.shaderCode,
|
|
81
|
+
},
|
|
82
|
+
inputConnectionPoints,
|
|
83
|
+
disableOptimization: !!fragmentShaderInfo.disableOptimization,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SerializedBlockDefinitionV1 } from "./v1/blockSerialization.types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type union of all versions of serialized SmartFilter block definitions
|
|
5
|
+
* A block definition is an object which is used to create a CustomShaderBlock instance.
|
|
6
|
+
*/
|
|
7
|
+
export type SerializedBlockDefinition = SerializedBlockDefinitionV1;
|
|
@@ -6,13 +6,22 @@ import { OutputBlock } from "../blocks/outputBlock.js";
|
|
|
6
6
|
import type { ThinEngine } from "@babylonjs/core/Engines/thinEngine";
|
|
7
7
|
import { InputBlock } from "../blocks/inputBlock.js";
|
|
8
8
|
import type {
|
|
9
|
-
DeserializeBlockV1,
|
|
10
9
|
ISerializedBlockV1,
|
|
11
10
|
ISerializedConnectionV1,
|
|
12
11
|
OptionalBlockDeserializerV1,
|
|
13
12
|
SerializedSmartFilterV1,
|
|
14
|
-
} from "./v1/
|
|
13
|
+
} from "./v1/smartFilterSerialization.types";
|
|
15
14
|
import { UniqueIdGenerator } from "../utils/uniqueIdGenerator.js";
|
|
15
|
+
import type { Nullable } from "@babylonjs/core/types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A function that creates a block instance of the given class block type, or return null if it cannot.
|
|
19
|
+
*/
|
|
20
|
+
export type BlockFactory = (
|
|
21
|
+
smartFilter: SmartFilter,
|
|
22
|
+
engine: ThinEngine,
|
|
23
|
+
serializedBlock: ISerializedBlockV1
|
|
24
|
+
) => Promise<Nullable<BaseBlock>>;
|
|
16
25
|
|
|
17
26
|
/**
|
|
18
27
|
* Deserializes serialized SmartFilters. The caller passes in a map of block deserializers it wants to use,
|
|
@@ -20,39 +29,17 @@ import { UniqueIdGenerator } from "../utils/uniqueIdGenerator.js";
|
|
|
20
29
|
* The deserializer supports versioned serialized SmartFilters.
|
|
21
30
|
*/
|
|
22
31
|
export class SmartFilterDeserializer {
|
|
23
|
-
private readonly
|
|
32
|
+
private readonly _blockFactory: BlockFactory;
|
|
33
|
+
private readonly _customInputBlockDeserializer?: OptionalBlockDeserializerV1;
|
|
24
34
|
|
|
25
35
|
/**
|
|
26
36
|
* Creates a new SmartFilterDeserializer
|
|
27
|
-
* @param
|
|
37
|
+
* @param blockFactory - A function that creates a block of the given class name, or returns null if it cannot
|
|
28
38
|
* @param customInputBlockDeserializer - An optional custom deserializer for InputBlocks - if supplied and it returns null, the default deserializer will be used
|
|
29
39
|
*/
|
|
30
|
-
public constructor(
|
|
31
|
-
|
|
32
|
-
customInputBlockDeserializer
|
|
33
|
-
) {
|
|
34
|
-
this._blockDeserializersV1 = blockDeserializers;
|
|
35
|
-
|
|
36
|
-
this._blockDeserializersV1.set(
|
|
37
|
-
InputBlock.ClassName,
|
|
38
|
-
async (smartFilter: SmartFilter, serializedBlock: ISerializedBlockV1, engine: ThinEngine) => {
|
|
39
|
-
if (customInputBlockDeserializer) {
|
|
40
|
-
const customDeserializerResult = await customInputBlockDeserializer(
|
|
41
|
-
smartFilter,
|
|
42
|
-
serializedBlock,
|
|
43
|
-
engine
|
|
44
|
-
);
|
|
45
|
-
if (customDeserializerResult !== null) {
|
|
46
|
-
return customDeserializerResult;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return inputBlockDeserializer(smartFilter, serializedBlock);
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
this._blockDeserializersV1.set(OutputBlock.ClassName, (smartFilter: SmartFilter) =>
|
|
54
|
-
Promise.resolve(smartFilter.output.ownerBlock)
|
|
55
|
-
);
|
|
40
|
+
public constructor(blockFactory: BlockFactory, customInputBlockDeserializer?: OptionalBlockDeserializerV1) {
|
|
41
|
+
this._blockFactory = blockFactory;
|
|
42
|
+
this._customInputBlockDeserializer = customInputBlockDeserializer;
|
|
56
43
|
}
|
|
57
44
|
|
|
58
45
|
/**
|
|
@@ -63,7 +50,14 @@ export class SmartFilterDeserializer {
|
|
|
63
50
|
*/
|
|
64
51
|
public async deserialize(engine: ThinEngine, smartFilterJson: any): Promise<SmartFilter> {
|
|
65
52
|
const serializedSmartFilter: SerializedSmartFilter = smartFilterJson;
|
|
66
|
-
|
|
53
|
+
|
|
54
|
+
// Back-compat for the rename of version to formatVersion, didn't warrant a new version
|
|
55
|
+
if ((serializedSmartFilter as any).version && serializedSmartFilter.formatVersion === undefined) {
|
|
56
|
+
serializedSmartFilter.formatVersion = (serializedSmartFilter as any).version;
|
|
57
|
+
delete (serializedSmartFilter as any).version;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
switch (serializedSmartFilter.formatVersion) {
|
|
67
61
|
case 1:
|
|
68
62
|
return await this._deserializeV1(engine, serializedSmartFilter);
|
|
69
63
|
}
|
|
@@ -85,31 +79,28 @@ export class SmartFilterDeserializer {
|
|
|
85
79
|
|
|
86
80
|
// Deserialize the blocks
|
|
87
81
|
const blockDeserializationWork: Promise<void>[] = [];
|
|
82
|
+
const blockDefinitionsWhichCouldNotBeDeserialized: string[] = [];
|
|
88
83
|
serializedSmartFilter.blocks.forEach((serializedBlock: ISerializedBlockV1) => {
|
|
89
|
-
const blockDeserializer = this._blockDeserializersV1.get(serializedBlock.className);
|
|
90
|
-
if (!blockDeserializer) {
|
|
91
|
-
throw new Error(`No deserializer found for block type ${serializedBlock.className}`);
|
|
92
|
-
}
|
|
93
84
|
blockDeserializationWork.push(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// are higher than this one.
|
|
103
|
-
UniqueIdGenerator.EnsureIdsGreaterThan(newBlock.uniqueId);
|
|
104
|
-
|
|
105
|
-
// Save in the map
|
|
106
|
-
blockIdMap.set(newBlock.uniqueId, newBlock);
|
|
107
|
-
blockNameMap.set(newBlock.name, newBlock);
|
|
108
|
-
})
|
|
85
|
+
this._deserializeBlockV1(
|
|
86
|
+
smartFilter,
|
|
87
|
+
serializedBlock,
|
|
88
|
+
engine,
|
|
89
|
+
blockDefinitionsWhichCouldNotBeDeserialized,
|
|
90
|
+
blockIdMap,
|
|
91
|
+
blockNameMap
|
|
92
|
+
)
|
|
109
93
|
);
|
|
110
94
|
});
|
|
111
95
|
await Promise.all(blockDeserializationWork);
|
|
112
96
|
|
|
97
|
+
// If any block definitions could not be deserialized, throw an error
|
|
98
|
+
if (blockDefinitionsWhichCouldNotBeDeserialized.length > 0) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Could not deserialize the following block definitions: ${blockDefinitionsWhichCouldNotBeDeserialized.join(", ")}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
113
104
|
// Deserialize the connections
|
|
114
105
|
serializedSmartFilter.connections.forEach((connection: ISerializedConnectionV1) => {
|
|
115
106
|
// Find the source block and its connection point's connectTo function
|
|
@@ -121,7 +112,9 @@ export class SmartFilterDeserializer {
|
|
|
121
112
|
if (!sourceBlock) {
|
|
122
113
|
throw new Error(`Source block ${connection.outputBlock} not found`);
|
|
123
114
|
}
|
|
124
|
-
const sourceConnectionPoint =
|
|
115
|
+
const sourceConnectionPoint = sourceBlock.outputs.find(
|
|
116
|
+
(output) => output.name === connection.outputConnectionPoint
|
|
117
|
+
);
|
|
125
118
|
if (!sourceConnectionPoint || typeof sourceConnectionPoint.connectTo !== "function") {
|
|
126
119
|
throw new Error(
|
|
127
120
|
`Block ${connection.outputBlock} does not have an connection point named ${connection.outputConnectionPoint}`
|
|
@@ -137,7 +130,10 @@ export class SmartFilterDeserializer {
|
|
|
137
130
|
if (!targetBlock) {
|
|
138
131
|
throw new Error(`Target block ${connection.inputBlock} not found`);
|
|
139
132
|
}
|
|
140
|
-
|
|
133
|
+
|
|
134
|
+
const targetConnectionPoint = targetBlock.inputs.find(
|
|
135
|
+
(input) => input.name === connection.inputConnectionPoint
|
|
136
|
+
);
|
|
141
137
|
if (!targetConnectionPoint || typeof targetConnectionPoint !== "object") {
|
|
142
138
|
throw new Error(
|
|
143
139
|
`Block ${connection.inputBlock} does not have a connection point named ${connection.inputConnectionPoint}`
|
|
@@ -150,4 +146,62 @@ export class SmartFilterDeserializer {
|
|
|
150
146
|
|
|
151
147
|
return smartFilter;
|
|
152
148
|
}
|
|
149
|
+
|
|
150
|
+
private async _deserializeBlockV1(
|
|
151
|
+
smartFilter: SmartFilter,
|
|
152
|
+
serializedBlock: ISerializedBlockV1,
|
|
153
|
+
engine: ThinEngine,
|
|
154
|
+
blockTypesWhichCouldNotBeDeserialized: string[],
|
|
155
|
+
blockIdMap: Map<number, BaseBlock>,
|
|
156
|
+
blockNameMap: Map<string, BaseBlock>
|
|
157
|
+
): Promise<void> {
|
|
158
|
+
let newBlock: Nullable<BaseBlock> = null;
|
|
159
|
+
|
|
160
|
+
// Back compat for early Smart Filter V1 serialization where the blockType was stored in className
|
|
161
|
+
// Not worth creating a new version for this, as it's only used in the deserializer
|
|
162
|
+
if ((serializedBlock as any).className && !serializedBlock.blockType) {
|
|
163
|
+
serializedBlock.blockType = (serializedBlock as any).className;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Get the instance of the block
|
|
167
|
+
switch (serializedBlock.blockType) {
|
|
168
|
+
case InputBlock.ClassName:
|
|
169
|
+
{
|
|
170
|
+
if (this._customInputBlockDeserializer) {
|
|
171
|
+
newBlock = await this._customInputBlockDeserializer(smartFilter, serializedBlock, engine);
|
|
172
|
+
}
|
|
173
|
+
if (newBlock === null) {
|
|
174
|
+
newBlock = inputBlockDeserializer(smartFilter, serializedBlock);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
case OutputBlock.ClassName:
|
|
179
|
+
{
|
|
180
|
+
newBlock = smartFilter.output.ownerBlock;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
default: {
|
|
184
|
+
// If it's not an input or output block, use the provided block factory
|
|
185
|
+
newBlock = await this._blockFactory(smartFilter, engine, serializedBlock);
|
|
186
|
+
if (!newBlock) {
|
|
187
|
+
blockTypesWhichCouldNotBeDeserialized.push(serializedBlock.blockType);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Deserializers are not responsible for setting the uniqueId or comments.
|
|
194
|
+
// This is so they don't have to be passed into the constructors when programmatically creating
|
|
195
|
+
// blocks, and so each deserializer doesn't have to remember to do it.
|
|
196
|
+
newBlock.uniqueId = serializedBlock.uniqueId;
|
|
197
|
+
newBlock.comments = serializedBlock.comments;
|
|
198
|
+
|
|
199
|
+
// We need to ensure any uniqueIds generated in the future (e.g. a new block is added to the SmartFilter)
|
|
200
|
+
// are higher than this one.
|
|
201
|
+
UniqueIdGenerator.EnsureIdsGreaterThan(newBlock.uniqueId);
|
|
202
|
+
|
|
203
|
+
// Save in the map
|
|
204
|
+
blockIdMap.set(newBlock.uniqueId, newBlock);
|
|
205
|
+
blockNameMap.set(newBlock.name, newBlock);
|
|
206
|
+
}
|
|
153
207
|
}
|
|
@@ -10,7 +10,8 @@ import type {
|
|
|
10
10
|
ISerializedConnectionV1,
|
|
11
11
|
SerializeBlockV1,
|
|
12
12
|
SerializedSmartFilterV1,
|
|
13
|
-
} from "./v1/
|
|
13
|
+
} from "./v1/smartFilterSerialization.types";
|
|
14
|
+
import { CustomShaderBlock } from "../blocks/customShaderBlock.js";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Determines if two serialized connection points are equivalent to each other
|
|
@@ -37,17 +38,17 @@ export class SmartFilterSerializer {
|
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* Creates a new SmartFilterSerializer
|
|
40
|
-
* @param blocksUsingDefaultSerialization - A list of the
|
|
41
|
+
* @param blocksUsingDefaultSerialization - A list of the blockType of blocks which can use default serialization (they only have ConnectionPoint properties and no constructor parameters)
|
|
41
42
|
* @param additionalBlockSerializers - An array of block serializers to use, beyond those for the core blocks
|
|
42
43
|
*/
|
|
43
44
|
public constructor(blocksUsingDefaultSerialization: string[], additionalBlockSerializers: IBlockSerializerV1[]) {
|
|
44
|
-
this._blockSerializers.set(inputBlockSerializer.
|
|
45
|
+
this._blockSerializers.set(inputBlockSerializer.blockType, inputBlockSerializer.serialize);
|
|
45
46
|
this._blockSerializers.set(OutputBlock.ClassName, defaultBlockSerializer);
|
|
46
47
|
blocksUsingDefaultSerialization.forEach((block) => {
|
|
47
48
|
this._blockSerializers.set(block, defaultBlockSerializer);
|
|
48
49
|
});
|
|
49
50
|
additionalBlockSerializers.forEach((serializer) =>
|
|
50
|
-
this._blockSerializers.set(serializer.
|
|
51
|
+
this._blockSerializers.set(serializer.blockType, serializer.serialize)
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -61,9 +62,12 @@ export class SmartFilterSerializer {
|
|
|
61
62
|
|
|
62
63
|
const blocks = smartFilter.attachedBlocks.map((block: BaseBlock) => {
|
|
63
64
|
// Serialize the block itself
|
|
64
|
-
const serializeFn =
|
|
65
|
+
const serializeFn =
|
|
66
|
+
block.getClassName() === CustomShaderBlock.ClassName
|
|
67
|
+
? defaultBlockSerializer
|
|
68
|
+
: this._blockSerializers.get(block.blockType);
|
|
65
69
|
if (!serializeFn) {
|
|
66
|
-
throw new Error(`No serializer was provided for a block of type ${block.
|
|
70
|
+
throw new Error(`No serializer was provided for a block of type ${block.blockType}`);
|
|
67
71
|
}
|
|
68
72
|
const serializedBlock: ISerializedBlockV1 = serializeFn(block);
|
|
69
73
|
|
|
@@ -102,7 +106,7 @@ export class SmartFilterSerializer {
|
|
|
102
106
|
});
|
|
103
107
|
|
|
104
108
|
return {
|
|
105
|
-
|
|
109
|
+
formatVersion: 1,
|
|
106
110
|
name: smartFilter.name,
|
|
107
111
|
comments: smartFilter.comments,
|
|
108
112
|
editorData: smartFilter.editorData,
|