@babylonjs/smart-filters 0.1.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.
Files changed (138) hide show
  1. package/dist/IDisposable.d.ts +10 -0
  2. package/dist/IDisposable.d.ts.map +1 -0
  3. package/dist/IDisposable.js +2 -0
  4. package/dist/IDisposable.js.map +1 -0
  5. package/dist/blocks/aggregateBlock.d.ts +50 -0
  6. package/dist/blocks/aggregateBlock.d.ts.map +1 -0
  7. package/dist/blocks/aggregateBlock.js +103 -0
  8. package/dist/blocks/aggregateBlock.js.map +1 -0
  9. package/dist/blocks/baseBlock.d.ts +160 -0
  10. package/dist/blocks/baseBlock.d.ts.map +1 -0
  11. package/dist/blocks/baseBlock.js +256 -0
  12. package/dist/blocks/baseBlock.js.map +1 -0
  13. package/dist/blocks/copyBlock.d.ts +59 -0
  14. package/dist/blocks/copyBlock.d.ts.map +1 -0
  15. package/dist/blocks/copyBlock.js +84 -0
  16. package/dist/blocks/copyBlock.js.map +1 -0
  17. package/dist/blocks/disableableBlock.d.ts +30 -0
  18. package/dist/blocks/disableableBlock.d.ts.map +1 -0
  19. package/dist/blocks/disableableBlock.js +22 -0
  20. package/dist/blocks/disableableBlock.js.map +1 -0
  21. package/dist/blocks/inputBlock.d.ts +64 -0
  22. package/dist/blocks/inputBlock.d.ts.map +1 -0
  23. package/dist/blocks/inputBlock.js +74 -0
  24. package/dist/blocks/inputBlock.js.map +1 -0
  25. package/dist/blocks/outputBlock.d.ts +42 -0
  26. package/dist/blocks/outputBlock.d.ts.map +1 -0
  27. package/dist/blocks/outputBlock.js +74 -0
  28. package/dist/blocks/outputBlock.js.map +1 -0
  29. package/dist/blocks/shaderBlock.d.ts +68 -0
  30. package/dist/blocks/shaderBlock.d.ts.map +1 -0
  31. package/dist/blocks/shaderBlock.js +101 -0
  32. package/dist/blocks/shaderBlock.js.map +1 -0
  33. package/dist/command/command.d.ts +49 -0
  34. package/dist/command/command.d.ts.map +1 -0
  35. package/dist/command/command.js +15 -0
  36. package/dist/command/command.js.map +1 -0
  37. package/dist/command/commandBuffer.d.ts +40 -0
  38. package/dist/command/commandBuffer.d.ts.map +1 -0
  39. package/dist/command/commandBuffer.js +58 -0
  40. package/dist/command/commandBuffer.js.map +1 -0
  41. package/dist/command/commandBufferDebugger.d.ts +7 -0
  42. package/dist/command/commandBufferDebugger.d.ts.map +1 -0
  43. package/dist/command/commandBufferDebugger.js +12 -0
  44. package/dist/command/commandBufferDebugger.js.map +1 -0
  45. package/dist/connection/connectionPoint.d.ts +110 -0
  46. package/dist/connection/connectionPoint.d.ts.map +1 -0
  47. package/dist/connection/connectionPoint.js +153 -0
  48. package/dist/connection/connectionPoint.js.map +1 -0
  49. package/dist/connection/connectionPointCompatibilityState.d.ts +20 -0
  50. package/dist/connection/connectionPointCompatibilityState.d.ts.map +1 -0
  51. package/dist/connection/connectionPointCompatibilityState.js +32 -0
  52. package/dist/connection/connectionPointCompatibilityState.js.map +1 -0
  53. package/dist/connection/connectionPointDirection.d.ts +10 -0
  54. package/dist/connection/connectionPointDirection.d.ts.map +1 -0
  55. package/dist/connection/connectionPointDirection.js +11 -0
  56. package/dist/connection/connectionPointDirection.js.map +1 -0
  57. package/dist/connection/connectionPointType.d.ts +22 -0
  58. package/dist/connection/connectionPointType.d.ts.map +1 -0
  59. package/dist/connection/connectionPointType.js +17 -0
  60. package/dist/connection/connectionPointType.js.map +1 -0
  61. package/dist/connection/connectionPointWithDefault.d.ts +23 -0
  62. package/dist/connection/connectionPointWithDefault.d.ts.map +1 -0
  63. package/dist/connection/connectionPointWithDefault.js +19 -0
  64. package/dist/connection/connectionPointWithDefault.js.map +1 -0
  65. package/dist/index.d.ts +28 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +28 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/optimization/dependencyGraph.d.ts +31 -0
  70. package/dist/optimization/dependencyGraph.d.ts.map +1 -0
  71. package/dist/optimization/dependencyGraph.js +77 -0
  72. package/dist/optimization/dependencyGraph.js.map +1 -0
  73. package/dist/optimization/optimizedShaderBlock.d.ts +75 -0
  74. package/dist/optimization/optimizedShaderBlock.d.ts.map +1 -0
  75. package/dist/optimization/optimizedShaderBlock.js +105 -0
  76. package/dist/optimization/optimizedShaderBlock.js.map +1 -0
  77. package/dist/optimization/smartFilterOptimizer.d.ts +72 -0
  78. package/dist/optimization/smartFilterOptimizer.d.ts.map +1 -0
  79. package/dist/optimization/smartFilterOptimizer.js +482 -0
  80. package/dist/optimization/smartFilterOptimizer.js.map +1 -0
  81. package/dist/runtime/renderTargetGenerator.d.ts +35 -0
  82. package/dist/runtime/renderTargetGenerator.d.ts.map +1 -0
  83. package/dist/runtime/renderTargetGenerator.js +153 -0
  84. package/dist/runtime/renderTargetGenerator.js.map +1 -0
  85. package/dist/runtime/shaderRuntime.d.ts +95 -0
  86. package/dist/runtime/shaderRuntime.d.ts.map +1 -0
  87. package/dist/runtime/shaderRuntime.js +120 -0
  88. package/dist/runtime/shaderRuntime.js.map +1 -0
  89. package/dist/runtime/smartFilterRuntime.d.ts +69 -0
  90. package/dist/runtime/smartFilterRuntime.d.ts.map +1 -0
  91. package/dist/runtime/smartFilterRuntime.js +57 -0
  92. package/dist/runtime/smartFilterRuntime.js.map +1 -0
  93. package/dist/runtime/strongRef.d.ts +16 -0
  94. package/dist/runtime/strongRef.d.ts.map +1 -0
  95. package/dist/runtime/strongRef.js +9 -0
  96. package/dist/runtime/strongRef.js.map +1 -0
  97. package/dist/smartFilter.d.ts +100 -0
  98. package/dist/smartFilter.d.ts.map +1 -0
  99. package/dist/smartFilter.js +154 -0
  100. package/dist/smartFilter.js.map +1 -0
  101. package/dist/utils/shaderCodeUtils.d.ts +131 -0
  102. package/dist/utils/shaderCodeUtils.d.ts.map +1 -0
  103. package/dist/utils/shaderCodeUtils.js +115 -0
  104. package/dist/utils/shaderCodeUtils.js.map +1 -0
  105. package/dist/utils/textureLoaders.d.ts +14 -0
  106. package/dist/utils/textureLoaders.d.ts.map +1 -0
  107. package/dist/utils/textureLoaders.js +22 -0
  108. package/dist/utils/textureLoaders.js.map +1 -0
  109. package/license.md +21 -0
  110. package/package.json +47 -0
  111. package/readme.md +165 -0
  112. package/src/IDisposable.ts +9 -0
  113. package/src/blocks/aggregateBlock.ts +121 -0
  114. package/src/blocks/baseBlock.ts +341 -0
  115. package/src/blocks/copyBlock.ts +103 -0
  116. package/src/blocks/disableableBlock.ts +40 -0
  117. package/src/blocks/inputBlock.ts +114 -0
  118. package/src/blocks/outputBlock.ts +97 -0
  119. package/src/blocks/shaderBlock.ts +145 -0
  120. package/src/command/command.ts +60 -0
  121. package/src/command/commandBuffer.ts +71 -0
  122. package/src/command/commandBufferDebugger.ts +13 -0
  123. package/src/connection/connectionPoint.ts +212 -0
  124. package/src/connection/connectionPointCompatibilityState.ts +31 -0
  125. package/src/connection/connectionPointDirection.ts +9 -0
  126. package/src/connection/connectionPointType.ts +30 -0
  127. package/src/connection/connectionPointWithDefault.ts +35 -0
  128. package/src/index.ts +36 -0
  129. package/src/optimization/dependencyGraph.ts +94 -0
  130. package/src/optimization/optimizedShaderBlock.ts +133 -0
  131. package/src/optimization/smartFilterOptimizer.ts +706 -0
  132. package/src/runtime/renderTargetGenerator.ts +204 -0
  133. package/src/runtime/shaderRuntime.ts +155 -0
  134. package/src/runtime/smartFilterRuntime.ts +104 -0
  135. package/src/runtime/strongRef.ts +18 -0
  136. package/src/smartFilter.ts +227 -0
  137. package/src/utils/shaderCodeUtils.ts +242 -0
  138. package/src/utils/textureLoaders.ts +28 -0
