@luma.gl/engine 9.0.0-beta.1 → 9.0.0-beta.10

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 (154) hide show
  1. package/dist/animation/key-frames.js +54 -54
  2. package/dist/animation/timeline.d.ts.map +1 -1
  3. package/dist/animation/timeline.js +95 -100
  4. package/dist/animation-loop/animation-loop-template.d.ts +1 -1
  5. package/dist/animation-loop/animation-loop-template.d.ts.map +1 -1
  6. package/dist/animation-loop/animation-loop-template.js +19 -5
  7. package/dist/animation-loop/animation-loop.d.ts +2 -2
  8. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  9. package/dist/animation-loop/animation-loop.js +433 -356
  10. package/dist/animation-loop/animation-props.d.ts +2 -2
  11. package/dist/animation-loop/animation-props.d.ts.map +1 -1
  12. package/dist/animation-loop/animation-props.js +0 -1
  13. package/dist/animation-loop/make-animation-loop.d.ts +2 -2
  14. package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
  15. package/dist/animation-loop/make-animation-loop.js +28 -24
  16. package/dist/computation.d.ts +95 -0
  17. package/dist/computation.d.ts.map +1 -0
  18. package/dist/computation.js +248 -0
  19. package/dist/debug/copy-texture-to-image.d.ts.map +1 -1
  20. package/dist/debug/copy-texture-to-image.js +39 -42
  21. package/dist/debug/debug-framebuffer.d.ts.map +1 -1
  22. package/dist/debug/debug-framebuffer.js +43 -40
  23. package/dist/debug/debug-shader-layout.js +24 -25
  24. package/dist/debug/pixel-data-utils.d.ts.map +1 -1
  25. package/dist/debug/pixel-data-utils.js +34 -36
  26. package/dist/dist.dev.js +2538 -3027
  27. package/dist/dist.min.js +102 -0
  28. package/dist/geometries/cone-geometry.d.ts +1 -1
  29. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  30. package/dist/geometries/cone-geometry.js +11 -17
  31. package/dist/geometries/cube-geometry.d.ts +1 -1
  32. package/dist/geometries/cube-geometry.d.ts.map +1 -1
  33. package/dist/geometries/cube-geometry.js +190 -61
  34. package/dist/geometries/cylinder-geometry.d.ts +1 -1
  35. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  36. package/dist/geometries/cylinder-geometry.js +9 -14
  37. package/dist/geometries/ico-sphere-geometry.d.ts +1 -1
  38. package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -1
  39. package/dist/geometries/ico-sphere-geometry.js +141 -160
  40. package/dist/geometries/plane-geometry.d.ts +1 -1
  41. package/dist/geometries/plane-geometry.d.ts.map +1 -1
  42. package/dist/geometries/plane-geometry.js +92 -110
  43. package/dist/geometries/sphere-geometry.d.ts +1 -1
  44. package/dist/geometries/sphere-geometry.d.ts.map +1 -1
  45. package/dist/geometries/sphere-geometry.js +76 -95
  46. package/dist/geometries/truncated-cone-geometry.d.ts +1 -1
  47. package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
  48. package/dist/geometries/truncated-cone-geometry.js +99 -117
  49. package/dist/geometry/geometry-table.d.ts.map +1 -1
  50. package/dist/geometry/geometry-table.js +3 -1
  51. package/dist/geometry/geometry-utils.js +35 -32
  52. package/dist/geometry/geometry.d.ts.map +1 -1
  53. package/dist/geometry/geometry.js +80 -71
  54. package/dist/geometry/gpu-geometry.d.ts +1 -1
  55. package/dist/geometry/gpu-geometry.d.ts.map +1 -1
  56. package/dist/geometry/gpu-geometry.js +79 -99
  57. package/dist/geometry/gpu-table.js +41 -1
  58. package/dist/index.cjs +725 -409
  59. package/dist/index.cjs.map +7 -0
  60. package/dist/index.d.ts +43 -40
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +6 -1
  63. package/dist/lib/clip-space.d.ts +2 -2
  64. package/dist/lib/clip-space.d.ts.map +1 -1
  65. package/dist/lib/clip-space.js +28 -34
  66. package/dist/lib/pipeline-factory.d.ts +13 -14
  67. package/dist/lib/pipeline-factory.d.ts.map +1 -1
  68. package/dist/lib/pipeline-factory.js +86 -85
  69. package/dist/lib/shader-factory.d.ts +17 -0
  70. package/dist/lib/shader-factory.d.ts.map +1 -0
  71. package/dist/lib/shader-factory.js +46 -0
  72. package/dist/model/model.d.ts +59 -46
  73. package/dist/model/model.d.ts.map +1 -1
  74. package/dist/model/model.js +635 -411
  75. package/dist/scenegraph/group-node.d.ts +1 -1
  76. package/dist/scenegraph/group-node.d.ts.map +1 -1
  77. package/dist/scenegraph/group-node.js +73 -83
  78. package/dist/scenegraph/model-node.d.ts +3 -3
  79. package/dist/scenegraph/model-node.d.ts.map +1 -1
  80. package/dist/scenegraph/model-node.js +31 -24
  81. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  82. package/dist/scenegraph/scenegraph-node.js +136 -124
  83. package/dist/shader-inputs.d.ts.map +1 -1
  84. package/dist/shader-inputs.js +99 -58
  85. package/dist/transform/buffer-transform.d.ts +1 -1
  86. package/dist/transform/buffer-transform.d.ts.map +1 -1
  87. package/dist/transform/buffer-transform.js +65 -57
  88. package/dist/transform/texture-transform.d.ts +1 -1
  89. package/dist/transform/texture-transform.d.ts.map +1 -1
  90. package/dist/transform/texture-transform.js +109 -114
  91. package/dist.min.js +3 -271
  92. package/package.json +10 -9
  93. package/src/animation/timeline.ts +20 -20
  94. package/src/animation-loop/animation-loop-template.ts +10 -8
  95. package/src/animation-loop/animation-loop.ts +20 -10
  96. package/src/animation-loop/animation-props.ts +1 -1
  97. package/src/animation-loop/make-animation-loop.ts +17 -8
  98. package/src/computation.ts +346 -0
  99. package/src/debug/copy-texture-to-image.ts +9 -11
  100. package/src/debug/debug-framebuffer.ts +16 -3
  101. package/src/debug/debug-shader-layout.ts +1 -1
  102. package/src/debug/pixel-data-utils.ts +3 -6
  103. package/src/geometries/cube-geometry.ts +17 -13
  104. package/src/geometries/ico-sphere-geometry.ts +1 -1
  105. package/src/geometries/plane-geometry.ts +1 -1
  106. package/src/geometries/sphere-geometry.ts +1 -1
  107. package/src/geometries/truncated-cone-geometry.ts +2 -1
  108. package/src/geometry/geometry-table.ts +9 -6
  109. package/src/geometry/geometry-utils.ts +16 -0
  110. package/src/geometry/geometry.ts +9 -6
  111. package/src/geometry/gpu-geometry.ts +18 -11
  112. package/src/index.ts +4 -1
  113. package/src/lib/clip-space.ts +16 -19
  114. package/src/lib/pipeline-factory.ts +71 -64
  115. package/src/lib/shader-factory.ts +57 -0
  116. package/src/model/model.ts +255 -146
  117. package/src/scenegraph/group-node.ts +14 -10
  118. package/src/scenegraph/model-node.ts +2 -2
  119. package/src/scenegraph/scenegraph-node.ts +2 -2
  120. package/src/shader-inputs.ts +19 -12
  121. package/src/transform/buffer-transform.ts +16 -8
  122. package/src/transform/texture-transform.ts +14 -15
  123. package/dist/animation/key-frames.js.map +0 -1
  124. package/dist/animation/timeline.js.map +0 -1
  125. package/dist/animation-loop/animation-loop-template.js.map +0 -1
  126. package/dist/animation-loop/animation-loop.js.map +0 -1
  127. package/dist/animation-loop/animation-props.js.map +0 -1
  128. package/dist/animation-loop/make-animation-loop.js.map +0 -1
  129. package/dist/debug/copy-texture-to-image.js.map +0 -1
  130. package/dist/debug/debug-framebuffer.js.map +0 -1
  131. package/dist/debug/debug-shader-layout.js.map +0 -1
  132. package/dist/debug/pixel-data-utils.js.map +0 -1
  133. package/dist/geometries/cone-geometry.js.map +0 -1
  134. package/dist/geometries/cube-geometry.js.map +0 -1
  135. package/dist/geometries/cylinder-geometry.js.map +0 -1
  136. package/dist/geometries/ico-sphere-geometry.js.map +0 -1
  137. package/dist/geometries/plane-geometry.js.map +0 -1
  138. package/dist/geometries/sphere-geometry.js.map +0 -1
  139. package/dist/geometries/truncated-cone-geometry.js.map +0 -1
  140. package/dist/geometry/geometry-table.js.map +0 -1
  141. package/dist/geometry/geometry-utils.js.map +0 -1
  142. package/dist/geometry/geometry.js.map +0 -1
  143. package/dist/geometry/gpu-geometry.js.map +0 -1
  144. package/dist/geometry/gpu-table.js.map +0 -1
  145. package/dist/index.js.map +0 -1
  146. package/dist/lib/clip-space.js.map +0 -1
  147. package/dist/lib/pipeline-factory.js.map +0 -1
  148. package/dist/model/model.js.map +0 -1
  149. package/dist/scenegraph/group-node.js.map +0 -1
  150. package/dist/scenegraph/model-node.js.map +0 -1
  151. package/dist/scenegraph/scenegraph-node.js.map +0 -1
  152. package/dist/shader-inputs.js.map +0 -1
  153. package/dist/transform/buffer-transform.js.map +0 -1
  154. package/dist/transform/texture-transform.js.map +0 -1
