@luma.gl/webgl 9.3.0-alpha.2 → 9.3.0-alpha.6

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 (106) hide show
  1. package/dist/adapter/converters/webgl-texture-table.d.ts +7 -1
  2. package/dist/adapter/converters/webgl-texture-table.d.ts.map +1 -1
  3. package/dist/adapter/converters/webgl-texture-table.js +121 -43
  4. package/dist/adapter/converters/webgl-texture-table.js.map +1 -1
  5. package/dist/adapter/device-helpers/webgl-device-features.d.ts.map +1 -1
  6. package/dist/adapter/device-helpers/webgl-device-features.js +1 -2
  7. package/dist/adapter/device-helpers/webgl-device-features.js.map +1 -1
  8. package/dist/adapter/device-helpers/webgl-device-info.js +5 -0
  9. package/dist/adapter/device-helpers/webgl-device-info.js.map +1 -1
  10. package/dist/adapter/helpers/get-shader-layout-from-glsl.js +23 -21
  11. package/dist/adapter/helpers/get-shader-layout-from-glsl.js.map +1 -1
  12. package/dist/adapter/helpers/parse-shader-compiler-log.d.ts +1 -1
  13. package/dist/adapter/helpers/parse-shader-compiler-log.d.ts.map +1 -1
  14. package/dist/adapter/helpers/parse-shader-compiler-log.js +20 -0
  15. package/dist/adapter/helpers/parse-shader-compiler-log.js.map +1 -1
  16. package/dist/adapter/resources/webgl-buffer.d.ts.map +1 -1
  17. package/dist/adapter/resources/webgl-buffer.js +19 -4
  18. package/dist/adapter/resources/webgl-buffer.js.map +1 -1
  19. package/dist/adapter/resources/webgl-command-buffer.d.ts +3 -4
  20. package/dist/adapter/resources/webgl-command-buffer.d.ts.map +1 -1
  21. package/dist/adapter/resources/webgl-command-buffer.js +11 -7
  22. package/dist/adapter/resources/webgl-command-buffer.js.map +1 -1
  23. package/dist/adapter/resources/webgl-command-encoder.d.ts +5 -4
  24. package/dist/adapter/resources/webgl-command-encoder.d.ts.map +1 -1
  25. package/dist/adapter/resources/webgl-command-encoder.js +20 -7
  26. package/dist/adapter/resources/webgl-command-encoder.js.map +1 -1
  27. package/dist/adapter/resources/webgl-query-set.d.ts +29 -31
  28. package/dist/adapter/resources/webgl-query-set.d.ts.map +1 -1
  29. package/dist/adapter/resources/webgl-query-set.js +193 -97
  30. package/dist/adapter/resources/webgl-query-set.js.map +1 -1
  31. package/dist/adapter/resources/webgl-render-pass.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgl-render-pass.js +17 -0
  33. package/dist/adapter/resources/webgl-render-pass.js.map +1 -1
  34. package/dist/adapter/resources/webgl-render-pipeline.d.ts +13 -19
  35. package/dist/adapter/resources/webgl-render-pipeline.d.ts.map +1 -1
  36. package/dist/adapter/resources/webgl-render-pipeline.js +36 -154
  37. package/dist/adapter/resources/webgl-render-pipeline.js.map +1 -1
  38. package/dist/adapter/resources/webgl-shared-render-pipeline.d.ts +24 -0
  39. package/dist/adapter/resources/webgl-shared-render-pipeline.d.ts.map +1 -0
  40. package/dist/adapter/resources/webgl-shared-render-pipeline.js +152 -0
  41. package/dist/adapter/resources/webgl-shared-render-pipeline.js.map +1 -0
  42. package/dist/adapter/resources/webgl-texture.d.ts +23 -4
  43. package/dist/adapter/resources/webgl-texture.d.ts.map +1 -1
  44. package/dist/adapter/resources/webgl-texture.js +203 -100
  45. package/dist/adapter/resources/webgl-texture.js.map +1 -1
  46. package/dist/adapter/resources/webgl-transform-feedback.js +5 -5
  47. package/dist/adapter/resources/webgl-transform-feedback.js.map +1 -1
  48. package/dist/adapter/webgl-adapter.d.ts.map +1 -1
  49. package/dist/adapter/webgl-adapter.js +3 -4
  50. package/dist/adapter/webgl-adapter.js.map +1 -1
  51. package/dist/adapter/webgl-device.d.ts +6 -3
  52. package/dist/adapter/webgl-device.d.ts.map +1 -1
  53. package/dist/adapter/webgl-device.js +56 -14
  54. package/dist/adapter/webgl-device.js.map +1 -1
  55. package/dist/adapter/webgl-presentation-context.d.ts +21 -0
  56. package/dist/adapter/webgl-presentation-context.d.ts.map +1 -0
  57. package/dist/adapter/webgl-presentation-context.js +64 -0
  58. package/dist/adapter/webgl-presentation-context.js.map +1 -0
  59. package/dist/context/debug/spector.d.ts.map +1 -1
  60. package/dist/context/debug/spector.js +4 -4
  61. package/dist/context/debug/spector.js.map +1 -1
  62. package/dist/context/debug/webgl-developer-tools.js +2 -0
  63. package/dist/context/debug/webgl-developer-tools.js.map +1 -1
  64. package/dist/context/helpers/create-browser-context.d.ts.map +1 -1
  65. package/dist/context/helpers/create-browser-context.js +6 -8
  66. package/dist/context/helpers/create-browser-context.js.map +1 -1
  67. package/dist/context/helpers/webgl-context-data.d.ts +5 -1
  68. package/dist/context/helpers/webgl-context-data.d.ts.map +1 -1
  69. package/dist/context/helpers/webgl-context-data.js +9 -10
  70. package/dist/context/helpers/webgl-context-data.js.map +1 -1
  71. package/dist/context/parameters/unified-parameter-api.d.ts +1 -1
  72. package/dist/context/parameters/unified-parameter-api.js +2 -2
  73. package/dist/context/parameters/unified-parameter-api.js.map +1 -1
  74. package/dist/context/state-tracker/webgl-state-tracker.js +2 -2
  75. package/dist/context/state-tracker/webgl-state-tracker.js.map +1 -1
  76. package/dist/dist.dev.js +1427 -828
  77. package/dist/dist.min.js +2 -2
  78. package/dist/index.cjs +1325 -811
  79. package/dist/index.cjs.map +4 -4
  80. package/dist/utils/fill-array.js +1 -1
  81. package/dist/utils/fill-array.js.map +1 -1
  82. package/package.json +4 -4
  83. package/src/adapter/converters/webgl-texture-table.ts +159 -47
  84. package/src/adapter/device-helpers/webgl-device-features.ts +1 -2
  85. package/src/adapter/device-helpers/webgl-device-info.ts +6 -0
  86. package/src/adapter/helpers/get-shader-layout-from-glsl.ts +25 -24
  87. package/src/adapter/helpers/parse-shader-compiler-log.ts +23 -1
  88. package/src/adapter/resources/webgl-buffer.ts +16 -4
  89. package/src/adapter/resources/webgl-command-buffer.ts +21 -24
  90. package/src/adapter/resources/webgl-command-encoder.ts +22 -7
  91. package/src/adapter/resources/webgl-query-set.ts +229 -102
  92. package/src/adapter/resources/webgl-render-pass.ts +19 -0
  93. package/src/adapter/resources/webgl-render-pipeline.ts +46 -181
  94. package/src/adapter/resources/webgl-shared-render-pipeline.ts +208 -0
  95. package/src/adapter/resources/webgl-texture.ts +326 -121
  96. package/src/adapter/resources/webgl-transform-feedback.ts +5 -5
  97. package/src/adapter/webgl-adapter.ts +3 -4
  98. package/src/adapter/webgl-device.ts +66 -19
  99. package/src/adapter/webgl-presentation-context.ts +93 -0
  100. package/src/context/debug/spector.ts +4 -4
  101. package/src/context/debug/webgl-developer-tools.ts +2 -0
  102. package/src/context/helpers/create-browser-context.ts +8 -8
  103. package/src/context/helpers/webgl-context-data.ts +17 -11
  104. package/src/context/parameters/unified-parameter-api.ts +2 -2
  105. package/src/context/state-tracker/webgl-state-tracker.ts +2 -2
  106. package/src/utils/fill-array.ts +1 -1
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {CommandEncoder, CommandEncoderProps} from '@luma.gl/core';
5
+ import {CommandBufferProps, CommandEncoder, CommandEncoderProps} from '@luma.gl/core';
6
6
  import type {
7
7
  RenderPassProps,
8
8
  ComputePass,
@@ -20,6 +20,7 @@ import type {
20
20
  import {WEBGLCommandBuffer} from './webgl-command-buffer';
21
21
  import {WEBGLRenderPass} from './webgl-render-pass';
22
22
  import {WebGLDevice} from '../webgl-device';
23
+ import {WEBGLQuerySet} from './webgl-query-set';
23
24
 
24
25
  export class WEBGLCommandEncoder extends CommandEncoder {
25
26
  readonly device: WebGLDevice;
@@ -30,20 +31,29 @@ export class WEBGLCommandEncoder extends CommandEncoder {
30
31
  constructor(device: WebGLDevice, props: CommandEncoderProps) {
31
32
  super(device, props);
32
33
  this.device = device;
33
- this.commandBuffer = new WEBGLCommandBuffer(device);
34
+ this.commandBuffer = new WEBGLCommandBuffer(device, {
35
+ id: `${this.props.id}-command-buffer`
36
+ });
34
37
  }
35
38
 
36
- override destroy(): void {}
39
+ override destroy(): void {
40
+ this.destroyResource();
41
+ }
37
42
 
38
- override finish(): WEBGLCommandBuffer {
43
+ override finish(props?: CommandBufferProps): WEBGLCommandBuffer {
44
+ if (props?.id && this.commandBuffer.id !== props.id) {
45
+ this.commandBuffer.id = props.id;
46
+ this.commandBuffer.props.id = props.id;
47
+ }
48
+ this.destroy();
39
49
  return this.commandBuffer;
40
50
  }
41
51
 
42
- beginRenderPass(props: RenderPassProps): WEBGLRenderPass {
43
- return new WEBGLRenderPass(this.device, props);
52
+ beginRenderPass(props: RenderPassProps = {}): WEBGLRenderPass {
53
+ return new WEBGLRenderPass(this.device, this._applyTimeProfilingToPassProps(props));
44
54
  }
45
55
 
46
- beginComputePass(props: ComputePassProps): ComputePass {
56
+ beginComputePass(props: ComputePassProps = {}): ComputePass {
47
57
  throw new Error('ComputePass not supported in WebGL');
48
58
  }
49
59
 
@@ -81,4 +91,9 @@ export class WEBGLCommandEncoder extends CommandEncoder {
81
91
  destinationOffset?: number;
82
92
  }
83
93
  ): void {}
94
+
95
+ writeTimestamp(querySet: QuerySet, queryIndex: number): void {
96
+ const webglQuerySet = querySet as WEBGLQuerySet;
97
+ webglQuerySet.writeTimestamp(queryIndex);
98
+ }
84
99
  }
@@ -1,175 +1,302 @@
1
- // WebGL2 Query (also handles disjoint timer extensions)
1
+ // WebGL2 QuerySet (also handles disjoint timer extensions)
2
2
  import {QuerySet, QuerySetProps} from '@luma.gl/core';
3
3
  import {GL} from '@luma.gl/constants';
4
4
  import {WebGLDevice} from '../webgl-device';
5
5
 
6
+ type WebGLPendingQuery = {
7
+ handle: WebGLQuery;
8
+ promise: Promise<bigint> | null;
9
+ result: bigint | null;
10
+ disjoint: boolean;
11
+ };
12
+
13
+ type WebGLTimestampPair = {
14
+ activeQuery: WebGLPendingQuery | null;
15
+ completedQueries: WebGLPendingQuery[];
16
+ };
17
+
6
18
  /**
7
19
  * Asynchronous queries for different kinds of information
8
20
  */
9
21
  export class WEBGLQuerySet extends QuerySet {
10
22
  readonly device: WebGLDevice;
11
- readonly handle: WebGLQuery;
23
+ readonly handle: WebGLQuery | null;
12
24
 
13
- target: number | null = null;
14
- _queryPending = false;
15
- _pollingPromise: Promise<any> | null = null;
25
+ protected _timestampPairs: WebGLTimestampPair[] = [];
26
+ protected _occlusionQuery: WebGLPendingQuery | null = null;
27
+ protected _occlusionActive = false;
16
28
 
17
29
  override get [Symbol.toStringTag](): string {
18
- return 'Query';
30
+ return 'QuerySet';
19
31
  }
20
32
 
21
- // Create a query class
22
33
  constructor(device: WebGLDevice, props: QuerySetProps) {
23
34
  super(device, props);
24
35
  this.device = device;
25
36
 
26
- if (props.count > 1) {
27
- throw new Error('WebGL QuerySet can only have one value');
37
+ if (props.type === 'timestamp') {
38
+ if (props.count < 2) {
39
+ throw new Error('Timestamp QuerySet requires at least two query slots');
40
+ }
41
+ this._timestampPairs = new Array(Math.ceil(props.count / 2))
42
+ .fill(null)
43
+ .map(() => ({activeQuery: null, completedQueries: []}));
44
+ this.handle = null;
45
+ } else {
46
+ if (props.count > 1) {
47
+ throw new Error('WebGL occlusion QuerySet can only have one value');
48
+ }
49
+ const handle = this.device.gl.createQuery();
50
+ if (!handle) {
51
+ throw new Error('WebGL query not supported');
52
+ }
53
+ this.handle = handle;
28
54
  }
29
55
 
30
- const handle = this.device.gl.createQuery();
31
- if (!handle) {
32
- throw new Error('WebGL query not supported');
33
- }
34
- this.handle = handle;
35
56
  Object.seal(this);
36
57
  }
37
58
 
38
- override destroy() {
39
- this.device.gl.deleteQuery(this.handle);
40
- }
59
+ override destroy(): void {
60
+ if (this.destroyed) {
61
+ return;
62
+ }
41
63
 
42
- // FOR RENDER PASS AND COMMAND ENCODER
64
+ if (this.handle) {
65
+ this.device.gl.deleteQuery(this.handle);
66
+ }
43
67
 
44
- /**
45
- * Shortcut for timer query (dependent on extension in both WebGL1 and 2)
46
- * Measures GPU time delta between this call and a matching `end` call in the
47
- * GPU instruction stream.
48
- */
49
- beginTimestampQuery(): void {
50
- return this._begin(GL.TIME_ELAPSED_EXT);
51
- }
68
+ for (const pair of this._timestampPairs) {
69
+ if (pair.activeQuery) {
70
+ this.device.gl.deleteQuery(pair.activeQuery.handle);
71
+ }
72
+ for (const query of pair.completedQueries) {
73
+ this.device.gl.deleteQuery(query.handle);
74
+ }
75
+ }
76
+
77
+ if (this._occlusionQuery) {
78
+ this.device.gl.deleteQuery(this._occlusionQuery.handle);
79
+ }
52
80
 
53
- endTimestampQuery(): void {
54
- this._end();
81
+ this.destroyResource();
55
82
  }
56
83
 
57
- // Shortcut for occlusion queries
58
- beginOcclusionQuery(options?: {conservative?: boolean}): void {
59
- return this._begin(
60
- options?.conservative ? GL.ANY_SAMPLES_PASSED_CONSERVATIVE : GL.ANY_SAMPLES_PASSED
61
- );
84
+ isResultAvailable(queryIndex?: number): boolean {
85
+ if (this.props.type === 'timestamp') {
86
+ if (queryIndex === undefined) {
87
+ return this._timestampPairs.some((_, pairIndex) =>
88
+ this._isTimestampPairAvailable(pairIndex)
89
+ );
90
+ }
91
+ return this._isTimestampPairAvailable(this._getTimestampPairIndex(queryIndex));
92
+ }
93
+
94
+ if (!this._occlusionQuery) {
95
+ return false;
96
+ }
97
+
98
+ return this._pollQueryAvailability(this._occlusionQuery);
62
99
  }
63
100
 
64
- endOcclusionQuery() {
65
- this._end();
101
+ async readResults(options?: {firstQuery?: number; queryCount?: number}): Promise<bigint[]> {
102
+ const firstQuery = options?.firstQuery || 0;
103
+ const queryCount = options?.queryCount || this.props.count - firstQuery;
104
+ this._validateRange(firstQuery, queryCount);
105
+
106
+ if (this.props.type === 'timestamp') {
107
+ const results = new Array<bigint>(queryCount).fill(0n);
108
+ const startPairIndex = Math.floor(firstQuery / 2);
109
+ const endPairIndex = Math.floor((firstQuery + queryCount - 1) / 2);
110
+
111
+ for (let pairIndex = startPairIndex; pairIndex <= endPairIndex; pairIndex++) {
112
+ const duration = await this._consumeTimestampPairResult(pairIndex);
113
+ const beginSlot = pairIndex * 2;
114
+ const endSlot = beginSlot + 1;
115
+
116
+ if (beginSlot >= firstQuery && beginSlot < firstQuery + queryCount) {
117
+ results[beginSlot - firstQuery] = 0n;
118
+ }
119
+ if (endSlot >= firstQuery && endSlot < firstQuery + queryCount) {
120
+ results[endSlot - firstQuery] = duration;
121
+ }
122
+ }
123
+
124
+ return results;
125
+ }
126
+
127
+ if (!this._occlusionQuery) {
128
+ throw new Error('Occlusion query has not been started');
129
+ }
130
+
131
+ return [await this._consumeQueryResult(this._occlusionQuery)];
66
132
  }
67
133
 
68
- // Shortcut for transformFeedbackQuery
69
- beginTransformFeedbackQuery(): void {
70
- return this._begin(GL.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
134
+ async readTimestampDuration(beginIndex: number, endIndex: number): Promise<number> {
135
+ if (this.props.type !== 'timestamp') {
136
+ throw new Error('Timestamp durations require a timestamp QuerySet');
137
+ }
138
+ if (beginIndex < 0 || endIndex >= this.props.count || endIndex <= beginIndex) {
139
+ throw new Error('Timestamp duration range is out of bounds');
140
+ }
141
+ if (beginIndex % 2 !== 0 || endIndex !== beginIndex + 1) {
142
+ throw new Error('WebGL timestamp durations require adjacent even/odd query indices');
143
+ }
144
+
145
+ const result = await this._consumeTimestampPairResult(this._getTimestampPairIndex(beginIndex));
146
+ return Number(result) / 1e6;
71
147
  }
72
148
 
73
- endTransformFeedbackQuery(): void {
74
- this._end();
149
+ beginOcclusionQuery(): void {
150
+ if (this.props.type !== 'occlusion') {
151
+ throw new Error('Occlusion queries require an occlusion QuerySet');
152
+ }
153
+ if (!this.handle) {
154
+ throw new Error('WebGL occlusion query is not available');
155
+ }
156
+ if (this._occlusionActive) {
157
+ throw new Error('Occlusion query is already active');
158
+ }
159
+
160
+ this.device.gl.beginQuery(GL.ANY_SAMPLES_PASSED, this.handle);
161
+ this._occlusionQuery = {
162
+ handle: this.handle,
163
+ promise: null,
164
+ result: null,
165
+ disjoint: false
166
+ };
167
+ this._occlusionActive = true;
75
168
  }
76
169
 
77
- async resolveQuery(): Promise<bigint[]> {
78
- const value = await this.pollQuery();
79
- return [value];
170
+ endOcclusionQuery(): void {
171
+ if (!this._occlusionActive) {
172
+ throw new Error('Occlusion query is not active');
173
+ }
174
+
175
+ this.device.gl.endQuery(GL.ANY_SAMPLES_PASSED);
176
+ this._occlusionActive = false;
80
177
  }
81
178
 
82
- // PRIVATE METHODS
83
-
84
- /**
85
- * Due to OpenGL API limitations, after calling `begin()` on one Query
86
- * instance, `end()` must be called on that same instance before
87
- * calling `begin()` on another query. While there can be multiple
88
- * outstanding queries representing disjoint `begin()`/`end()` intervals.
89
- * It is not possible to interleave or overlap `begin` and `end` calls.
90
- */
91
- protected _begin(target: number): void {
92
- // Don't start a new query if one is already active.
93
- if (this._queryPending) {
179
+ writeTimestamp(queryIndex: number): void {
180
+ if (this.props.type !== 'timestamp') {
181
+ throw new Error('Timestamp writes require a timestamp QuerySet');
182
+ }
183
+
184
+ const pairIndex = this._getTimestampPairIndex(queryIndex);
185
+ const pair = this._timestampPairs[pairIndex];
186
+
187
+ if (queryIndex % 2 === 0) {
188
+ if (pair.activeQuery) {
189
+ throw new Error('Timestamp query pair is already active');
190
+ }
191
+
192
+ const handle = this.device.gl.createQuery();
193
+ if (!handle) {
194
+ throw new Error('WebGL query not supported');
195
+ }
196
+
197
+ const query: WebGLPendingQuery = {
198
+ handle,
199
+ promise: null,
200
+ result: null,
201
+ disjoint: false
202
+ };
203
+
204
+ this.device.gl.beginQuery(GL.TIME_ELAPSED_EXT, handle);
205
+ pair.activeQuery = query;
94
206
  return;
95
207
  }
96
208
 
97
- this.target = target;
98
- this.device.gl.beginQuery(this.target, this.handle);
209
+ if (!pair.activeQuery) {
210
+ throw new Error('Timestamp query pair was ended before it was started');
211
+ }
99
212
 
100
- return;
213
+ this.device.gl.endQuery(GL.TIME_ELAPSED_EXT);
214
+ pair.completedQueries.push(pair.activeQuery);
215
+ pair.activeQuery = null;
101
216
  }
102
217
 
103
- // ends the current query
104
- protected _end(): void {
105
- // Can't end a new query if the last one hasn't been resolved.
106
- if (this._queryPending) {
107
- return;
218
+ protected _validateRange(firstQuery: number, queryCount: number): void {
219
+ if (firstQuery < 0 || queryCount < 0 || firstQuery + queryCount > this.props.count) {
220
+ throw new Error('Query read range is out of bounds');
108
221
  }
222
+ }
109
223
 
110
- if (this.target) {
111
- this.device.gl.endQuery(this.target);
112
- this.target = null;
113
- this._queryPending = true;
224
+ protected _getTimestampPairIndex(queryIndex: number): number {
225
+ if (queryIndex < 0 || queryIndex >= this.props.count) {
226
+ throw new Error('Query index is out of bounds');
114
227
  }
115
- return;
228
+
229
+ return Math.floor(queryIndex / 2);
116
230
  }
117
231
 
118
- // Returns true if the query result is available
119
- isResultAvailable(): boolean {
120
- if (!this._queryPending) {
232
+ protected _isTimestampPairAvailable(pairIndex: number): boolean {
233
+ const pair = this._timestampPairs[pairIndex];
234
+ if (!pair || pair.completedQueries.length === 0) {
121
235
  return false;
122
236
  }
123
237
 
238
+ return this._pollQueryAvailability(pair.completedQueries[0]);
239
+ }
240
+
241
+ protected _pollQueryAvailability(query: WebGLPendingQuery): boolean {
242
+ if (query.result !== null || query.disjoint) {
243
+ return true;
244
+ }
245
+
124
246
  const resultAvailable = this.device.gl.getQueryParameter(
125
- this.handle,
247
+ query.handle,
126
248
  GL.QUERY_RESULT_AVAILABLE
127
249
  );
128
- if (resultAvailable) {
129
- this._queryPending = false;
250
+ if (!resultAvailable) {
251
+ return false;
130
252
  }
131
- return resultAvailable;
132
- }
133
253
 
134
- // Timing query is disjoint, i.e. results are invalid
135
- isTimerDisjoint(): boolean {
136
- return this.device.gl.getParameter(GL.GPU_DISJOINT_EXT);
254
+ const isDisjoint = Boolean(this.device.gl.getParameter(GL.GPU_DISJOINT_EXT));
255
+ query.disjoint = isDisjoint;
256
+ query.result = isDisjoint
257
+ ? 0n
258
+ : BigInt(this.device.gl.getQueryParameter(query.handle, GL.QUERY_RESULT));
259
+ return true;
137
260
  }
138
261
 
139
- // Returns query result.
140
- getResult(): any {
141
- return this.device.gl.getQueryParameter(this.handle, GL.QUERY_RESULT);
142
- }
262
+ protected async _consumeTimestampPairResult(pairIndex: number): Promise<bigint> {
263
+ const pair = this._timestampPairs[pairIndex];
264
+ if (!pair || pair.completedQueries.length === 0) {
265
+ throw new Error('Timestamp query pair has no completed result');
266
+ }
143
267
 
144
- // Returns the query result, converted to milliseconds to match JavaScript conventions.
145
- getTimerMilliseconds() {
146
- return this.getResult() / 1e6;
147
- }
268
+ const query = pair.completedQueries.shift()!;
148
269
 
149
- // Polls the query
150
- pollQuery(limit: number = Number.POSITIVE_INFINITY): Promise<any> {
151
- if (this._pollingPromise) {
152
- return this._pollingPromise;
270
+ try {
271
+ return await this._consumeQueryResult(query);
272
+ } finally {
273
+ this.device.gl.deleteQuery(query.handle);
153
274
  }
275
+ }
154
276
 
155
- let counter = 0;
277
+ protected _consumeQueryResult(query: WebGLPendingQuery): Promise<bigint> {
278
+ if (query.promise) {
279
+ return query.promise;
280
+ }
156
281
 
157
- this._pollingPromise = new Promise((resolve, reject) => {
282
+ query.promise = new Promise((resolve, reject) => {
158
283
  const poll = () => {
159
- if (this.isResultAvailable()) {
160
- resolve(this.getResult());
161
- this._pollingPromise = null;
162
- } else if (counter++ > limit) {
163
- reject('Timed out');
164
- this._pollingPromise = null;
165
- } else {
284
+ if (!this._pollQueryAvailability(query)) {
166
285
  requestAnimationFrame(poll);
286
+ return;
287
+ }
288
+
289
+ query.promise = null;
290
+ if (query.disjoint) {
291
+ reject(new Error('GPU timestamp query was invalidated by a disjoint event'));
292
+ } else {
293
+ resolve(query.result || 0n);
167
294
  }
168
295
  };
169
296
 
170
- requestAnimationFrame(poll);
297
+ poll();
171
298
  });
172
299
 
173
- return this._pollingPromise;
300
+ return query.promise;
174
301
  }
175
302
  }
@@ -24,6 +24,12 @@ export class WEBGLRenderPass extends RenderPass {
24
24
  super(device, props);
25
25
  this.device = device;
26
26
 
27
+ if (!props?.framebuffer) {
28
+ // Default-framebuffer rendering bypasses CanvasContext.getCurrentFramebuffer(),
29
+ // so flush any deferred canvas resize before deriving viewport state.
30
+ device.getDefaultCanvasContext()._resizeDrawingBufferIfNeeded();
31
+ }
32
+
27
33
  // If no viewport is provided, apply reasonably defaults
28
34
  let viewport: NumberArray4 | undefined;
29
35
  if (!props?.parameters?.viewport) {
@@ -57,11 +63,24 @@ export class WEBGLRenderPass extends RenderPass {
57
63
 
58
64
  // Hack - for now WebGL draws in "immediate mode" (instead of queueing the operations)...
59
65
  this.clear();
66
+
67
+ if (this.props.timestampQuerySet && this.props.beginTimestampIndex !== undefined) {
68
+ const webglQuerySet = this.props.timestampQuerySet as WEBGLQuerySet;
69
+ webglQuerySet.writeTimestamp(this.props.beginTimestampIndex);
70
+ }
60
71
  }
61
72
 
62
73
  end(): void {
74
+ if (this.destroyed) {
75
+ return;
76
+ }
77
+ if (this.props.timestampQuerySet && this.props.endTimestampIndex !== undefined) {
78
+ const webglQuerySet = this.props.timestampQuerySet as WEBGLQuerySet;
79
+ webglQuerySet.writeTimestamp(this.props.endTimestampIndex);
80
+ }
63
81
  this.device.popState();
64
82
  // should add commands to CommandEncoder.
83
+ this.destroy();
65
84
  }
66
85
 
67
86
  pushDebugGroup(groupLabel: string): void {}