@@ -0,0 +1,212 @@
1
+ import type { Nullable } from "@babylonjs/core/types";
2
+ import {
3
+ ConnectionPointCompatibilityState,
4
+ getCompatibilityIssueMessage,
5
+ } from "./connectionPointCompatibilityState.js";
6
+ import { ConnectionPointDirection } from "./connectionPointDirection.js";
7
+ import type { BaseBlock } from "../blocks/baseBlock";
8
+ import type { ConnectionPointType, ConnectionPointValue } from "./connectionPointType";
9
+ import type { StrongRef } from "../runtime/strongRef";
10
+
11
+ /**
12
+ * This represents a strong reference to the data being passed through a connection point.
13
+ */
14
+ export type RuntimeData<U extends ConnectionPointType> = StrongRef<ConnectionPointValue<U>>;
15
+
16
+ /**
17
+ * This defines a connection point.
18
+ *
19
+ * A connection point is any input/output of a block.
20
+ * It can be linked to another connection point following some rules:
21
+ * - The type of the connection point must be compatible with the other one.
22
+ * - The direction of the connection point must be different from the other one.
23
+ * - The connection cannot create a cycle in the list of blocks.
24
+ * The relationship is always 1:N for input:output
25
+ */
26
+ export class ConnectionPoint<U extends ConnectionPointType = ConnectionPointType> {
27
+ /**
28
+ * The name of the connection point.
29
+ * This is used to identify the connection point inside a block.
30
+ */
31
+ public readonly name: string;
32
+
33
+ /**
34
+ * The type of the connection point (float, texture, etc.)
35
+ */
36
+ public readonly type: U;
37
+
38
+ /**
39
+ * The direction of the connection point (input or output)
40
+ */
41
+ public readonly direction: ConnectionPointDirection;
42
+
43
+ /**
44
+ * The smart filter block the connection point belongs to.
45
+ */
46
+ public readonly ownerBlock: BaseBlock;
47
+
48
+ /**
49
+ * User provided name for the connection point.
50
+ */
51
+ public displayName: Nullable<string> = null;
52
+
53
+ /**
54
+ * The @see RunTimeData used during the init phase to reference the result of the previous block.
55
+ * Those are only used for input connection points.
56
+ * The previous block are "pushing" this in during the init stage.
57
+ */
58
+ public runtimeData: Nullable<RuntimeData<U>> = null;
59
+
60
+ /**
61
+ * The default runtimeData used when no connection is made to the connection point.
62
+ */
63
+ public readonly defaultRuntimeData: Nullable<RuntimeData<U>> = null;
64
+
65
+ private _connectedTo: Nullable<ConnectionPoint<U>> = null;
66
+ private _endpoints: Array<ConnectionPoint<U>> = [];
67
+
68
+ /**
69
+ * Create a new connection point.
70
+ * @param name - The name the connection point has in the block
71
+ * @param ownerBlock - The block the connection point belongs to
72
+ * @param type - The type of the connection point
73
+ * @param direction - The direction of the connection point
74
+ * @param defaultRuntimeData - The default runtime data to use when no connection is made to the connection point
75
+ */
76
+ constructor(
77
+ name: string,
78
+ ownerBlock: BaseBlock,
79
+ type: U,
80
+ direction: ConnectionPointDirection,
81
+ defaultRuntimeData: Nullable<RuntimeData<U>> = null
82
+ ) {
83
+ this.name = name;
84
+ this.ownerBlock = ownerBlock;
85
+ this.type = type;
86
+ this.direction = direction;
87
+ this.defaultRuntimeData = defaultRuntimeData;
88
+ this.runtimeData = defaultRuntimeData;
89
+ }
90
+
91
+ /**
92
+ * @returns the connection point this connection point is connected to.
93
+ * (the one on the other side of the connection)
94
+ * Only input connection points have a connected point which they received their value from.
95
+ * (Relation is always 1:N for input:output)
96
+ */
97
+ public get connectedTo(): Nullable<ConnectionPoint<U>> {
98
+ return this._connectedTo;
99
+ }
100
+
101
+ /**
102
+ * @returns the connection point this connection point is to.
103
+ * (the one on the other side of the connection)
104
+ * Only output connection points have a list of endpoints which they provide their value to.
105
+ * (Relation is always 1:N for input:output)
106
+ */
107
+ public get endpoints(): ReadonlyArray<ConnectionPoint<U>> {
108
+ return this._endpoints;
109
+ }
110
+
111
+ /**
112
+ * Gets a state indicating if the current point can be connected to another point
113
+ * @param other - defines the other connection point
114
+ * @returns the compatibility state
115
+ */
116
+ public checkCompatibilityState(other: ConnectionPoint<U>): ConnectionPointCompatibilityState {
117
+ // Only connects output to input
118
+ if (this.direction === ConnectionPointDirection.Input) {
119
+ return other.checkCompatibilityState(this);
120
+ }
121
+
122
+ // Check types
123
+ if (this.type !== other.type) {
124
+ return ConnectionPointCompatibilityState.TypeIncompatible;
125
+ }
126
+
127
+ // Check directions
128
+ if (this.direction === other.direction) {
129
+ return ConnectionPointCompatibilityState.DirectionIncompatible;
130
+ }
131
+
132
+ // Check hierarchy
133
+ if (other.ownerBlock.isAnAncestorOf(this.ownerBlock)) {
134
+ return ConnectionPointCompatibilityState.HierarchyIssue;
135
+ }
136
+
137
+ return ConnectionPointCompatibilityState.Compatible;
138
+ }
139
+
140
+ /**
141
+ * Checks if the connection point can be connected to another one.
142
+ * @param connectionPoint - The other connection point to check compatibility with
143
+ * @returns true if the connection point can be connected to the other one
144
+ */
145
+ public canConnectTo(connectionPoint: ConnectionPoint<U>) {
146
+ return this.checkCompatibilityState(connectionPoint) === ConnectionPointCompatibilityState.Compatible;
147
+ }
148
+
149
+ /**
150
+ * Connect this connection point to another one.
151
+ * @param other - The other connection point to connect to
152
+ * @throws if the connection point cannot be connected to the other one
153
+ */
154
+ public connectTo(other: ConnectionPoint<U>): void {
155
+ // Only connects output to input
156
+ if (this.direction === ConnectionPointDirection.Input) {
157
+ other.connectTo(this);
158
+ return;
159
+ }
160
+
161
+ // Check compatibility
162
+ const compatibility = this.checkCompatibilityState(other);
163
+ if (compatibility !== ConnectionPointCompatibilityState.Compatible) {
164
+ throw getCompatibilityIssueMessage(compatibility);
165
+ }
166
+
167
+ // Adds the connection point to the list of endpoints
168
+ this._endpoints.push(other);
169
+ // Fill at the same time the connectedTo property of the other connection point
170
+ other._connectedTo = this;
171
+ }
172
+
173
+ /**
174
+ * Disconnects this point from one of his endpoint
175
+ * @param endpoint - defines the other connection point
176
+ */
177
+ public disconnectFrom(endpoint: ConnectionPoint<U>): void {
178
+ const index = this._endpoints.indexOf(endpoint);
179
+ if (index === -1) {
180
+ return;
181
+ }
182
+
183
+ // Remove the connection point from the list of endpoints
184
+ this._endpoints.splice(index, 1);
185
+ // Empty at the same time the connectedTo property of the other connection point
186
+ endpoint._connectedTo = null;
187
+ }
188
+
189
+ /**
190
+ * Disconnects this point from all its endpoints.
191
+ */
192
+ public disconnectAllEndpoints(): void {
193
+ // Detach outputs
194
+ let endpoint: ConnectionPoint<U> | undefined;
195
+ while ((endpoint = this._endpoints[0])) {
196
+ this.disconnectFrom(endpoint);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Propagates the current runtime data to all endpoints.
202
+ */
203
+ public propagateRuntimeData(): void {
204
+ if (!this.runtimeData) {
205
+ this.runtimeData = {} as RuntimeData<U>;
206
+ }
207
+
208
+ for (const endpoint of this.endpoints) {
209
+ endpoint.runtimeData = this.runtimeData;
210
+ }
211
+ }
212
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Defines how compatible connection points are.
3
+ */
4
+ export enum ConnectionPointCompatibilityState {
5
+ /** Points are compatibles */
6
+ Compatible,
7
+ /** Points are incompatible because of their types */
8
+ TypeIncompatible,
9
+ /** Points are incompatible because of their directions */
10
+ DirectionIncompatible,
11
+ /** Points are incompatible because they are in the same hierarchy **/
12
+ HierarchyIssue,
13
+ }
14
+
15
+ /**
16
+ * Gets a user friendly message for the given compatibility state.
17
+ * @param state - Defines the compatibility state
18
+ * @returns the message associated with a compatibility state.
19
+ */
20
+ export function getCompatibilityIssueMessage(state: ConnectionPointCompatibilityState): string {
21
+ switch (state) {
22
+ case ConnectionPointCompatibilityState.TypeIncompatible:
23
+ return "Cannot connect two different connection types";
24
+ case ConnectionPointCompatibilityState.DirectionIncompatible:
25
+ return "Cannot connect with the same direction";
26
+ case ConnectionPointCompatibilityState.HierarchyIssue:
27
+ return "Source block cannot be connected with one of its ancestors";
28
+ default:
29
+ return "";
30
+ }
31
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Defines the direction of a connection point.
3
+ */
4
+ export enum ConnectionPointDirection {
5
+ /** Input */
6
+ Input = 0,
7
+ /** Output */
8
+ Output = 1,
9
+ }
@@ -0,0 +1,30 @@
1
+ import type { ThinTexture } from "@babylonjs/core/Materials/Textures/thinTexture";
2
+ import type { IColor3Like, IColor4Like } from "@babylonjs/core/Maths/math.like";
3
+
4
+ /**
5
+ * Defines the type of a connection point.
6
+ */
7
+ export enum ConnectionPointType {
8
+ /** Float */
9
+ Float = 1,
10
+ /** Texture */
11
+ Texture = 2,
12
+ /** Color3 */
13
+ Color3 = 3,
14
+ /** Color4 */
15
+ Color4 = 4,
16
+ /** Boolean */
17
+ Boolean = 5,
18
+ }
19
+
20
+ /**
21
+ * Retrieves the type of the value from the Connection point type.
22
+ */
23
+ // prettier-ignore
24
+ export type ConnectionPointValue<T extends ConnectionPointType = ConnectionPointType> =
25
+ T extends ConnectionPointType.Float ? number :
26
+ T extends ConnectionPointType.Texture ? ThinTexture :
27
+ T extends ConnectionPointType.Color3 ? IColor3Like :
28
+ T extends ConnectionPointType.Color4 ? IColor4Like :
29
+ T extends ConnectionPointType.Boolean ? boolean :
30
+ never;
@@ -0,0 +1,35 @@
1
+ import type { BaseBlock } from "../blocks/baseBlock";
2
+ import { ConnectionPoint, type RuntimeData } from "./connectionPoint.js";
3
+ import type { ConnectionPointDirection } from "./connectionPointDirection";
4
+ import type { ConnectionPointType } from "./connectionPointType";
5
+
6
+ /**
7
+ * A ConnectionPoint whose runtimeData is never null - if not hooked up to a connection, it will use a default value.
8
+ */
9
+ export class ConnectionPointWithDefault<
10
+ U extends ConnectionPointType = ConnectionPointType,
11
+ > extends ConnectionPoint<U> {
12
+ /**
13
+ * The runtime data for this ConnectionPoint - it will never be null - if not hooked up to a connection, it will use the default value.
14
+ */
15
+ public override runtimeData: RuntimeData<U>;
16
+
17
+ /**
18
+ * Create a new ConnectionPointWithDefault
19
+ * @param name - The name the connection point has in the block
20
+ * @param ownerBlock - The block the connection point belongs to
21
+ * @param type - The type of the connection point
22
+ * @param direction - The direction of the connection point
23
+ * @param runtimeData - The runtimeData to use for this connection point if no connection is made
24
+ */
25
+ constructor(
26
+ name: string,
27
+ ownerBlock: BaseBlock,
28
+ type: U,
29
+ direction: ConnectionPointDirection,
30
+ runtimeData: RuntimeData<U>
31
+ ) {
32
+ super(name, ownerBlock, type, direction, runtimeData);
33
+ this.runtimeData = runtimeData;
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ export { type IDisposable } from "./IDisposable.js";
2
+ export { type StrongRef } from "./runtime/strongRef.js";
3
+ export { createStrongRef } from "./runtime/strongRef.js";
4
+
5
+ export * from "./command/command.js";
6
+ export * from "./command/commandBuffer.js";
7
+ export { logCommands } from "./command/commandBufferDebugger.js";
8
+
9
+ export { ConnectionPointDirection } from "./connection/connectionPointDirection.js";
10
+ export { ConnectionPointType } from "./connection/connectionPointType.js";
11
+ export { type ConnectionPointValue } from "./connection/connectionPointType.js";
12
+ export {
13
+ ConnectionPointCompatibilityState,
14
+ getCompatibilityIssueMessage,
15
+ } from "./connection/connectionPointCompatibilityState.js";
16
+ export { ConnectionPoint } from "./connection/connectionPoint.js";
17
+ export { type RuntimeData } from "./connection/connectionPoint.js";
18
+
19
+ export { BaseBlock } from "./blocks/baseBlock.js";
20
+ export { InputBlock } from "./blocks/inputBlock.js";
21
+ export { type AnyInputBlock } from "./blocks/inputBlock.js";
22
+ export { ShaderBlock } from "./blocks/shaderBlock.js";
23
+ export { AggregateBlock } from "./blocks/aggregateBlock.js";
24
+ export { CopyBlock } from "./blocks/copyBlock.js";
25
+ export { ShaderBinding, ShaderRuntime } from "./runtime/shaderRuntime.js";
26
+ export { type ShaderProgram, injectDisableUniform } from "./utils/shaderCodeUtils.js";
27
+ export { type IDisableableBlock } from "./blocks/disableableBlock.js";
28
+
29
+ export { type SmartFilterRuntime } from "./runtime/smartFilterRuntime.js";
30
+ export { InternalSmartFilterRuntime } from "./runtime/smartFilterRuntime.js";
31
+ export { RenderTargetGenerator } from "./runtime/renderTargetGenerator.js";
32
+
33
+ export { SmartFilter } from "./smartFilter.js";
34
+
35
+ export { SmartFilterOptimizer } from "./optimization/smartFilterOptimizer.js";
36
+ export * from "./utils/textureLoaders.js";
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Implementation of a dependency graph.
3
+ */
4
+ export class DependencyGraph<T> {
5
+ private _list: Set<T>;
6
+ private _dependOn: Map<T, Set<T>>;
7
+ private _requiredBy: Map<T, Set<T>>;
8
+
9
+ /**
10
+ * Creates a new instance of a dependency graph.
11
+ */
12
+ constructor() {
13
+ this._list = new Set();
14
+ this._dependOn = new Map();
15
+ this._requiredBy = new Map();
16
+ }
17
+
18
+ /**
19
+ * Adds an element to the graph.
20
+ * @param element - The element to add to the graph.
21
+ */
22
+ public addElement(element: T) {
23
+ if (this._list.has(element)) {
24
+ throw new Error(`Element "${element}" already added to the graph!`);
25
+ }
26
+
27
+ this._list.add(element);
28
+ }
29
+
30
+ /**
31
+ * Adds a dependency between two elements.
32
+ * @param element - The element that depends on another element.
33
+ * @param dependency - The element that is required by the element passed as the first parameter.
34
+ */
35
+ public addDependency(element: T, dependency: T) {
36
+ if (!this._dependOn.has(element)) {
37
+ this._dependOn.set(element, new Set());
38
+ }
39
+
40
+ if (!this._requiredBy.has(dependency)) {
41
+ this._requiredBy.set(dependency, new Set());
42
+ }
43
+
44
+ this._dependOn.get(element)!.add(dependency);
45
+ this._requiredBy.get(dependency)!.add(element);
46
+ }
47
+
48
+ /**
49
+ * Walks through the graph and calls the callback for each element.
50
+ * The elements that depend on other elements will be traversed after the elements they depend on.
51
+ * Note: the graph will be modified during the walk, so don't call walk twice on the same graph!
52
+ * @param callback - The callback to call for each element.
53
+ */
54
+ public walk(callback: (element: T) => void) {
55
+ const toVisit: T[] = [];
56
+
57
+ // Collect all elements that have no dependency
58
+ for (const element of this._list) {
59
+ if (!this._dependOn.get(element)) {
60
+ toVisit.push(element);
61
+ this._list.delete(element);
62
+ }
63
+ }
64
+
65
+ // Loop over all elements that have no dependency
66
+ while (toVisit.length > 0) {
67
+ const element = toVisit.shift()!;
68
+
69
+ callback(element);
70
+
71
+ this._list.delete(element);
72
+
73
+ const requiredBy = this._requiredBy.get(element);
74
+ if (requiredBy) {
75
+ for (const dependingElement of requiredBy) {
76
+ const dependencies = this._dependOn.get(dependingElement);
77
+
78
+ if (dependencies) {
79
+ dependencies.delete(element);
80
+
81
+ if (dependencies.size === 0) {
82
+ toVisit.push(dependingElement);
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ if (this._list.size > 0) {
90
+ console.error(this._list);
91
+ throw new Error("Circular dependency detected!");
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,133 @@
1
+ import type { Effect } from "@babylonjs/core/Materials/effect";
2
+ import type { Nullable } from "@babylonjs/core/types";
3
+ import type { ThinTexture } from "@babylonjs/core/Materials/Textures/thinTexture";
4
+
5
+ import type { SmartFilter } from "../smartFilter";
6
+ import type { ShaderProgram } from "../utils/shaderCodeUtils";
7
+ import type { StrongRef } from "../runtime/strongRef";
8
+ import type { RuntimeData } from "../connection/connectionPoint";
9
+ import { ShaderBlock } from "../blocks/shaderBlock.js";
10
+ import { Binding } from "../runtime/shaderRuntime.js";
11
+ import { ConnectionPointType } from "../connection/connectionPointType.js";
12
+
13
+ /**
14
+ * The shader bindings for the OptimizedShader block.
15
+ * @internal
16
+ */
17
+ export class OptimizedShaderBinding extends Binding {
18
+ private _shaderBindings: Binding[];
19
+ private _inputTextures: { [name: string]: StrongRef<ThinTexture> };
20
+
21
+ /**
22
+ * Creates a new shader binding instance for the OptimizedShader block.
23
+ * @param shaderBindings - The list of shader bindings to process
24
+ * @param inputTextures - The list of input textures to bind
25
+ */
26
+ constructor(shaderBindings: Binding[], inputTextures: { [name: string]: StrongRef<ThinTexture> }) {
27
+ super();
28
+
29
+ this._shaderBindings = shaderBindings;
30
+ this._inputTextures = inputTextures;
31
+ }
32
+
33
+ /**
34
+ * Binds all the required data to the shader when rendering.
35
+ * @param effect - defines the effect to bind the data to
36
+ * @param width - defines the width of the output
37
+ * @param height - defines the height of the output
38
+ */
39
+ public override bind(effect: Effect, width: number, height: number): void {
40
+ for (const shaderBinding of this._shaderBindings) {
41
+ shaderBinding.bind(effect, width, height);
42
+ }
43
+
44
+ for (const name in this._inputTextures) {
45
+ const texture = this._inputTextures[name];
46
+ // texture can't be undefined, so let's add "!" to make Typescript happy
47
+ effect.setTexture(this.getRemappedName(name), texture!.value);
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * A block used by the smart filter optimizer to group shader blocks together.
54
+ * Should be for internal use only.
55
+ * @internal
56
+ */
57
+ export class OptimizedShaderBlock extends ShaderBlock {
58
+ private _shaderBindings: Nullable<Binding[]>;
59
+ private _inputTextures: { [name: string]: StrongRef<ThinTexture> } = {};
60
+ private _shaderProgram: ShaderProgram;
61
+
62
+ /**
63
+ * The class name of the block.
64
+ */
65
+ public static override ClassName = "OptimizedShaderBlock";
66
+
67
+ /**
68
+ * Returns if the block is an input block.
69
+ */
70
+ public override get isInput(): boolean {
71
+ return false;
72
+ }
73
+
74
+ /**
75
+ * Creates a new OptimizedShaderBlock.
76
+ * @param smartFilter - The smart filter to add the block to
77
+ * @param name - The name of the block
78
+ */
79
+ constructor(smartFilter: SmartFilter, name: string) {
80
+ super(smartFilter, name, true);
81
+
82
+ this._shaderBindings = null;
83
+ this._shaderProgram = undefined as any;
84
+ }
85
+
86
+ /**
87
+ * Gets the shader program to use to render the block.
88
+ * @returns The shader program to use to render the block
89
+ */
90
+ public override getShaderProgram(): ShaderProgram {
91
+ return this._shaderProgram;
92
+ }
93
+
94
+ /**
95
+ * Sets the shader program to use to render the block.
96
+ * @param shaderProgram - The shader program to use to render the block
97
+ */
98
+ public setShaderProgram(shaderProgram: ShaderProgram): void {
99
+ this._shaderProgram = shaderProgram;
100
+ }
101
+
102
+ /**
103
+ * Sets the list of shader bindings to use to render the block.
104
+ * @param shaderBindings - The list of shader bindings to use to render the block
105
+ */
106
+ public setShaderBindings(shaderBindings: Binding[]): void {
107
+ this._shaderBindings = shaderBindings;
108
+ }
109
+
110
+ /**
111
+ * Get the class instance that binds all the required data to the shader (effect) when rendering.
112
+ * @returns The class instance that binds the data to the effect
113
+ */
114
+ public getShaderBinding(): Binding {
115
+ if (this._shaderBindings === null) {
116
+ throw new Error("Shader bindings not set!");
117
+ }
118
+
119
+ for (const input of this.inputs) {
120
+ const name = input.name;
121
+
122
+ if (input.type === ConnectionPointType.Texture) {
123
+ /**
124
+ * These are the inputs created by the OptimizedShaderBlock
125
+ * We pass them to OptimizedShaderBinding so that their value can be set appropriately at runtime (in the bind method)
126
+ */
127
+ this._inputTextures[name] = input.runtimeData as RuntimeData<ConnectionPointType.Texture>;
128
+ }
129
+ }
130
+
131
+ return new OptimizedShaderBinding(this._shaderBindings, this._inputTextures);
132
+ }
133
+ }