@@ -1,435 +1,659 @@
1
- import { Buffer, RenderPipeline, UniformStore, getTypedArrayFromDataType } from '@luma.gl/core';
2
- import { log, uid, deepEqual, splitUniformsAndBindings, isNumberArray } from '@luma.gl/core';
3
- import { getAttributeInfosFromLayouts } from '@luma.gl/core';
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { Buffer, Texture, TextureView, Sampler } from '@luma.gl/core';
5
+ import { RenderPipeline, UniformStore } from '@luma.gl/core';
6
+ import { log, uid, deepEqual, isObjectEmpty, splitUniformsAndBindings } from '@luma.gl/core';
7
+ import { getTypedArrayFromDataType, getAttributeInfosFromLayouts } from '@luma.gl/core';
4
8
  import { ShaderAssembler, getShaderLayoutFromWGSL } from '@luma.gl/shadertools';
5
- import { ShaderInputs } from "../shader-inputs.js";
6
9
  import { makeGPUGeometry } from "../geometry/gpu-geometry.js";
10
+ import { ShaderInputs } from "../shader-inputs.js";
7
11
  import { PipelineFactory } from "../lib/pipeline-factory.js";
12
+ import { ShaderFactory } from "../lib/shader-factory.js";
8
13
  import { getDebugTableForShaderLayout } from "../debug/debug-shader-layout.js";
9
14
  import { debugFramebuffer } from "../debug/debug-framebuffer.js";
10
15
  const LOG_DRAW_PRIORITY = 2;
11
16
  const LOG_DRAW_TIMEOUT = 10000;
