@onerjs/smart-filters 8.25.0
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/license.md +21 -0
- package/package.json +52 -0
- package/readme.md +9 -0
- package/src/IDisposable.ts +9 -0
- package/src/blockFoundation/aggregateBlock.ts +148 -0
- package/src/blockFoundation/baseBlock.ts +339 -0
- package/src/blockFoundation/customAggregateBlock.ts +88 -0
- package/src/blockFoundation/customShaderBlock.ts +362 -0
- package/src/blockFoundation/disableableShaderBlock.ts +91 -0
- package/src/blockFoundation/index.ts +9 -0
- package/src/blockFoundation/inputBlock.deserializer.ts +72 -0
- package/src/blockFoundation/inputBlock.serialization.types.ts +126 -0
- package/src/blockFoundation/inputBlock.serializer.ts +150 -0
- package/src/blockFoundation/inputBlock.ts +181 -0
- package/src/blockFoundation/outputBlock.ts +144 -0
- package/src/blockFoundation/shaderBlock.ts +156 -0
- package/src/blockFoundation/textureOptions.ts +57 -0
- package/src/command/command.ts +59 -0
- package/src/command/commandBuffer.ts +71 -0
- package/src/command/commandBufferDebugger.ts +14 -0
- package/src/command/index.ts +7 -0
- package/src/connection/connectionPoint.ts +205 -0
- package/src/connection/connectionPointCompatibilityState.ts +31 -0
- package/src/connection/connectionPointDirection.ts +9 -0
- package/src/connection/connectionPointType.ts +45 -0
- package/src/connection/connectionPointWithDefault.ts +27 -0
- package/src/connection/index.ts +8 -0
- package/src/editorUtils/editableInPropertyPage.ts +106 -0
- package/src/editorUtils/index.ts +3 -0
- package/src/index.ts +16 -0
- package/src/optimization/dependencyGraph.ts +96 -0
- package/src/optimization/index.ts +1 -0
- package/src/optimization/optimizedShaderBlock.ts +131 -0
- package/src/optimization/smartFilterOptimizer.ts +757 -0
- package/src/runtime/index.ts +8 -0
- package/src/runtime/renderTargetGenerator.ts +222 -0
- package/src/runtime/shaderRuntime.ts +174 -0
- package/src/runtime/smartFilterRuntime.ts +112 -0
- package/src/runtime/strongRef.ts +18 -0
- package/src/serialization/importCustomBlockDefinition.ts +86 -0
- package/src/serialization/index.ts +10 -0
- package/src/serialization/serializedBlockDefinition.ts +12 -0
- package/src/serialization/serializedShaderBlockDefinition.ts +7 -0
- package/src/serialization/serializedSmartFilter.ts +6 -0
- package/src/serialization/smartFilterDeserializer.ts +190 -0
- package/src/serialization/smartFilterSerializer.ts +110 -0
- package/src/serialization/v1/defaultBlockSerializer.ts +21 -0
- package/src/serialization/v1/index.ts +4 -0
- package/src/serialization/v1/shaderBlockSerialization.types.ts +85 -0
- package/src/serialization/v1/smartFilterSerialization.types.ts +129 -0
- package/src/smartFilter.ts +255 -0
- package/src/utils/buildTools/buildShaders.ts +14 -0
- package/src/utils/buildTools/convertGlslIntoBlock.ts +370 -0
- package/src/utils/buildTools/convertGlslIntoShaderProgram.ts +173 -0
- package/src/utils/buildTools/convertShaders.ts +65 -0
- package/src/utils/buildTools/recordVersionNumber.js +24 -0
- package/src/utils/buildTools/shaderCode.types.ts +59 -0
- package/src/utils/buildTools/shaderConverter.ts +466 -0
- package/src/utils/buildTools/watchShaders.ts +44 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/renderTargetUtils.ts +30 -0
- package/src/utils/shaderCodeUtils.ts +192 -0
- package/src/utils/textureLoaders.ts +31 -0
- package/src/utils/textureUtils.ts +28 -0
- package/src/utils/uniqueIdGenerator.ts +28 -0
- package/src/version.ts +4 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { type StrongRef } from "./strongRef.js";
|
|
2
|
+
export { CreateStrongRef } from "./strongRef.js";
|
|
3
|
+
// Back compat for when camelCase was used
|
|
4
|
+
export { CreateStrongRef as createStrongRef } from "./strongRef.js";
|
|
5
|
+
export { DisableableShaderBinding, ShaderBinding, ShaderRuntime } from "./shaderRuntime.js";
|
|
6
|
+
export { type SmartFilterRuntime } from "./smartFilterRuntime.js";
|
|
7
|
+
export { InternalSmartFilterRuntime } from "./smartFilterRuntime.js";
|
|
8
|
+
export { RenderTargetGenerator } from "./renderTargetGenerator.js";
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import type { ThinTexture } from "core/Materials/Textures/thinTexture.js";
|
|
2
|
+
import type { Nullable } from "core/types.js";
|
|
3
|
+
import { ThinRenderTargetTexture } from "core/Materials/Textures/thinRenderTargetTexture.js";
|
|
4
|
+
import type { RenderTargetCreationOptions } from "core/Materials/Textures/textureCreationOptions.js";
|
|
5
|
+
|
|
6
|
+
import type { BaseBlock } from "../blockFoundation/baseBlock.js";
|
|
7
|
+
import type { InitializationData, SmartFilter } from "../smartFilter.js";
|
|
8
|
+
import type { InternalSmartFilterRuntime } from "./smartFilterRuntime.js";
|
|
9
|
+
import { ShaderBlock } from "../blockFoundation/shaderBlock.js";
|
|
10
|
+
import { CreateStrongRef } from "./strongRef.js";
|
|
11
|
+
import { ConnectionPointType } from "../connection/connectionPointType.js";
|
|
12
|
+
import type { OutputTextureOptions } from "../blockFoundation/textureOptions.js";
|
|
13
|
+
import { GetBlockOutputTextureSize } from "../utils/textureUtils.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
interface IRefCountedTexture {
|
|
19
|
+
/**
|
|
20
|
+
* The texture.
|
|
21
|
+
*/
|
|
22
|
+
texture: ThinTexture;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The reference count.
|
|
26
|
+
*/
|
|
27
|
+
refCount: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The render target generator is responsible for creating and assigning render targets to ShaderBlocks.
|
|
32
|
+
*/
|
|
33
|
+
export class RenderTargetGenerator {
|
|
34
|
+
private _optimize: boolean;
|
|
35
|
+
private _renderTargetPool: Map<string, Set<IRefCountedTexture>>;
|
|
36
|
+
private _textureOptionsHashCache = new Map<ShaderBlock, string>();
|
|
37
|
+
|
|
38
|
+
private _numTargetsCreated;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new render target generator.
|
|
42
|
+
* @param optimize - If true, the render target generator will try to reuse render targets as much as possible.
|
|
43
|
+
*/
|
|
44
|
+
constructor(optimize = true) {
|
|
45
|
+
this._optimize = optimize;
|
|
46
|
+
this._renderTargetPool = new Map();
|
|
47
|
+
this._numTargetsCreated = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Returns the number of render targets created by the process
|
|
52
|
+
*/
|
|
53
|
+
public get numTargetsCreated() {
|
|
54
|
+
return this._numTargetsCreated;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Sets the output textures for the ShaderBlocks of the smart filter.
|
|
59
|
+
* @param smartFilter - The smart filter to generate the render targets for.
|
|
60
|
+
* @param initializationData - The initialization data to use.
|
|
61
|
+
*/
|
|
62
|
+
public setOutputTextures(smartFilter: SmartFilter, initializationData: InitializationData) {
|
|
63
|
+
smartFilter.output.ownerBlock.visit(initializationData, (block: BaseBlock, initializationData: InitializationData) => {
|
|
64
|
+
if (!(block instanceof ShaderBlock)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let refCountedTexture: Nullable<IRefCountedTexture> = null;
|
|
69
|
+
const textureOptionsHash = this._getTextureOptionsHash(block);
|
|
70
|
+
|
|
71
|
+
// We assign a texture to the output of the block only if this is not the last block in the chain,
|
|
72
|
+
// 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).
|
|
73
|
+
if (!block.output.endpoints.some((cp) => cp.ownerBlock === smartFilter.output.ownerBlock)) {
|
|
74
|
+
refCountedTexture = this._getTexture(initializationData.runtime, block.outputTextureOptions, textureOptionsHash, smartFilter);
|
|
75
|
+
|
|
76
|
+
if (!block.output.runtimeData) {
|
|
77
|
+
const runtimeOutput = CreateStrongRef(refCountedTexture.texture);
|
|
78
|
+
block.output.runtimeData = runtimeOutput;
|
|
79
|
+
} else {
|
|
80
|
+
block.output.runtimeData.value = refCountedTexture.texture;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this._optimize) {
|
|
85
|
+
if (refCountedTexture !== null) {
|
|
86
|
+
for (const output of block.outputs) {
|
|
87
|
+
output.endpoints.forEach((endpoint) => {
|
|
88
|
+
if (endpoint.connectedTo) {
|
|
89
|
+
refCountedTexture!.refCount++;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const input of block.inputs) {
|
|
96
|
+
if (!input.connectedTo || input.connectedTo.type !== ConnectionPointType.Texture) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const connectedBlock = input.connectedTo.ownerBlock;
|
|
100
|
+
if (connectedBlock instanceof ShaderBlock && connectedBlock.output.runtimeData && connectedBlock.output.runtimeData.value) {
|
|
101
|
+
this._releaseTexture(connectedBlock.output.runtimeData.value, this._getTextureOptionsHash(connectedBlock));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
this._renderTargetPool.clear();
|
|
107
|
+
this._textureOptionsHashCache.clear();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private _findAvailableTexture(textureOptionsHash: string): Nullable<IRefCountedTexture> {
|
|
111
|
+
const refCountedTextures = this._renderTargetPool.get(textureOptionsHash);
|
|
112
|
+
if (!refCountedTextures) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const refCountedTexture of refCountedTextures) {
|
|
117
|
+
if (refCountedTexture.refCount === 0) {
|
|
118
|
+
return refCountedTexture;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private _getTexture(runtime: InternalSmartFilterRuntime, textureOptions: OutputTextureOptions, textureOptionsHash: string, smartFilter: SmartFilter): IRefCountedTexture {
|
|
126
|
+
if (!this._optimize) {
|
|
127
|
+
this._numTargetsCreated++;
|
|
128
|
+
return {
|
|
129
|
+
texture: this._createTexture(runtime, smartFilter, textureOptions),
|
|
130
|
+
refCount: 0,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let refCountedTextures = this._renderTargetPool.get(textureOptionsHash);
|
|
135
|
+
if (!refCountedTextures) {
|
|
136
|
+
refCountedTextures = new Set();
|
|
137
|
+
this._renderTargetPool.set(textureOptionsHash, refCountedTextures);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let refCountedTexture = this._findAvailableTexture(textureOptionsHash);
|
|
141
|
+
if (!refCountedTexture) {
|
|
142
|
+
refCountedTexture = {
|
|
143
|
+
texture: this._createTexture(runtime, smartFilter, textureOptions),
|
|
144
|
+
refCount: 0,
|
|
145
|
+
};
|
|
146
|
+
refCountedTextures.add(refCountedTexture);
|
|
147
|
+
this._numTargetsCreated++;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return refCountedTexture;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private _releaseTexture(texture: ThinTexture, textureOptionsHash: string) {
|
|
154
|
+
if (!this._optimize) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const refCountedTextures = this._renderTargetPool.get(textureOptionsHash);
|
|
159
|
+
if (!refCountedTextures) {
|
|
160
|
+
throw new Error(`_releaseTexture: Trying to release a texture from a non existing pool ${textureOptionsHash}!`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const refCountedTexture of refCountedTextures) {
|
|
164
|
+
if (refCountedTexture.texture === texture) {
|
|
165
|
+
refCountedTexture.refCount--;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
throw new Error(`_releaseTexture: Can't find the texture in the pool ${textureOptionsHash}!`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Creates an offscreen texture to hold on the result of the block rendering.
|
|
175
|
+
* @param runtime - The current runtime we create the texture for
|
|
176
|
+
* @param smartFilter - The smart filter the texture is created for
|
|
177
|
+
* @param textureOptions - The options to use to create the texture
|
|
178
|
+
* @returns The render target texture
|
|
179
|
+
*/
|
|
180
|
+
private _createTexture(runtime: InternalSmartFilterRuntime, smartFilter: SmartFilter, textureOptions: OutputTextureOptions): ThinRenderTargetTexture {
|
|
181
|
+
const engine = runtime.engine;
|
|
182
|
+
|
|
183
|
+
// We are only rendering full screen post process without depth or stencil information
|
|
184
|
+
const setup: RenderTargetCreationOptions = {
|
|
185
|
+
generateDepthBuffer: false,
|
|
186
|
+
generateStencilBuffer: false,
|
|
187
|
+
generateMipMaps: false,
|
|
188
|
+
samplingMode: 2, // Babylon Constants.TEXTURE_LINEAR_LINEAR,
|
|
189
|
+
format: textureOptions.format,
|
|
190
|
+
type: textureOptions.type,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Get the smartFilter output size - either from the output block's renderTargetTexture or the engine's render size
|
|
194
|
+
const size = GetBlockOutputTextureSize(smartFilter, engine, textureOptions);
|
|
195
|
+
|
|
196
|
+
// Creates frame buffers for effects
|
|
197
|
+
const finalRenderTarget = new ThinRenderTargetTexture(engine, size, setup);
|
|
198
|
+
runtime.registerResource(finalRenderTarget);
|
|
199
|
+
|
|
200
|
+
// Babylon Constants.TEXTURE_CLAMP_ADDRESSMODE; NPOT Friendly
|
|
201
|
+
finalRenderTarget.wrapU = 0;
|
|
202
|
+
finalRenderTarget.wrapV = 0;
|
|
203
|
+
|
|
204
|
+
finalRenderTarget.anisotropicFilteringLevel = 1;
|
|
205
|
+
|
|
206
|
+
return finalRenderTarget;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Gets a textureOptionsHash for a block, using a cache to avoid recomputing it.
|
|
211
|
+
* @param block - The block to get the texture options hash for
|
|
212
|
+
* @returns The texture options hash for the block
|
|
213
|
+
*/
|
|
214
|
+
private _getTextureOptionsHash(block: ShaderBlock): string {
|
|
215
|
+
let textureOptionsHash = this._textureOptionsHashCache.get(block);
|
|
216
|
+
if (textureOptionsHash === undefined) {
|
|
217
|
+
textureOptionsHash = JSON.stringify(block.outputTextureOptions);
|
|
218
|
+
this._textureOptionsHashCache.set(block, textureOptionsHash);
|
|
219
|
+
}
|
|
220
|
+
return textureOptionsHash;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { EffectRenderer } from "core/Materials/effectRenderer.js";
|
|
2
|
+
import type { RenderTargetWrapper } from "core/Engines/renderTargetWrapper.js";
|
|
3
|
+
import type { AbstractEngine } from "core/Engines/abstractEngine.js";
|
|
4
|
+
import type { Effect } from "core/Materials/effect.js";
|
|
5
|
+
import { EffectWrapper } from "core/Materials/effectRenderer.js";
|
|
6
|
+
import type { ThinRenderTargetTexture } from "core/Materials/Textures/thinRenderTargetTexture.js";
|
|
7
|
+
|
|
8
|
+
import type { IDisposable } from "../IDisposable.js";
|
|
9
|
+
import type { ShaderProgram } from "../utils/shaderCodeUtils.js";
|
|
10
|
+
import { CreateStrongRef, type StrongRef } from "./strongRef.js";
|
|
11
|
+
import type { IDisableableBlock } from "../blockFoundation/disableableShaderBlock.js";
|
|
12
|
+
import { DecorateSymbol, DisableUniform, GetShaderCreateOptions } from "../utils/shaderCodeUtils.js";
|
|
13
|
+
import type { OutputBlock } from "../blockFoundation/outputBlock.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The shader bindings for a ShaderBlock that can't be disabled.
|
|
17
|
+
*/
|
|
18
|
+
export abstract class ShaderBinding {
|
|
19
|
+
/**
|
|
20
|
+
* Binds all the required data to the shader when rendering.
|
|
21
|
+
* Overridden by derived classes.
|
|
22
|
+
* @param effect - defines the effect to bind the data to
|
|
23
|
+
* @param width - defines the width of the output
|
|
24
|
+
* @param height - defines the height of the output
|
|
25
|
+
*/
|
|
26
|
+
public abstract bind(effect: Effect, width?: number, height?: number): void;
|
|
27
|
+
|
|
28
|
+
private _remappedShaderVariables: { [key: string]: string } = {};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Gets the remapped shader variable name.
|
|
32
|
+
* @param variableName - The variable name
|
|
33
|
+
* @returns The remapped variable name
|
|
34
|
+
*/
|
|
35
|
+
public getRemappedName(variableName: string) {
|
|
36
|
+
variableName = DecorateSymbol(variableName);
|
|
37
|
+
return this._remappedShaderVariables[variableName] ?? variableName;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets the remapped shader variables.
|
|
42
|
+
* @param variableName - defines the variable name to remap
|
|
43
|
+
* @param remappedName - defines the remapped name
|
|
44
|
+
*/
|
|
45
|
+
public addShaderVariableRemapping(variableName: string, remappedName: string) {
|
|
46
|
+
this._remappedShaderVariables[variableName] = remappedName;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The shader bindings for a disableable ShaderBlock.
|
|
52
|
+
*/
|
|
53
|
+
export abstract class DisableableShaderBinding extends ShaderBinding {
|
|
54
|
+
private _disabled: StrongRef<boolean>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Construct a ShaderBinding instance.
|
|
58
|
+
* @param parentBlock - The parent block
|
|
59
|
+
*/
|
|
60
|
+
constructor(parentBlock: IDisableableBlock) {
|
|
61
|
+
super();
|
|
62
|
+
this._disabled = parentBlock.disabled?.runtimeData || CreateStrongRef(false);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Binds all the required data to the shader when rendering.
|
|
67
|
+
* @param effect - defines the effect to bind the data to
|
|
68
|
+
* @param _width - defines the width of the output
|
|
69
|
+
* @param _height - defines the height of the output
|
|
70
|
+
*/
|
|
71
|
+
public bind(effect: Effect, _width?: number, _height?: number): void {
|
|
72
|
+
effect.setBool(this.getRemappedName(DisableUniform), this._disabled.value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The shader runtime is the base for any runtime associated with a @see ShaderBlock.
|
|
78
|
+
*
|
|
79
|
+
* It encapsulates the basic needs to render a full screen effect mainly the effect wrapper holding on the shader program.
|
|
80
|
+
*
|
|
81
|
+
* It is able to either render to a texture or directly to the main canvas.
|
|
82
|
+
*
|
|
83
|
+
* It also manages the disposal of the effect wrapper.
|
|
84
|
+
*/
|
|
85
|
+
export class ShaderRuntime implements IDisposable {
|
|
86
|
+
/**
|
|
87
|
+
* Promise that resolves when the effect is ready to be used.
|
|
88
|
+
*/
|
|
89
|
+
public readonly onReadyAsync: Promise<void>;
|
|
90
|
+
|
|
91
|
+
private readonly _engine: AbstractEngine;
|
|
92
|
+
private readonly _effectRenderer: EffectRenderer;
|
|
93
|
+
private readonly _effectWrapper: EffectWrapper;
|
|
94
|
+
private readonly _shaderBinding: ShaderBinding;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Creates a new @see ShaderRuntime.
|
|
98
|
+
* @param effectRenderer - defines the effect renderer to use to render the full screen effect
|
|
99
|
+
* @param shaderProgram - defines the shader code associated with this runtime
|
|
100
|
+
* @param shaderBinding - defines the shader bindings associated with this runtime
|
|
101
|
+
*/
|
|
102
|
+
constructor(effectRenderer: EffectRenderer, shaderProgram: ShaderProgram, shaderBinding: ShaderBinding) {
|
|
103
|
+
this._engine = effectRenderer.engine;
|
|
104
|
+
this._effectRenderer = effectRenderer;
|
|
105
|
+
this._shaderBinding = shaderBinding;
|
|
106
|
+
this._effectWrapper = new EffectWrapper({
|
|
107
|
+
engine: this._engine,
|
|
108
|
+
...GetShaderCreateOptions(shaderProgram),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Wraps the effect readiness in a promise to expose it as a public property.
|
|
112
|
+
this.onReadyAsync = new Promise<void>((resolve, reject) => {
|
|
113
|
+
this._effectWrapper.effect.executeWhenCompiled(() => {
|
|
114
|
+
resolve();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this._effectWrapper.effect.onErrorObservable.addOnce((error) => {
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
119
|
+
reject(error);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Renders the full screen effect into a render target.
|
|
126
|
+
* @param outputBlock - The output block to render to - assumes it has a .renderTargetWrapper
|
|
127
|
+
*/
|
|
128
|
+
public renderToTargetWrapper(outputBlock: OutputBlock): void {
|
|
129
|
+
this._renderToTargetWrapper(outputBlock.renderTargetWrapper!);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Renders the full screen effect into a texture.
|
|
134
|
+
* @param renderTargetTexture - The render target texture to render into
|
|
135
|
+
*/
|
|
136
|
+
public renderToTargetTexture(renderTargetTexture: ThinRenderTargetTexture): void {
|
|
137
|
+
const renderTargetWrapper = renderTargetTexture.renderTarget;
|
|
138
|
+
if (renderTargetWrapper) {
|
|
139
|
+
this._renderToTargetWrapper(renderTargetWrapper);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private _renderToTargetWrapper(renderTargetWrapper: RenderTargetWrapper): void {
|
|
144
|
+
this._engine.bindFramebuffer(renderTargetWrapper);
|
|
145
|
+
this._draw(renderTargetWrapper.width, renderTargetWrapper.height);
|
|
146
|
+
this._engine.unBindFramebuffer(renderTargetWrapper);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Renders the full screen effect into the main canvas.
|
|
151
|
+
*/
|
|
152
|
+
public renderToCanvas(): void {
|
|
153
|
+
this._effectRenderer.setViewport();
|
|
154
|
+
this._draw(this._engine.getRenderWidth(), this._engine.getRenderHeight());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* "Draws" the full screen effect into the currently bound output.
|
|
159
|
+
* @param width - defines the width of the output
|
|
160
|
+
* @param height - defines the height of the output
|
|
161
|
+
*/
|
|
162
|
+
private _draw(width: number, height: number): void {
|
|
163
|
+
this._effectRenderer.applyEffectWrapper(this._effectWrapper);
|
|
164
|
+
this._shaderBinding.bind(this._effectWrapper.effect, width, height);
|
|
165
|
+
this._effectRenderer.draw();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Disposes the runtime resources.
|
|
170
|
+
*/
|
|
171
|
+
public dispose(): void {
|
|
172
|
+
this._effectWrapper.dispose();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { EffectRenderer } from "core/Materials/effectRenderer.js";
|
|
2
|
+
import type { ThinEngine } from "core/Engines/thinEngine.js";
|
|
3
|
+
import { CommandBuffer } from "../command/commandBuffer.js";
|
|
4
|
+
import type { IDisposable } from "../IDisposable.js";
|
|
5
|
+
import type { Command } from "../command/command.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A runtime is a snapshot of a smart filter containing all the
|
|
9
|
+
* required data to render it as well as the entire command buffer.
|
|
10
|
+
*/
|
|
11
|
+
export type SmartFilterRuntime = {
|
|
12
|
+
/**
|
|
13
|
+
* The command buffer containing all the commands to execute during a frame.
|
|
14
|
+
*/
|
|
15
|
+
readonly commandBuffer: Readonly<CommandBuffer>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders one frame of the smart filter.
|
|
19
|
+
*/
|
|
20
|
+
render(): void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Dispose the runtime and all its associated resources
|
|
24
|
+
*/
|
|
25
|
+
dispose(): void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The internal runtime implementation exposing more information than @see SmartFilterRuntime.
|
|
30
|
+
* This is used internally to render the smart filter.
|
|
31
|
+
*
|
|
32
|
+
* It is not fully exposed publicly to prevent any misuse of the runtime.
|
|
33
|
+
*/
|
|
34
|
+
export class InternalSmartFilterRuntime implements SmartFilterRuntime {
|
|
35
|
+
/**
|
|
36
|
+
* The engine used by the smart filter.
|
|
37
|
+
*/
|
|
38
|
+
public readonly engine: ThinEngine;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The effect renderer used by the smart filter.
|
|
42
|
+
*/
|
|
43
|
+
public readonly effectRenderer: EffectRenderer;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The command buffer containing all the commands to execute during a frame.
|
|
47
|
+
*/
|
|
48
|
+
public readonly commandBuffer: CommandBuffer;
|
|
49
|
+
|
|
50
|
+
private readonly _resources: IDisposable[];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Instantiates a new smart filter runtime for one given engine.
|
|
54
|
+
* @param engine - the engine to use to render the smart filter
|
|
55
|
+
*/
|
|
56
|
+
constructor(engine: ThinEngine) {
|
|
57
|
+
this.engine = engine;
|
|
58
|
+
|
|
59
|
+
this._resources = [];
|
|
60
|
+
|
|
61
|
+
this.commandBuffer = new CommandBuffer();
|
|
62
|
+
|
|
63
|
+
this.effectRenderer = new EffectRenderer(engine);
|
|
64
|
+
this.registerResource(this.effectRenderer);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Register a resource to be disposed when the runtime is disposed.
|
|
69
|
+
* @param resource - defines the resource to dispose once the runtime is disposed
|
|
70
|
+
*/
|
|
71
|
+
public registerResource(resource: IDisposable): void {
|
|
72
|
+
this._resources.push(resource);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Registers a command to be executed during the render loop.
|
|
77
|
+
* @param command - defines the command to execute
|
|
78
|
+
*/
|
|
79
|
+
public registerCommand(command: Command): void {
|
|
80
|
+
this.commandBuffer.push(command);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Renders the smart filter.
|
|
85
|
+
* This function will execute all the commands contained int the command buffer.
|
|
86
|
+
*/
|
|
87
|
+
public render(): void {
|
|
88
|
+
try {
|
|
89
|
+
const depthTest = this.engine.depthCullingState.depthTest;
|
|
90
|
+
const stencilTest = this.engine.stencilState.stencilTest;
|
|
91
|
+
|
|
92
|
+
this.commandBuffer.execute();
|
|
93
|
+
|
|
94
|
+
// EffectRenderer.applyEffectWrapper(), which is called by ShaderRuntime._draw(),
|
|
95
|
+
// sets the depth/stencil state, so we need to restore it.
|
|
96
|
+
this.engine.depthCullingState.depthTest = depthTest;
|
|
97
|
+
this.engine.stencilState.stencilTest = stencilTest;
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// eslint-disable-next-line no-debugger
|
|
100
|
+
debugger;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Dispose the runtime and all its associated resources
|
|
106
|
+
*/
|
|
107
|
+
public dispose(): void {
|
|
108
|
+
for (const resource of this._resources) {
|
|
109
|
+
resource.dispose();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This describes a strong reference to any data type.
|
|
3
|
+
*/
|
|
4
|
+
export type StrongRef<T> = {
|
|
5
|
+
/**
|
|
6
|
+
* The value of the strong reference.
|
|
7
|
+
*/
|
|
8
|
+
value: T;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a strong reference to the given value.
|
|
13
|
+
* @param value - the value to wrap in a strong reference
|
|
14
|
+
* @returns the strong reference containing the value
|
|
15
|
+
*/
|
|
16
|
+
export function CreateStrongRef<T>(value: T): StrongRef<T> {
|
|
17
|
+
return { value };
|
|
18
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
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 { SerializedShaderBlockDefinition } from "./serializedShaderBlockDefinition.js";
|
|
5
|
+
import type { InputAutoBindV1, SerializedInputConnectionPointV1 } from "./v1/shaderBlockSerialization.types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Imports a serialized custom block definition. Supports either serialized CustomShaderBlock definitions or
|
|
9
|
+
* CustomAggregateBlock definitions. Can throw an exception if the serialized data is invalid.
|
|
10
|
+
*
|
|
11
|
+
* CustomShaderBlock definitions can be supplied either as serialized SerializedBlockDefinition object
|
|
12
|
+
* or a glsl shader with the required annotations (see readme.md for details).
|
|
13
|
+
*
|
|
14
|
+
* CustomAggregateBlock definitions must be supplied as serialized SerializedBlockDefinition object.
|
|
15
|
+
*
|
|
16
|
+
* @param serializedData - The serialized data
|
|
17
|
+
* @returns The serialized block definition
|
|
18
|
+
*/
|
|
19
|
+
export function ImportCustomBlockDefinition(serializedData: string): SerializedBlockDefinition {
|
|
20
|
+
if (HasGlslHeader(serializedData)) {
|
|
21
|
+
return ImportAnnotatedGlsl(serializedData);
|
|
22
|
+
} else {
|
|
23
|
+
// Assume this is a serialized JSON object
|
|
24
|
+
const blockDefinition = JSON.parse(serializedData);
|
|
25
|
+
|
|
26
|
+
// Some old SmartFilters didn't have a format property - default to smartFilter if missing
|
|
27
|
+
if (blockDefinition.format === undefined) {
|
|
28
|
+
blockDefinition.format = "smartFilter";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// SmartFilters can be serialized without a blockType
|
|
32
|
+
// By convention, we use the SmartFilter name as the blockType when importing them as SerializedBlockDefinitions
|
|
33
|
+
if (blockDefinition.format === "smartFilter" && blockDefinition.name && !blockDefinition.blockType) {
|
|
34
|
+
blockDefinition.blockType = blockDefinition.name;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validation
|
|
38
|
+
if (!blockDefinition.blockType) {
|
|
39
|
+
throw new Error("Could not find a blockType");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return blockDefinition;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Converts a fragment shader .glsl file to an SerializedBlockDefinition instance for use
|
|
48
|
+
* as a CustomShaderBlock. The .glsl file must contain certain annotations to be imported.
|
|
49
|
+
* See readme.md for more information.
|
|
50
|
+
* @param fragmentShader - The contents of the .glsl fragment shader file
|
|
51
|
+
* @returns The serialized block definition
|
|
52
|
+
*/
|
|
53
|
+
function ImportAnnotatedGlsl(fragmentShader: string): SerializedShaderBlockDefinition {
|
|
54
|
+
const fragmentShaderInfo = ParseFragmentShader(fragmentShader);
|
|
55
|
+
|
|
56
|
+
if (!fragmentShaderInfo.blockType) {
|
|
57
|
+
throw new Error("blockType must be defined");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Calculate the input connection points
|
|
61
|
+
const inputConnectionPoints: SerializedInputConnectionPointV1[] = [];
|
|
62
|
+
for (const uniform of fragmentShaderInfo.uniforms) {
|
|
63
|
+
// Add to input connection point list
|
|
64
|
+
const inputConnectionPoint: SerializedInputConnectionPointV1 = {
|
|
65
|
+
name: uniform.name,
|
|
66
|
+
type: uniform.type,
|
|
67
|
+
autoBind: uniform.properties?.autoBind as InputAutoBindV1,
|
|
68
|
+
};
|
|
69
|
+
if (inputConnectionPoint.type !== ConnectionPointType.Texture && uniform.properties?.default !== undefined) {
|
|
70
|
+
inputConnectionPoint.defaultValue = uniform.properties.default;
|
|
71
|
+
}
|
|
72
|
+
inputConnectionPoints.push(inputConnectionPoint);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
format: "shaderBlockDefinition",
|
|
77
|
+
formatVersion: 1,
|
|
78
|
+
blockType: fragmentShaderInfo.blockType,
|
|
79
|
+
namespace: fragmentShaderInfo.namespace,
|
|
80
|
+
shaderProgram: {
|
|
81
|
+
fragment: fragmentShaderInfo.shaderCode,
|
|
82
|
+
},
|
|
83
|
+
inputConnectionPoints,
|
|
84
|
+
disableOptimization: !!fragmentShaderInfo.disableOptimization,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
2
|
+
export * from "./v1/index.js";
|
|
3
|
+
export * from "./serializedSmartFilter.js";
|
|
4
|
+
export * from "./smartFilterDeserializer.js";
|
|
5
|
+
export * from "./smartFilterSerializer.js";
|
|
6
|
+
export * from "./serializedShaderBlockDefinition.js";
|
|
7
|
+
export * from "./serializedBlockDefinition.js";
|
|
8
|
+
export * from "./importCustomBlockDefinition.js";
|
|
9
|
+
// Back compat for when camelCase was used
|
|
10
|
+
export { ImportCustomBlockDefinition as importCustomBlockDefinition } from "./importCustomBlockDefinition.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SerializedShaderBlockDefinition } from "./serializedShaderBlockDefinition.js";
|
|
2
|
+
import type { SerializedSmartFilter } from "./serializedSmartFilter.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Type that represents any type of serialized block definition - shader or aggregate.
|
|
6
|
+
*/
|
|
7
|
+
export type SerializedBlockDefinition = (SerializedShaderBlockDefinition | SerializedSmartFilter) & {
|
|
8
|
+
/**
|
|
9
|
+
* The type of block this is.
|
|
10
|
+
*/
|
|
11
|
+
blockType: string;
|
|
12
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SerializedShaderBlockDefinitionV1 } from "./v1/shaderBlockSerialization.types.js";
|
|
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 SerializedShaderBlockDefinition = SerializedShaderBlockDefinitionV1;
|