17
+ /**
18
+ * v9 Model API
19
+ * A model
20
+ * - automatically reuses pipelines (programs) when possible
21
+ * - automatically rebuilds pipelines if necessary to accommodate changed settings
22
+ * shadertools integration
23
+ * - accepts modules and performs shader transpilation
24
+ */
12
25
  export class Model {
13
- constructor(device, props) {
14
- var _this$props$modules, _this$props$modules2, _this$shaderInputs;
15
- this.device = void 0;
16
- this.id = void 0;
17
- this.vs = void 0;
18
- this.fs = void 0;
19
- this.pipelineFactory = void 0;
20
- this.userData = {};
21
- this.parameters = void 0;
22
- this.topology = void 0;
23
- this.bufferLayout = void 0;
24
- this.vertexCount = void 0;
25
- this.instanceCount = 0;
26
- this.indexBuffer = null;
27
- this.bufferAttributes = {};
28
- this.constantAttributes = {};
29
- this.bindings = {};
30
- this.uniforms = {};
31
- this.vertexArray = void 0;
32
- this.transformFeedback = null;
33
- this.pipeline = void 0;
34
- this.shaderInputs = void 0;
35
- this._uniformStore = void 0;
36
- this._pipelineNeedsUpdate = 'newly created';
37
- this._attributeInfos = {};
38
- this._gpuGeometry = null;
39
- this._getModuleUniforms = void 0;
40
- this.props = void 0;
41
- this._lastLogTime = 0;
42
- this._logOpen = false;
43
- this._drawCount = 0;
44
- this.props = {
45
- ...Model.defaultProps,
46
- ...props
26
+ static defaultProps = {
27
+ ...RenderPipeline.defaultProps,
28
+ source: null,
29
+ vs: null,
30
+ fs: null,
31
+ id: 'unnamed',
32
+ handle: undefined,
33
+ userData: {},
34
+ defines: {},
35
+ modules: [],
36
+ moduleSettings: undefined,
37
+ geometry: null,
38
+ indexBuffer: null,
39
+ attributes: {},
40
+ constantAttributes: {},
41
+ varyings: [],
42
+ shaderInputs: undefined,
43
+ pipelineFactory: undefined,
44
+ shaderFactory: undefined,
45
+ transformFeedback: undefined,
46
+ shaderAssembler: ShaderAssembler.getDefaultShaderAssembler(),
47
+ debugShaders: undefined,
48
+ ignoreUnknownAttributes: undefined
47
49
  };
48
- props = this.props;
49
- this.id = props.id || uid('model');
50
- this.device = device;
51
- Object.assign(this.userData, props.userData);
52
- const moduleMap = Object.fromEntries(((_this$props$modules = this.props.modules) === null || _this$props$modules === void 0 ? void 0 : _this$props$modules.map(module => [module.name, module])) || []);
53
- this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
54
- const isWebGPU = this.device.info.type === 'webgpu';
55
- if (this.props.source) {
56
- if (isWebGPU) {
57
- var _this$props;
58
- (_this$props = this.props).shaderLayout || (_this$props.shaderLayout = getShaderLayoutFromWGSL(this.props.source));
59
- }
60
- this.props.fs = this.props.source;
61
- this.props.vs = this.props.source;
62
- }
63
- if (isWebGPU && typeof this.props.vs !== 'string') {
64
- var _this$props2;
65
- (_this$props2 = this.props).shaderLayout || (_this$props2.shaderLayout = getShaderLayoutFromWGSL(this.props.vs.wgsl));
66
- }
67
- const platformInfo = getPlatformInfo(device);
68
- const modules = (((_this$props$modules2 = this.props.modules) === null || _this$props$modules2 === void 0 ? void 0 : _this$props$modules2.length) > 0 ? this.props.modules : (_this$shaderInputs = this.shaderInputs) === null || _this$shaderInputs === void 0 ? void 0 : _this$shaderInputs.getModules()) || [];
69
- const {
70
- vs,
71
- fs,
72
- getUniforms
73
- } = this.props.shaderAssembler.assembleShaders({
74
- platformInfo,
75
- ...this.props,
76
- modules
77
- });
78
- this.vs = vs;
79
- this.fs = fs;
80
- this._getModuleUniforms = getUniforms;
81
- this.vertexCount = this.props.vertexCount;
82
- this.instanceCount = this.props.instanceCount;
83
- this.topology = this.props.topology;
84
- this.bufferLayout = this.props.bufferLayout;
85
- this.parameters = this.props.parameters;
86
- if (props.geometry) {
87
- this._gpuGeometry = this.setGeometry(props.geometry);
88
- }
89
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
90
- this.pipeline = this._updatePipeline();
91
- this.vertexArray = device.createVertexArray({
92
- renderPipeline: this.pipeline
93
- });
94
- if (this._gpuGeometry) {
95
- this._setGeometryAttributes(this._gpuGeometry);
96
- }
97
- if (props.vertexCount) {
98
- this.setVertexCount(props.vertexCount);
99
- }
100
- if (props.instanceCount) {
101
- this.setInstanceCount(props.instanceCount);
102
- }
103
- if (props.indices) {
104
- throw new Error('Model.props.indices removed. Use props.indexBuffer');
105
- }
106
- if (props.indexBuffer) {
107
- this.setIndexBuffer(props.indexBuffer);
108
- }
109
- if (props.attributes) {
110
- this.setAttributes(props.attributes);
111
- }
112
- if (props.constantAttributes) {
113
- this.setConstantAttributes(props.constantAttributes);
114
- }
115
- if (props.bindings) {
116
- this.setBindings(props.bindings);
117
- }
118
- if (props.uniforms) {
119
- this.setUniforms(props.uniforms);
120
- }
121
- if (props.moduleSettings) {
122
- log.warn('Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()')();
123
- this.updateModuleSettings(props.moduleSettings);
124
- }
125
- if (props.transformFeedback) {
126
- this.transformFeedback = props.transformFeedback;
127
- }
128
- Object.seal(this);
129
- }
130
- destroy() {
131
- this.pipelineFactory.release(this.pipeline);
132
- this._uniformStore.destroy();
133
- }
134
- predraw() {
135
- this.updateShaderInputs();
136
- }
137
- draw(renderPass) {
138
- this.predraw();
139
- try {
140
- this._logDrawCallStart();
141
- this.pipeline = this._updatePipeline();
142
- this.pipeline.setBindings(this.bindings);
143
- this.pipeline.setUniforms(this.uniforms);
144
- this.pipeline.draw({
145
- renderPass,
146
- vertexArray: this.vertexArray,
147
- vertexCount: this.vertexCount,
148
- instanceCount: this.instanceCount,
149
- transformFeedback: this.transformFeedback
150
- });
151
- } finally {
152
- this._logDrawCallEnd();
153
- }
154
- this._logFramebuffer(renderPass);
155
- }
156
- setGeometry(geometry) {
157
- const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
158
- this.setTopology(gpuGeometry.topology || 'triangle-list');
159
- this.bufferLayout = mergeBufferLayouts(this.bufferLayout, gpuGeometry.bufferLayout);
160
- if (this.vertexArray) {
161
- this._setGeometryAttributes(gpuGeometry);
162
- }
163
- return gpuGeometry;
164
- }
165
- _setGeometryAttributes(gpuGeometry) {
166
- const attributes = {
167
- ...gpuGeometry.attributes
168
- };
169
- for (const [attributeName] of Object.entries(attributes)) {
170
- if (!this.pipeline.shaderLayout.attributes.find(layout => layout.name === attributeName) && attributeName !== 'positions') {
171
- delete attributes[attributeName];
172
- }
173
- }
174
- this.vertexCount = gpuGeometry.vertexCount;
175
- this.setIndexBuffer(gpuGeometry.indices);
176
- this.setAttributes(gpuGeometry.attributes, 'ignore-unknown');
177
- this.setAttributes(attributes);
178
- }
179
- setTopology(topology) {
180
- if (topology !== this.topology) {
181
- this.topology = topology;
182
- this._setPipelineNeedsUpdate('topology');
183
- }
184
- }
185
- setBufferLayout(bufferLayout) {
186
- this.bufferLayout = this._gpuGeometry ? mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout) : bufferLayout;
187
- this._setPipelineNeedsUpdate('bufferLayout');
188
- this.pipeline = this._updatePipeline();
189
- this.vertexArray = this.device.createVertexArray({
190
- renderPipeline: this.pipeline
191
- });
192
- if (this._gpuGeometry) {
193
- this._setGeometryAttributes(this._gpuGeometry);
194
- }
195
- }
196
- setParameters(parameters) {
197
- if (!deepEqual(parameters, this.parameters, 2)) {
198
- this.parameters = parameters;
199
- this._setPipelineNeedsUpdate('parameters');
200
- }
201
- }
202
- setVertexCount(vertexCount) {
203
- this.vertexCount = vertexCount;
204
- }
205
- setInstanceCount(instanceCount) {
206
- this.instanceCount = instanceCount;
207
- }
208
- setShaderInputs(shaderInputs) {
209
- this.shaderInputs = shaderInputs;
210
- this._uniformStore = new UniformStore(this.shaderInputs.modules);
211
- for (const moduleName of Object.keys(this.shaderInputs.modules)) {
212
- const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
213
- this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
214
- }
215
- }
216
- setShaderModuleProps(props) {
217
- const uniforms = this._getModuleUniforms(props);
218
- const keys = Object.keys(uniforms).filter(k => {
219
- const uniform = uniforms[k];
220
- return !isNumberArray(uniform) && typeof uniform !== 'number' && typeof uniform !== 'boolean';
221
- });
222
- const bindings = {};
223
- for (const k of keys) {
224
- bindings[k] = uniforms[k];
225
- delete uniforms[k];
226
- }
227
- }
228
- updateShaderInputs() {
229
- this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
230
- }
231
- updateModuleSettings(props) {
232
- log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
233
- const {
234
- bindings,
235
- uniforms
236
- } = splitUniformsAndBindings(this._getModuleUniforms(props));
237
- Object.assign(this.bindings, bindings);
238
- Object.assign(this.uniforms, uniforms);
239
- }
240
- setBindings(bindings) {
241
- Object.assign(this.bindings, bindings);
242
- }
243
- setUniforms(uniforms) {
244
- this.pipeline.setUniforms(uniforms);
245
- Object.assign(this.uniforms, uniforms);
246
- }
247
- setIndexBuffer(indexBuffer) {
248
- this.vertexArray.setIndexBuffer(indexBuffer);
249
- }
250
- setTransformFeedback(transformFeedback) {
251
- this.transformFeedback = transformFeedback;
252
- }
253
- setAttributes(buffers, _option) {
254
- if (buffers.indices) {
255
- log.warn(`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`)();
256
- }
257
- for (const [bufferName, buffer] of Object.entries(buffers)) {
258
- const bufferLayout = this.bufferLayout.find(layout => getAttributeNames(layout).includes(bufferName));
259
- if (!bufferLayout) {
260
- log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
261
- continue;
262
- }
263
- const attributeNames = getAttributeNames(bufferLayout);
264
- let set = false;
265
- for (const attributeName of attributeNames) {
266
- const attributeInfo = this._attributeInfos[attributeName];
267
- if (attributeInfo) {
268
- this.vertexArray.setBuffer(attributeInfo.location, buffer);
269
- set = true;
270
- }
271
- }
272
- if (!set && _option !== 'ignore-unknown') {
273
- log.warn(`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`)();
274
- }
275
- }
276
- }
277
- setConstantAttributes(attributes) {
278
- for (const [attributeName, value] of Object.entries(attributes)) {
279
- const attributeInfo = this._attributeInfos[attributeName];
280
- if (attributeInfo) {
281
- this.vertexArray.setConstant(attributeInfo.location, value);
282
- } else {
283
- log.warn(`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`)();
284
- }
285
- }
286
- }
287
- _setPipelineNeedsUpdate(reason) {
288
- this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
289
- }
290
- _updatePipeline() {
291
- if (this._pipelineNeedsUpdate) {
292
- if (this.pipeline) {
293
- log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
294
- }
295
- this._pipelineNeedsUpdate = false;
296
- const vs = this.device.createShader({
297
- id: `${this.id}-vertex`,
298
- stage: 'vertex',
299
- source: this.vs
300
- });
301
- const fs = this.fs ? this.device.createShader({
302
- id: `${this.id}-fragment`,
303
- stage: 'fragment',
304
- source: this.fs
305
- }) : null;
306
- this.pipeline = this.device.createRenderPipeline({
307
- ...this.props,
308
- bufferLayout: this.bufferLayout,
309
- topology: this.topology,
310
- parameters: this.parameters,
311
- vs,
312
- fs
313
- });
314
- this._attributeInfos = getAttributeInfosFromLayouts(this.pipeline.shaderLayout, this.bufferLayout);
50
+ device;
51
+ id;
52
+ source;
53
+ vs;
54
+ fs;
55
+ pipelineFactory;
56
+ shaderFactory;
57
+ userData = {};
58
+ // Fixed properties (change can trigger pipeline rebuild)
59
+ /** The render pipeline GPU parameters, depth testing etc */
60
+ parameters;
61
+ /** The primitive topology */
62
+ topology;
63
+ /** Buffer layout */
64
+ bufferLayout;
65
+ // Dynamic properties
66
+ /** Vertex count */
67
+ vertexCount;
68
+ /** instance count */
69
+ instanceCount = 0;
70
+ /** Index buffer */
71
+ indexBuffer = null;
72
+ /** Buffer-valued attributes */
73
+ bufferAttributes = {};
74
+ /** Constant-valued attributes */
75
+ constantAttributes = {};
76
+ /** Bindings (textures, samplers, uniform buffers) */
77
+ bindings = {};
78
+ /** Sets uniforms @deprecated Use uniform buffers and setBindings() for portability*/
79
+ uniforms = {};
80
+ /**
81
+ * VertexArray
82
+ * @note not implemented: if bufferLayout is updated, vertex array has to be rebuilt!
83
+ * @todo - allow application to define multiple vertex arrays?
84
+ * */
85
+ vertexArray;
86
+ /** TransformFeedback, WebGL 2 only. */
87
+ transformFeedback = null;
88
+ /** The underlying GPU "program". @note May be recreated if parameters change */
89
+ pipeline;
90
+ /** ShaderInputs instance */
91
+ shaderInputs;
92
+ _uniformStore;
93
+ _attributeInfos = {};
94
+ _gpuGeometry = null;
95
+ _getModuleUniforms;
96
+ props;
97
+ _pipelineNeedsUpdate = 'newly created';
98
+ _needsRedraw = 'initializing';
99
+ _destroyed = false;
100
+ /** "Time" of last draw. Monotonically increasing timestamp */
101
+ _lastDrawTimestamp = -1;
102
+ constructor(device, props) {
103
+ this.props = { ...Model.defaultProps, ...props };
104
+ props = this.props;
105
+ this.id = props.id || uid('model');
106
+ this.device = device;
107
+ Object.assign(this.userData, props.userData);
108
+ // Setup shader module inputs
109
+ const moduleMap = Object.fromEntries(this.props.modules?.map(module => [module.name, module]) || []);
110
+ this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
111
+ // Setup shader assembler
112
+ const platformInfo = getPlatformInfo(device);
113
+ // Extract modules from shader inputs if not supplied
114
+ const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
115
+ const isWebGPU = this.device.type === 'webgpu';
116
+ // WebGPU
117
+ // TODO - hack to support unified WGSL shader
118
+ // TODO - this is wrong, compile a single shader
119
+ if (isWebGPU && this.props.source) {
120
+ // WGSL
121
+ this.props.shaderLayout ||= getShaderLayoutFromWGSL(this.props.source);
122
+ const { source, getUniforms } = this.props.shaderAssembler.assembleShader({
123
+ platformInfo,
124
+ ...this.props,
125
+ modules
126
+ });
127
+ this.source = source;
128
+ this._getModuleUniforms = getUniforms;
129
+ }
130
+ else {
131
+ // GLSL
132
+ const { vs, fs, getUniforms } = this.props.shaderAssembler.assembleShaderPair({
133
+ platformInfo,
134
+ ...this.props,
135
+ modules
136
+ });
137
+ this.vs = vs;
138
+ this.fs = fs;
139
+ this._getModuleUniforms = getUniforms;
140
+ }
141
+ this.vertexCount = this.props.vertexCount;
142
+ this.instanceCount = this.props.instanceCount;
143
+ this.topology = this.props.topology;
144
+ this.bufferLayout = this.props.bufferLayout;
145
+ this.parameters = this.props.parameters;
146
+ // Geometry, if provided, sets topology and vertex cound
147
+ if (props.geometry) {
148
+ this.setGeometry(props.geometry);
149
+ }
150
+ this.pipelineFactory =
151
+ props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
152
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
153
+ // Create the pipeline
154
+ // @note order is important
155
+ this.pipeline = this._updatePipeline();
156
+ this.vertexArray = device.createVertexArray({
157
+ renderPipeline: this.pipeline
158
+ });
159
+ // Now we can apply geometry attributes
160
+ if (this._gpuGeometry) {
161
+ this._setGeometryAttributes(this._gpuGeometry);
162
+ }
163
+ // Apply any dynamic settings that will not trigger pipeline change
164
+ if (props.vertexCount) {
165
+ this.setVertexCount(props.vertexCount);
166
+ }
167
+ if (props.instanceCount) {
168
+ this.setInstanceCount(props.instanceCount);
169
+ }
170
+ // @ts-expect-error
171
+ if (props.indices) {
172
+ throw new Error('Model.props.indices removed. Use props.indexBuffer');
173
+ }
174
+ if (props.indexBuffer) {
175
+ this.setIndexBuffer(props.indexBuffer);
176
+ }
177
+ if (props.attributes) {
178
+ this.setAttributes(props.attributes, {
179
+ ignoreUnknownAttributes: props.ignoreUnknownAttributes
180
+ });
181
+ }
182
+ if (props.constantAttributes) {
183
+ this.setConstantAttributes(props.constantAttributes);
184
+ }
185
+ if (props.bindings) {
186
+ this.setBindings(props.bindings);
187
+ }
188
+ if (props.uniforms) {
189
+ this.setUniforms(props.uniforms);
190
+ }
191
+ if (props.moduleSettings) {
192
+ log.warn('Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()')();
193
+ this.updateModuleSettings(props.moduleSettings);
194
+ }
195
+ if (props.transformFeedback) {
196
+ this.transformFeedback = props.transformFeedback;
197
+ }
198
+ // Catch any access to non-standard props
199
+ Object.seal(this);
200
+ }
201
+ destroy() {
202
+ if (this._destroyed)
203
+ return;
204
+ this.pipelineFactory.release(this.pipeline);
205
+ this.shaderFactory.release(this.pipeline.vs);
206
+ if (this.pipeline.fs) {
207
+ this.shaderFactory.release(this.pipeline.fs);
208
+ }
209
+ this._uniformStore.destroy();
210
+ // TODO - mark resource as managed and destroyIfManaged() ?
211
+ this._gpuGeometry?.destroy();
212
+ this._destroyed = true;
213
+ }
214
+ // Draw call
215
+ /** Query redraw status. Clears the status. */
216
+ needsRedraw() {
217
+ // Catch any writes to already bound resources
218
+ if (this._getBindingsUpdateTimestamp() > this._lastDrawTimestamp) {
219
+ this.setNeedsRedraw('contents of bound textures or buffers updated');
220
+ }
221
+ const needsRedraw = this._needsRedraw;
222
+ this._needsRedraw = false;
223
+ return needsRedraw;
224
+ }
225
+ /** Mark the model as needing a redraw */
226
+ setNeedsRedraw(reason) {
227
+ this._needsRedraw ||= reason;
228
+ }
229
+ predraw() {
230
+ // Update uniform buffers if needed
231
+ this.updateShaderInputs();
232
+ // Check if the pipeline is invalidated
233
+ this.pipeline = this._updatePipeline();
234
+ }
235
+ draw(renderPass) {
236
+ this.predraw();
237
+ let drawSuccess;
238
+ try {
239
+ this._logDrawCallStart();
240
+ // Update the pipeline if invalidated
241
+ // TODO - inside RenderPass is likely the worst place to do this from performance perspective.
242
+ // Application can call Model.predraw() to avoid this.
243
+ this.pipeline = this._updatePipeline();
244
+ // Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
245
+ // Any caching needs to be done inside the pipeline functions
246
+ this.pipeline.setBindings(this.bindings);
247
+ if (!isObjectEmpty(this.uniforms)) {
248
+ this.pipeline.setUniformsWebGL(this.uniforms);
249
+ }
250
+ const { indexBuffer } = this.vertexArray;
251
+ const indexCount = indexBuffer
252
+ ? indexBuffer.byteLength / (indexBuffer.indexType === 'uint32' ? 4 : 2)
253
+ : undefined;
254
+ drawSuccess = this.pipeline.draw({
255
+ renderPass,
256
+ vertexArray: this.vertexArray,
257
+ vertexCount: this.vertexCount,
258
+ instanceCount: this.instanceCount,
259
+ indexCount,
260
+ transformFeedback: this.transformFeedback
261
+ });
262
+ }
263
+ finally {
264
+ this._logDrawCallEnd();
265
+ }
266
+ this._logFramebuffer(renderPass);
267
+ // Update needsRedraw flag
268
+ if (drawSuccess) {
269
+ this._lastDrawTimestamp = this.device.timestamp;
270
+ this._needsRedraw = false;
271
+ }
272
+ else {
273
+ this._needsRedraw = 'waiting for resource initialization';
274
+ }
275
+ return drawSuccess;
276
+ }
277
+ // Update fixed fields (can trigger pipeline rebuild)
278
+ /**
279
+ * Updates the optional geometry
280
+ * Geometry, set topology and bufferLayout
281
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
282
+ */
283
+ setGeometry(geometry) {
284
+ this._gpuGeometry?.destroy();
285
+ const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
286
+ if (gpuGeometry) {
287
+ this.setTopology(gpuGeometry.topology || 'triangle-list');
288
+ this.bufferLayout = mergeBufferLayouts(gpuGeometry.bufferLayout, this.bufferLayout);
289
+ if (this.vertexArray) {
290
+ this._setGeometryAttributes(gpuGeometry);
291
+ }
292
+ }
293
+ this._gpuGeometry = gpuGeometry;
294
+ }
295
+ /**
296
+ * Updates the primitive topology ('triangle-list', 'triangle-strip' etc).
297
+ * @note Triggers a pipeline rebuild / pipeline cache fetch on WebGPU
298
+ */
299
+ setTopology(topology) {
300
+ if (topology !== this.topology) {
301
+ this.topology = topology;
302
+ this._setPipelineNeedsUpdate('topology');
303
+ }
315
304
  }
316
- return this.pipeline;
317
- }
318
- _logDrawCallStart() {
319
- const logDrawTimeout = log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
320
- if (log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
321
- return;
305
+ /**
306
+ * Updates the buffer layout.
307
+ * @note Triggers a pipeline rebuild / pipeline cache fetch
308
+ */
309
+ setBufferLayout(bufferLayout) {
310
+ this.bufferLayout = this._gpuGeometry
311
+ ? mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout)
312
+ : bufferLayout;
313
+ this._setPipelineNeedsUpdate('bufferLayout');
314
+ // Recreate the pipeline
315
+ this.pipeline = this._updatePipeline();
316
+ // vertex array needs to be updated if we update buffer layout,
317
+ // but not if we update parameters
318
+ this.vertexArray = this.device.createVertexArray({
319
+ renderPipeline: this.pipeline
320
+ });
321
+ // Reapply geometry attributes to the new vertex array
322
+ if (this._gpuGeometry) {
323
+ this._setGeometryAttributes(this._gpuGeometry);
324
+ }
322
325
  }
323
- this._lastLogTime = Date.now();
324
- this._logOpen = true;
325
- log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, {
326
- collapsed: log.level <= 2
327
- })();
328
- }
329
- _logDrawCallEnd() {
330
- if (this._logOpen) {
331
- const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
332
- log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
333
- const uniformTable = this.shaderInputs.getDebugTable();
334
- for (const [name, value] of Object.entries(this.uniforms)) {
335
- uniformTable[name] = {
336
- value
337
- };
338
- }
339
- log.table(LOG_DRAW_PRIORITY, uniformTable)();
340
- const attributeTable = this._getAttributeDebugTable();
341
- log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
342
- log.table(LOG_DRAW_PRIORITY, attributeTable)();
343
- log.groupEnd(LOG_DRAW_PRIORITY)();
344
- this._logOpen = false;
326
+ /**
327
+ * Set GPU parameters.
328
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch.
329
+ * @param parameters
330
+ */
331
+ setParameters(parameters) {
332
+ if (!deepEqual(parameters, this.parameters, 2)) {
333
+ this.parameters = parameters;
334
+ this._setPipelineNeedsUpdate('parameters');
335
+ }
345
336
  }
346
- }
347
- _logFramebuffer(renderPass) {
348
- const debugFramebuffers = log.get('framebuffer');
349
- this._drawCount++;
350
- if (!debugFramebuffers || this._drawCount++ > 3 && this._drawCount % 60) {
351
- return;
337
+ // Update dynamic fields
338
+ /**
339
+ * Updates the vertex count (used in draw calls)
340
+ * @note Any attributes with stepMode=vertex need to be at least this big
341
+ */
342
+ setVertexCount(vertexCount) {
343
+ this.vertexCount = vertexCount;
344
+ this.setNeedsRedraw('vertexCount');
345
+ }
346
+ /**
347
+ * Updates the instance count (used in draw calls)
348
+ * @note Any attributes with stepMode=instance need to be at least this big
349
+ */
350
+ setInstanceCount(instanceCount) {
351
+ this.instanceCount = instanceCount;
352
+ this.setNeedsRedraw('instanceCount');
353
+ }
354
+ setShaderInputs(shaderInputs) {
355
+ this.shaderInputs = shaderInputs;
356
+ this._uniformStore = new UniformStore(this.shaderInputs.modules);
357
+ // Create uniform buffer bindings for all modules
358
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
359
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
360
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
361
+ }
362
+ this.setNeedsRedraw('shaderInputs');
363
+ }
364
+ updateShaderInputs() {
365
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
366
+ // TODO - this is already tracked through buffer/texture update times?
367
+ this.setNeedsRedraw('shaderInputs');
368
+ }
369
+ /**
370
+ * Sets bindings (textures, samplers, uniform buffers)
371
+ */
372
+ setBindings(bindings) {
373
+ Object.assign(this.bindings, bindings);
374
+ this.setNeedsRedraw('bindings');
375
+ }
376
+ /**
377
+ * Updates optional transform feedback. WebGL only.
378
+ */
379
+ setTransformFeedback(transformFeedback) {
380
+ this.transformFeedback = transformFeedback;
381
+ this.setNeedsRedraw('transformFeedback');
382
+ }
383
+ /**
384
+ * Sets the index buffer
385
+ * @todo - how to unset it if we change geometry?
386
+ */
387
+ setIndexBuffer(indexBuffer) {
388
+ this.vertexArray.setIndexBuffer(indexBuffer);
389
+ this.setNeedsRedraw('indexBuffer');
390
+ }
391
+ /**
392
+ * Sets attributes (buffers)
393
+ * @note Overrides any attributes previously set with the same name
394
+ */
395
+ setAttributes(buffers, options) {
396
+ if (buffers.indices) {
397
+ log.warn(`Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`)();
398
+ }
399
+ for (const [bufferName, buffer] of Object.entries(buffers)) {
400
+ const bufferLayout = this.bufferLayout.find(layout => getAttributeNames(layout).includes(bufferName));
401
+ if (!bufferLayout) {
402
+ log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
403
+ continue; // eslint-disable-line no-continue
404
+ }
405
+ // For an interleaved attribute we may need to set multiple attributes
406
+ const attributeNames = getAttributeNames(bufferLayout);
407
+ let set = false;
408
+ for (const attributeName of attributeNames) {
409
+ const attributeInfo = this._attributeInfos[attributeName];
410
+ if (attributeInfo) {
411
+ this.vertexArray.setBuffer(attributeInfo.location, buffer);
412
+ set = true;
413
+ }
414
+ }
415
+ if (!set && (options?.ignoreUnknownAttributes || this.props.ignoreUnknownAttributes)) {
416
+ log.warn(`Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`)();
417
+ }
418
+ }
419
+ this.setNeedsRedraw('attributes');
420
+ }
421
+ /**
422
+ * Sets constant attributes
423
+ * @note Overrides any attributes previously set with the same name
424
+ * Constant attributes are only supported in WebGL, not in WebGPU
425
+ * Any attribute that is disabled in the current vertex array object
426
+ * is read from the context's global constant value for that attribute location.
427
+ * @param constantAttributes
428
+ */
429
+ setConstantAttributes(attributes) {
430
+ for (const [attributeName, value] of Object.entries(attributes)) {
431
+ const attributeInfo = this._attributeInfos[attributeName];
432
+ if (attributeInfo) {
433
+ this.vertexArray.setConstantWebGL(attributeInfo.location, value);
434
+ }
435
+ else {
436
+ log.warn(`Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`)();
437
+ }
438
+ }
439
+ this.setNeedsRedraw('constants');
440
+ }
441
+ // DEPRECATED METHODS
442
+ /**
443
+ * Sets individual uniforms
444
+ * @deprecated WebGL only, use uniform buffers for portability
445
+ * @param uniforms
446
+ */
447
+ setUniforms(uniforms) {
448
+ if (!isObjectEmpty(uniforms)) {
449
+ this.pipeline.setUniformsWebGL(uniforms);
450
+ Object.assign(this.uniforms, uniforms);
451
+ }
452
+ this.setNeedsRedraw('uniforms');
453
+ }
454
+ /**
455
+ * @deprecated Updates shader module settings (which results in uniforms being set)
456
+ */
457
+ updateModuleSettings(props) {
458
+ log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
459
+ const { bindings, uniforms } = splitUniformsAndBindings(this._getModuleUniforms(props));
460
+ Object.assign(this.bindings, bindings);
461
+ Object.assign(this.uniforms, uniforms);
462
+ this.setNeedsRedraw('moduleSettings');
463
+ }
464
+ // Internal methods
465
+ /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
466
+ _getBindingsUpdateTimestamp() {
467
+ let timestamp = 0;
468
+ for (const binding of Object.values(this.bindings)) {
469
+ if (binding instanceof TextureView) {
470
+ timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
471
+ }
472
+ else if (binding instanceof Buffer || binding instanceof Texture) {
473
+ timestamp = Math.max(timestamp, binding.updateTimestamp);
474
+ }
475
+ else if (!(binding instanceof Sampler)) {
476
+ timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
477
+ }
478
+ }
479
+ return timestamp;
480
+ }
481
+ /**
482
+ * Updates the optional geometry attributes
483
+ * Geometry, sets several attributes, indexBuffer, and also vertex count
484
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
485
+ */
486
+ _setGeometryAttributes(gpuGeometry) {
487
+ // Filter geometry attribute so that we don't issue warnings for unused attributes
488
+ const attributes = { ...gpuGeometry.attributes };
489
+ for (const [attributeName] of Object.entries(attributes)) {
490
+ if (!this.pipeline.shaderLayout.attributes.find(layout => layout.name === attributeName) &&
491
+ attributeName !== 'positions') {
492
+ delete attributes[attributeName];
493
+ }
494
+ }
495
+ // TODO - delete previous geometry?
496
+ this.vertexCount = gpuGeometry.vertexCount;
497
+ this.setIndexBuffer(gpuGeometry.indices);
498
+ this.setAttributes(gpuGeometry.attributes, { ignoreUnknownAttributes: true });
499
+ this.setAttributes(attributes, { ignoreUnknownAttributes: this.props.ignoreUnknownAttributes });
500
+ this.setNeedsRedraw('geometry attributes');
501
+ }
502
+ /** Mark pipeline as needing update */
503
+ _setPipelineNeedsUpdate(reason) {
504
+ this._pipelineNeedsUpdate ||= reason;
505
+ this.setNeedsRedraw(reason);
506
+ }
507
+ /** Update pipeline if needed */
508
+ _updatePipeline() {
509
+ if (this._pipelineNeedsUpdate) {
510
+ let prevShaderVs = null;
511
+ let prevShaderFs = null;
512
+ if (this.pipeline) {
513
+ log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
514
+ prevShaderVs = this.pipeline.vs;
515
+ prevShaderFs = this.pipeline.fs;
516
+ }
517
+ this._pipelineNeedsUpdate = false;
518
+ const vs = this.shaderFactory.createShader({
519
+ id: `${this.id}-vertex`,
520
+ stage: 'vertex',
521
+ source: this.source || this.vs,
522
+ debug: this.props.debugShaders
523
+ });
524
+ let fs = null;
525
+ if (this.source) {
526
+ fs = vs;
527
+ }
528
+ else if (this.fs) {
529
+ fs = this.shaderFactory.createShader({
530
+ id: `${this.id}-fragment`,
531
+ stage: 'fragment',
532
+ source: this.source || this.fs,
533
+ debug: this.props.debugShaders
534
+ });
535
+ }
536
+ this.pipeline = this.pipelineFactory.createRenderPipeline({
537
+ ...this.props,
538
+ bufferLayout: this.bufferLayout,
539
+ topology: this.topology,
540
+ parameters: this.parameters,
541
+ vs,
542
+ fs
543
+ });
544
+ this._attributeInfos = getAttributeInfosFromLayouts(this.pipeline.shaderLayout, this.bufferLayout);
545
+ if (prevShaderVs)
546
+ this.shaderFactory.release(prevShaderVs);
547
+ if (prevShaderFs)
548
+ this.shaderFactory.release(prevShaderFs);
549
+ }
550
+ return this.pipeline;
551
+ }
552
+ /** Throttle draw call logging */
553
+ _lastLogTime = 0;
554
+ _logOpen = false;
555
+ _logDrawCallStart() {
556
+ // IF level is 4 or higher, log every frame.
557
+ const logDrawTimeout = log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
558
+ if (log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
559
+ return;
560
+ }
561
+ this._lastLogTime = Date.now();
562
+ this._logOpen = true;
563
+ log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: log.level <= 2 })();
564
+ }
565
+ _logDrawCallEnd() {
566
+ if (this._logOpen) {
567
+ const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
568
+ // log.table(logLevel, attributeTable)();
569
+ // log.table(logLevel, uniformTable)();
570
+ log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
571
+ const uniformTable = this.shaderInputs.getDebugTable();
572
+ // Add any global uniforms
573
+ for (const [name, value] of Object.entries(this.uniforms)) {
574
+ uniformTable[name] = { value };
575
+ }
576
+ log.table(LOG_DRAW_PRIORITY, uniformTable)();
577
+ const attributeTable = this._getAttributeDebugTable();
578
+ log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
579
+ log.table(LOG_DRAW_PRIORITY, attributeTable)();
580
+ log.groupEnd(LOG_DRAW_PRIORITY)();
581
+ this._logOpen = false;
582
+ }
352
583
  }
353
- const framebuffer = renderPass.props.framebuffer;
354
- if (framebuffer) {
355
- debugFramebuffer(framebuffer, {
356
- id: framebuffer.id,
357
- minimap: true
358
- });
584
+ _drawCount = 0;
585
+ _logFramebuffer(renderPass) {
586
+ const debugFramebuffers = log.get('framebuffer');
587
+ this._drawCount++;
588
+ // Update first 3 frames and then every 60 frames
589
+ if (!debugFramebuffers || (this._drawCount++ > 3 && this._drawCount % 60)) {
590
+ return;
591
+ }
592
+ // TODO - display framebuffer output in debug window
593
+ const framebuffer = renderPass.props.framebuffer;
594
+ if (framebuffer) {
595
+ debugFramebuffer(framebuffer, { id: framebuffer.id, minimap: true });
596
+ // log.image({logLevel: LOG_DRAW_PRIORITY, message: `${framebuffer.id} %c sup?`, image})();
597
+ }
359
598
  }
360
- }
361
- _getAttributeDebugTable() {
362
- const table = {};
363
- for (const [name, attributeInfo] of Object.entries(this._attributeInfos)) {
364
- table[attributeInfo.location] = {
365
- name,
366
- type: attributeInfo.shaderType,
367
- values: this._getBufferOrConstantValues(this.vertexArray.attributes[attributeInfo.location], attributeInfo.bufferDataType)
368
- };
599
+ _getAttributeDebugTable() {
600
+ const table = {};
601
+ for (const [name, attributeInfo] of Object.entries(this._attributeInfos)) {
602
+ table[attributeInfo.location] = {
603
+ name,
604
+ type: attributeInfo.shaderType,
605
+ values: this._getBufferOrConstantValues(this.vertexArray.attributes[attributeInfo.location], attributeInfo.bufferDataType)
606
+ };
607
+ }
608
+ if (this.vertexArray.indexBuffer) {
609
+ const { indexBuffer } = this.vertexArray;
610
+ const values = indexBuffer.indexType === 'uint32'
611
+ ? new Uint32Array(indexBuffer.debugData)
612
+ : new Uint16Array(indexBuffer.debugData);
613
+ table.indices = {
614
+ name: 'indices',
615
+ type: indexBuffer.indexType,
616
+ values: values.toString()
617
+ };
618
+ }
619
+ return table;
369
620
  }
370
- if (this.vertexArray.indexBuffer) {
371
- const {
372
- indexBuffer
373
- } = this.vertexArray;
374
- const values = indexBuffer.indexType === 'uint32' ? new Uint32Array(indexBuffer.debugData) : new Uint16Array(indexBuffer.debugData);
375
- table.indices = {
376
- name: 'indices',
377
- type: indexBuffer.indexType,
378
- values: values.toString()
379
- };
621
+ // TODO - fix typing of luma data types
622
+ _getBufferOrConstantValues(attribute, dataType) {
623
+ const TypedArrayConstructor = getTypedArrayFromDataType(dataType);
624
+ const typedArray = attribute instanceof Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
625
+ return typedArray.toString();
380
626
  }
381
- return table;
382
- }
383
- _getBufferOrConstantValues(attribute, dataType) {
384
- const TypedArrayConstructor = getTypedArrayFromDataType(dataType);
385
- const typedArray = attribute instanceof Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
386
- return typedArray.toString();
387
- }
388
627
  }
389
- Model.defaultProps = {
390
- ...RenderPipeline.defaultProps,
391
- source: null,
392
- vs: null,
393
- fs: null,
394
- id: 'unnamed',
395
- handle: undefined,
396
- userData: {},
397
- defines: {},
398
- modules: [],
399
- moduleSettings: undefined,
400
- geometry: null,
401
- indexBuffer: null,
402
- attributes: {},
403
- constantAttributes: {},
404
- varyings: [],
405
- shaderInputs: undefined,
406
- pipelineFactory: undefined,
407
- transformFeedback: undefined,
408
- shaderAssembler: ShaderAssembler.getDefaultShaderAssembler()
409
- };
628
+ // HELPERS
629
+ /** TODO - move to core, document add tests */
410
630
  function mergeBufferLayouts(layouts1, layouts2) {
411
- const layouts = [...layouts1];
412
- for (const attribute of layouts2) {
413
- const index = layouts.findIndex(attribute2 => attribute2.name === attribute.name);
414
- if (index < 0) {
415
- layouts.push(attribute);
416
- } else {
417
- layouts[index] = attribute;
631
+ const layouts = [...layouts1];
632
+ for (const attribute of layouts2) {
633
+ const index = layouts.findIndex(attribute2 => attribute2.name === attribute.name);
634
+ if (index < 0) {
635
+ layouts.push(attribute);
636
+ }
637
+ else {
638
+ layouts[index] = attribute;
639
+ }
418
640
  }
419
- }
420
- return layouts;
641
+ return layouts;
421
642
  }
643
+ /** Create a shadertools platform info from the Device */
422
644
  export function getPlatformInfo(device) {
423
- return {
424
- type: device.info.type,
425
- shaderLanguage: device.info.shadingLanguage,
426
- shaderLanguageVersion: device.info.shadingLanguageVersion,
427
- gpu: device.info.gpu,
428
- features: device.features
429
- };
645
+ return {
646
+ type: device.type,
647
+ shaderLanguage: device.info.shadingLanguage,
648
+ shaderLanguageVersion: device.info.shadingLanguageVersion,
649
+ gpu: device.info.gpu,
650
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
651
+ features: device.features
652
+ };
430
653
  }
654
+ /** Get attribute names from a BufferLayout */
431
655
  function getAttributeNames(bufferLayout) {
432
- var _bufferLayout$attribu;
433
- return bufferLayout.attributes ? (_bufferLayout$attribu = bufferLayout.attributes) === null || _bufferLayout$attribu === void 0 ? void 0 : _bufferLayout$attribu.map(layout => layout.attribute) : [bufferLayout.name];
656
+ return bufferLayout.attributes
657
+ ? bufferLayout.attributes?.map(layout => layout.attribute)
658
+ : [bufferLayout.name];
434
659
  }
435
- //# sourceMappingURL=model.js.map