@zephyr3d/editor 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{index-ySiLtSxz.js → index-BZ1L8ggE.js} +43 -3
- package/dist/assistant/zephyr-types-index.json +13230 -9488
- package/dist/images/icon1024.png +0 -0
- package/dist/index.html +1 -1
- package/dist/modules/zephyr3d_backend-webgl.js +2 -0
- package/dist/modules/zephyr3d_backend-webgl.js.map +1 -1
- package/dist/modules/zephyr3d_backend-webgpu.js +457 -16
- package/dist/modules/zephyr3d_backend-webgpu.js.map +1 -1
- package/dist/modules/zephyr3d_device.js +27 -0
- package/dist/modules/zephyr3d_device.js.map +1 -1
- package/dist/modules/zephyr3d_loaders.js +208 -1
- package/dist/modules/zephyr3d_loaders.js.map +1 -1
- package/dist/modules/zephyr3d_scene.js +774 -80
- package/dist/modules/zephyr3d_scene.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgl/dist/capabilities_webgl.js +2 -0
- package/dist/vendor/zephyr3d/backend-webgl/dist/capabilities_webgl.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/capabilities_webgpu.js +2 -0
- package/dist/vendor/zephyr3d/backend-webgpu/dist/capabilities_webgpu.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/commandqueue.js +55 -10
- package/dist/vendor/zephyr3d/backend-webgpu/dist/commandqueue.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/computepass_webgpu.js +4 -0
- package/dist/vendor/zephyr3d/backend-webgpu/dist/computepass_webgpu.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/device.js +44 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/device.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/renderpass_webgpu.js +58 -5
- package/dist/vendor/zephyr3d/backend-webgpu/dist/renderpass_webgpu.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/timestamp_query.js +297 -0
- package/dist/vendor/zephyr3d/backend-webgpu/dist/timestamp_query.js.map +1 -0
- package/dist/vendor/zephyr3d/device/dist/base_types.js.map +1 -1
- package/dist/vendor/zephyr3d/device/dist/device.js +27 -0
- package/dist/vendor/zephyr3d/device/dist/device.js.map +1 -1
- package/dist/vendor/zephyr3d/device/dist/index.d.ts +81 -1
- package/dist/vendor/zephyr3d/loaders/dist/gltf/gltf_importer.js +208 -1
- package/dist/vendor/zephyr3d/loaders/dist/gltf/gltf_importer.js.map +1 -1
- package/dist/vendor/zephyr3d/loaders/dist/index.d.ts +8 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js +334 -63
- package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/animation/morphtargetgrouptrack.js +66 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/morphtargetgrouptrack.js.map +1 -0
- package/dist/vendor/zephyr3d/scene/dist/asset/model.js +9 -0
- package/dist/vendor/zephyr3d/scene/dist/asset/model.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/index.d.ts +260 -4
- package/dist/vendor/zephyr3d/scene/dist/index.js +7 -0
- package/dist/vendor/zephyr3d/scene/dist/index.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/posteffect/sao.js +1 -1
- package/dist/vendor/zephyr3d/scene/dist/posteffect/sao.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/executor.js +270 -1
- package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/forward_plus_builder.js +19 -1
- package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/types.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/scene/scene.js +1 -0
- package/dist/vendor/zephyr3d/scene/dist/scene/scene.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js +2 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js +70 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/camera.js.map +1 -1
- package/package.json +6 -6
- package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics/convex_collider.js +0 -320
- package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics/convex_collider.js.map +0 -1
|
@@ -2773,6 +2773,7 @@ class WebGPUMiscCaps {
|
|
|
2773
2773
|
supportDepthClamp;
|
|
2774
2774
|
maxBindGroups;
|
|
2775
2775
|
maxTexCoordIndex;
|
|
2776
|
+
supportTimestampQuery;
|
|
2776
2777
|
constructor(device){
|
|
2777
2778
|
this.supportOversizedViewport = false;
|
|
2778
2779
|
this.supportBlendMinMax = true;
|
|
@@ -2780,6 +2781,7 @@ class WebGPUMiscCaps {
|
|
|
2780
2781
|
this.supportDepthClamp = device.device.features.has('depth-clip-control');
|
|
2781
2782
|
this.maxBindGroups = 4;
|
|
2782
2783
|
this.maxTexCoordIndex = 8;
|
|
2784
|
+
this.supportTimestampQuery = device.device.features.has('timestamp-query');
|
|
2783
2785
|
}
|
|
2784
2786
|
}
|
|
2785
2787
|
class WebGPUShaderCaps {
|
|
@@ -4879,6 +4881,7 @@ class WebGPURenderPass {
|
|
|
4879
4881
|
_currentViewport;
|
|
4880
4882
|
_currentScissor;
|
|
4881
4883
|
_frameBufferInfo;
|
|
4884
|
+
_deferredMipmapTextures;
|
|
4882
4885
|
constructor(device){
|
|
4883
4886
|
this._device = device;
|
|
4884
4887
|
this._renderCommandEncoder = null;
|
|
@@ -4887,6 +4890,7 @@ class WebGPURenderPass {
|
|
|
4887
4890
|
this._currentViewport = null;
|
|
4888
4891
|
this._currentScissor = null;
|
|
4889
4892
|
this._frameBufferInfo = this.createFrameBufferInfo(null);
|
|
4893
|
+
this._deferredMipmapTextures = [];
|
|
4890
4894
|
}
|
|
4891
4895
|
get active() {
|
|
4892
4896
|
return !!this._renderPassEncoder;
|
|
@@ -5034,6 +5038,10 @@ class WebGPURenderPass {
|
|
|
5034
5038
|
if (validation & VALIDATION_FAILED$1) {
|
|
5035
5039
|
return;
|
|
5036
5040
|
}
|
|
5041
|
+
if (this.hasDeferredMipmapsForBindGroups(bindGroups)) {
|
|
5042
|
+
this.end();
|
|
5043
|
+
this.flushDeferredMipmapsForBindGroups(bindGroups);
|
|
5044
|
+
}
|
|
5037
5045
|
if (validation & VALIDATION_NEED_NEW_PASS) {
|
|
5038
5046
|
this.end();
|
|
5039
5047
|
}
|
|
@@ -5153,7 +5161,7 @@ class WebGPURenderPass {
|
|
|
5153
5161
|
this.setViewport(this._currentViewport);
|
|
5154
5162
|
this.setScissor(this._currentScissor);
|
|
5155
5163
|
}
|
|
5156
|
-
end() {
|
|
5164
|
+
end(generateMipmaps = true) {
|
|
5157
5165
|
if (!this.active) {
|
|
5158
5166
|
return;
|
|
5159
5167
|
}
|
|
@@ -5168,15 +5176,38 @@ class WebGPURenderPass {
|
|
|
5168
5176
|
const options = this._frameBufferInfo.frameBuffer.getOptions();
|
|
5169
5177
|
if (options.colorAttachments) {
|
|
5170
5178
|
for (const attachment of options.colorAttachments){
|
|
5171
|
-
attachment.texture
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
}
|
|
5179
|
+
const texture = attachment.texture;
|
|
5180
|
+
texture._markAsCurrentFB(false);
|
|
5181
|
+
this.handleColorAttachmentMipmap(texture, !!attachment.generateMipmaps, generateMipmaps);
|
|
5175
5182
|
}
|
|
5176
5183
|
}
|
|
5177
5184
|
options.depthAttachment?.texture?._markAsCurrentFB(false);
|
|
5178
5185
|
}
|
|
5179
5186
|
}
|
|
5187
|
+
hasDeferredMipmapsForBindGroups(bindGroups) {
|
|
5188
|
+
return bindGroups?.some((bindGroup)=>{
|
|
5189
|
+
return bindGroup?.textureList.some((texture)=>this._deferredMipmapTextures.indexOf(texture) >= 0);
|
|
5190
|
+
});
|
|
5191
|
+
}
|
|
5192
|
+
flushDeferredMipmapsForBindGroups(bindGroups) {
|
|
5193
|
+
if (!bindGroups) {
|
|
5194
|
+
this.flushDeferredMipmaps();
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
5197
|
+
for (const bindGroup of bindGroups){
|
|
5198
|
+
for (const texture of bindGroup?.textureList ?? []){
|
|
5199
|
+
this.flushDeferredMipmap(texture);
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
5203
|
+
flushDeferredMipmaps() {
|
|
5204
|
+
const textures = this._deferredMipmapTextures.splice(0);
|
|
5205
|
+
for (const texture of textures){
|
|
5206
|
+
if (!texture.disposed) {
|
|
5207
|
+
texture.generateMipmaps();
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5180
5211
|
capture(renderBundleEncoder, program, vertexData, stateSet, bindGroups, bindGroupOffsets, primitiveType, first, count, numInstances) {
|
|
5181
5212
|
const validation = this.validateDraw(program, bindGroups, stateSet, primitiveType, first, count, numInstances);
|
|
5182
5213
|
if (validation & VALIDATION_FAILED$1) {
|
|
@@ -5287,6 +5318,30 @@ class WebGPURenderPass {
|
|
|
5287
5318
|
}
|
|
5288
5319
|
return validation;
|
|
5289
5320
|
}
|
|
5321
|
+
handleColorAttachmentMipmap(texture, attachmentGenerateMipmaps, generateMipmaps) {
|
|
5322
|
+
if (!attachmentGenerateMipmaps || texture.mipLevelCount <= 1) {
|
|
5323
|
+
return;
|
|
5324
|
+
}
|
|
5325
|
+
if (generateMipmaps) {
|
|
5326
|
+
this.removeDeferredMipmap(texture);
|
|
5327
|
+
texture.generateMipmaps();
|
|
5328
|
+
} else if (this._deferredMipmapTextures.indexOf(texture) < 0) {
|
|
5329
|
+
this._deferredMipmapTextures.push(texture);
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
5332
|
+
flushDeferredMipmap(texture) {
|
|
5333
|
+
if (this.removeDeferredMipmap(texture) && !texture.disposed) {
|
|
5334
|
+
texture.generateMipmaps();
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
removeDeferredMipmap(texture) {
|
|
5338
|
+
const index = this._deferredMipmapTextures.indexOf(texture);
|
|
5339
|
+
if (index >= 0) {
|
|
5340
|
+
this._deferredMipmapTextures.splice(index, 1);
|
|
5341
|
+
return true;
|
|
5342
|
+
}
|
|
5343
|
+
return false;
|
|
5344
|
+
}
|
|
5290
5345
|
setBindGroupsForRender(renderPassEncoder, program, bindGroups, bindGroupOffsets, renderBundleEncoder) {
|
|
5291
5346
|
if (bindGroups) {
|
|
5292
5347
|
for(let i = 0; i < 4; i++){
|
|
@@ -5331,6 +5386,10 @@ class WebGPUComputePass {
|
|
|
5331
5386
|
if (validation & VALIDATION_FAILED) {
|
|
5332
5387
|
return;
|
|
5333
5388
|
}
|
|
5389
|
+
if (this._device.commandQueue.hasDeferredMipmapsForBindGroups(bindGroups)) {
|
|
5390
|
+
this.end();
|
|
5391
|
+
this._device.commandQueue.flushDeferredMipmapsForBindGroups(bindGroups);
|
|
5392
|
+
}
|
|
5334
5393
|
if (!this.active) {
|
|
5335
5394
|
this.begin();
|
|
5336
5395
|
}
|
|
@@ -5437,10 +5496,7 @@ class CommandQueueImmediate {
|
|
|
5437
5496
|
getEncoder() {
|
|
5438
5497
|
const segment = this.getOrCreateCurrentSegment();
|
|
5439
5498
|
segment.hasBodyCommands = true;
|
|
5440
|
-
|
|
5441
|
-
segment.bodyEncoder = this._device.device.createCommandEncoder();
|
|
5442
|
-
}
|
|
5443
|
-
return segment.bodyEncoder;
|
|
5499
|
+
return this.getOrCreateBodyEncoder(segment);
|
|
5444
5500
|
}
|
|
5445
5501
|
flushUploads() {
|
|
5446
5502
|
this.finalizeCurrentSegmentUploads();
|
|
@@ -5557,6 +5613,26 @@ class CommandQueueImmediate {
|
|
|
5557
5613
|
this.submit();
|
|
5558
5614
|
return this._device.device.queue.onSubmittedWorkDone();
|
|
5559
5615
|
}
|
|
5616
|
+
insertTimestampMarker(querySet, queryIndex, includePendingUploads) {
|
|
5617
|
+
this.endAllBodyPasses(false);
|
|
5618
|
+
if (includePendingUploads) {
|
|
5619
|
+
this.finalizeCurrentSegmentUploads();
|
|
5620
|
+
const encoder = this.getEncoder();
|
|
5621
|
+
this.encodeTimestampMarker(encoder, querySet, queryIndex);
|
|
5622
|
+
} else {
|
|
5623
|
+
const segment = this.getOrCreateCurrentSegment();
|
|
5624
|
+
const pendingBuffers = segment.buffersWithPendingUploads.splice(0);
|
|
5625
|
+
const pendingTextures = segment.texturesWithPendingUploads.splice(0);
|
|
5626
|
+
segment.hasBodyCommands = true;
|
|
5627
|
+
const encoder = this.getOrCreateBodyEncoder(segment);
|
|
5628
|
+
this.encodeTimestampMarker(encoder, querySet, queryIndex);
|
|
5629
|
+
if (pendingBuffers.length > 0 || pendingTextures.length > 0) {
|
|
5630
|
+
const nextSegment = this.createSegment();
|
|
5631
|
+
nextSegment.buffersWithPendingUploads.push(...pendingBuffers);
|
|
5632
|
+
nextSegment.texturesWithPendingUploads.push(...pendingTextures);
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5560
5636
|
createSegment() {
|
|
5561
5637
|
const segment = {
|
|
5562
5638
|
uploadEncoder: null,
|
|
@@ -5581,6 +5657,12 @@ class CommandQueueImmediate {
|
|
|
5581
5657
|
}
|
|
5582
5658
|
return segment.uploadEncoder;
|
|
5583
5659
|
}
|
|
5660
|
+
getOrCreateBodyEncoder(segment) {
|
|
5661
|
+
if (!segment.bodyEncoder) {
|
|
5662
|
+
segment.bodyEncoder = this._device.device.createCommandEncoder();
|
|
5663
|
+
}
|
|
5664
|
+
return segment.bodyEncoder;
|
|
5665
|
+
}
|
|
5584
5666
|
ensureRenderBodyReady() {
|
|
5585
5667
|
if (this._computePass.active) {
|
|
5586
5668
|
this._computePass.end();
|
|
@@ -5591,9 +5673,15 @@ class CommandQueueImmediate {
|
|
|
5591
5673
|
this._renderPass.end();
|
|
5592
5674
|
}
|
|
5593
5675
|
}
|
|
5594
|
-
|
|
5676
|
+
flushDeferredMipmapsForBindGroups(bindGroups) {
|
|
5677
|
+
this._renderPass.flushDeferredMipmapsForBindGroups(bindGroups);
|
|
5678
|
+
}
|
|
5679
|
+
hasDeferredMipmapsForBindGroups(bindGroups) {
|
|
5680
|
+
return this._renderPass.hasDeferredMipmapsForBindGroups(bindGroups);
|
|
5681
|
+
}
|
|
5682
|
+
endAllBodyPasses(generateMipmaps = true) {
|
|
5595
5683
|
if (this._renderPass.active) {
|
|
5596
|
-
this._renderPass.end();
|
|
5684
|
+
this._renderPass.end(generateMipmaps);
|
|
5597
5685
|
}
|
|
5598
5686
|
if (this._computePass.active) {
|
|
5599
5687
|
this._computePass.end();
|
|
@@ -5601,9 +5689,10 @@ class CommandQueueImmediate {
|
|
|
5601
5689
|
}
|
|
5602
5690
|
submit() {
|
|
5603
5691
|
this.endAllBodyPasses();
|
|
5692
|
+
this._renderPass.flushDeferredMipmaps();
|
|
5604
5693
|
this.finalizeCurrentSegmentUploads();
|
|
5694
|
+
const commandBuffers = [];
|
|
5605
5695
|
if (this._segments.length > 0) {
|
|
5606
|
-
const commandBuffers = [];
|
|
5607
5696
|
for (const segment of this._segments){
|
|
5608
5697
|
if (segment.uploadEncoder && segment.hasUploadCommands) {
|
|
5609
5698
|
commandBuffers.push(segment.uploadEncoder.finish());
|
|
@@ -5612,9 +5701,14 @@ class CommandQueueImmediate {
|
|
|
5612
5701
|
commandBuffers.push(segment.bodyEncoder.finish());
|
|
5613
5702
|
}
|
|
5614
5703
|
}
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5704
|
+
}
|
|
5705
|
+
const timestampResolveCommandBuffer = this._device.createTimestampResolveCommandBuffer();
|
|
5706
|
+
if (timestampResolveCommandBuffer) {
|
|
5707
|
+
commandBuffers.push(timestampResolveCommandBuffer);
|
|
5708
|
+
}
|
|
5709
|
+
if (commandBuffers.length > 0) {
|
|
5710
|
+
this._device.device.queue.submit(commandBuffers);
|
|
5711
|
+
this._device.onTimestampCommandBuffersSubmitted();
|
|
5618
5712
|
}
|
|
5619
5713
|
for (const buffer of this._buffersAwaitingSyncEnd){
|
|
5620
5714
|
buffer.endSyncChanges();
|
|
@@ -5627,6 +5721,16 @@ class CommandQueueImmediate {
|
|
|
5627
5721
|
this._segments.length = 0;
|
|
5628
5722
|
this._drawcallCounter = 0;
|
|
5629
5723
|
}
|
|
5724
|
+
encodeTimestampMarker(encoder, querySet, queryIndex) {
|
|
5725
|
+
const pass = encoder.beginComputePass({
|
|
5726
|
+
label: `timestamp-marker:${queryIndex}`,
|
|
5727
|
+
timestampWrites: {
|
|
5728
|
+
querySet,
|
|
5729
|
+
beginningOfPassWriteIndex: queryIndex
|
|
5730
|
+
}
|
|
5731
|
+
});
|
|
5732
|
+
pass.end();
|
|
5733
|
+
}
|
|
5630
5734
|
markBufferAwaitingSyncEnd(buffer) {
|
|
5631
5735
|
if (this._buffersAwaitingSyncEnd.indexOf(buffer) < 0) {
|
|
5632
5736
|
this._buffersAwaitingSyncEnd.push(buffer);
|
|
@@ -5675,6 +5779,301 @@ class CommandQueueImmediate {
|
|
|
5675
5779
|
}
|
|
5676
5780
|
}
|
|
5677
5781
|
|
|
5782
|
+
const TIMESTAMP_QUERY_COUNT = 4096;
|
|
5783
|
+
const TIMESTAMP_BYTES_PER_QUERY = 8;
|
|
5784
|
+
const TIMESTAMP_QUERIES_PER_SCOPE = 2;
|
|
5785
|
+
const TIMESTAMP_RESOLVE_STRIDE = 256;
|
|
5786
|
+
const MAX_RESOLVED_RESULT_HISTORY = 4096;
|
|
5787
|
+
const defaultTimestampQueryOptions = {
|
|
5788
|
+
includePendingUploads: true,
|
|
5789
|
+
allowCrossFrame: false,
|
|
5790
|
+
strict: false
|
|
5791
|
+
};
|
|
5792
|
+
class WebGPUTimestampQueryManager {
|
|
5793
|
+
_device;
|
|
5794
|
+
_supported;
|
|
5795
|
+
_querySet;
|
|
5796
|
+
_freeQueryPairs;
|
|
5797
|
+
_records;
|
|
5798
|
+
_openRecords;
|
|
5799
|
+
_endedRecords;
|
|
5800
|
+
_pendingSubmitBatches;
|
|
5801
|
+
_resolvedResults;
|
|
5802
|
+
_nextId;
|
|
5803
|
+
constructor(device){
|
|
5804
|
+
this._device = device;
|
|
5805
|
+
this._supported = device.device.features.has('timestamp-query');
|
|
5806
|
+
this._querySet = this._supported ? device.device.createQuerySet({
|
|
5807
|
+
type: 'timestamp',
|
|
5808
|
+
count: TIMESTAMP_QUERY_COUNT
|
|
5809
|
+
}) : null;
|
|
5810
|
+
this._freeQueryPairs = [];
|
|
5811
|
+
for(let i = TIMESTAMP_QUERY_COUNT - 2; i >= 0; i -= 2){
|
|
5812
|
+
this._freeQueryPairs.push(i);
|
|
5813
|
+
}
|
|
5814
|
+
this._records = new Map();
|
|
5815
|
+
this._openRecords = new Map();
|
|
5816
|
+
this._endedRecords = [];
|
|
5817
|
+
this._pendingSubmitBatches = [];
|
|
5818
|
+
this._resolvedResults = [];
|
|
5819
|
+
this._nextId = 0;
|
|
5820
|
+
}
|
|
5821
|
+
get supported() {
|
|
5822
|
+
return this._supported;
|
|
5823
|
+
}
|
|
5824
|
+
begin(label, options) {
|
|
5825
|
+
if (!this._supported || !this._querySet) {
|
|
5826
|
+
return 0;
|
|
5827
|
+
}
|
|
5828
|
+
const id = ++this._nextId;
|
|
5829
|
+
const resolvedOptions = {
|
|
5830
|
+
...defaultTimestampQueryOptions,
|
|
5831
|
+
...options ?? {}
|
|
5832
|
+
};
|
|
5833
|
+
const startIndex = this._freeQueryPairs.pop();
|
|
5834
|
+
if (startIndex === undefined) {
|
|
5835
|
+
const result = this.createResult(id, label ?? '', this._device.frameInfo.frameCounter, 0n, 0n, 'exhausted', false, 'GPU timestamp query pool exhausted');
|
|
5836
|
+
this._records.set(id, {
|
|
5837
|
+
id,
|
|
5838
|
+
label: result.label,
|
|
5839
|
+
frameId: result.frameId,
|
|
5840
|
+
options: resolvedOptions,
|
|
5841
|
+
startIndex: -1,
|
|
5842
|
+
endIndex: -1,
|
|
5843
|
+
status: 'exhausted',
|
|
5844
|
+
autoClosed: false,
|
|
5845
|
+
result,
|
|
5846
|
+
waiters: []
|
|
5847
|
+
});
|
|
5848
|
+
this.pushResolvedResult(result);
|
|
5849
|
+
return id;
|
|
5850
|
+
}
|
|
5851
|
+
const record = {
|
|
5852
|
+
id,
|
|
5853
|
+
label: label ?? '',
|
|
5854
|
+
frameId: this._device.frameInfo.frameCounter,
|
|
5855
|
+
options: resolvedOptions,
|
|
5856
|
+
startIndex,
|
|
5857
|
+
endIndex: startIndex + 1,
|
|
5858
|
+
status: 'open',
|
|
5859
|
+
autoClosed: false,
|
|
5860
|
+
result: null,
|
|
5861
|
+
waiters: []
|
|
5862
|
+
};
|
|
5863
|
+
this._records.set(id, record);
|
|
5864
|
+
this._openRecords.set(id, record);
|
|
5865
|
+
this._device.commandQueue.insertTimestampMarker(this._querySet, record.startIndex, record.options.includePendingUploads);
|
|
5866
|
+
return id;
|
|
5867
|
+
}
|
|
5868
|
+
end(id) {
|
|
5869
|
+
if (!this._supported) {
|
|
5870
|
+
return;
|
|
5871
|
+
}
|
|
5872
|
+
const record = this._records.get(id);
|
|
5873
|
+
if (!record) {
|
|
5874
|
+
this.reportInvalidEnd(id, 'endTimestampQuery() ignored: invalid query id');
|
|
5875
|
+
return;
|
|
5876
|
+
}
|
|
5877
|
+
if (record.status !== 'open') {
|
|
5878
|
+
if (record.status !== 'exhausted' && record.status !== 'unsupported') {
|
|
5879
|
+
this.reportInvalidEnd(id, 'endTimestampQuery() ignored: query is not open');
|
|
5880
|
+
}
|
|
5881
|
+
return;
|
|
5882
|
+
}
|
|
5883
|
+
this.endRecord(record, false);
|
|
5884
|
+
}
|
|
5885
|
+
autoCloseOpenScopes() {
|
|
5886
|
+
for (const record of Array.from(this._openRecords.values())){
|
|
5887
|
+
if (!record.options.allowCrossFrame) {
|
|
5888
|
+
this.endRecord(record, true);
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
}
|
|
5892
|
+
createResolveCommandBuffer() {
|
|
5893
|
+
if (!this._querySet || this._endedRecords.length === 0) {
|
|
5894
|
+
return null;
|
|
5895
|
+
}
|
|
5896
|
+
const records = this._endedRecords.splice(0);
|
|
5897
|
+
const entries = records.map((record, index)=>{
|
|
5898
|
+
record.status = 'resolving';
|
|
5899
|
+
return {
|
|
5900
|
+
record,
|
|
5901
|
+
readbackOffset: index * TIMESTAMP_RESOLVE_STRIDE
|
|
5902
|
+
};
|
|
5903
|
+
});
|
|
5904
|
+
const byteLength = entries.length * TIMESTAMP_RESOLVE_STRIDE;
|
|
5905
|
+
const resolveBuffer = this._device.device.createBuffer({
|
|
5906
|
+
label: 'timestamp-query-resolve',
|
|
5907
|
+
size: byteLength,
|
|
5908
|
+
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC
|
|
5909
|
+
});
|
|
5910
|
+
const readbackBuffer = this._device.device.createBuffer({
|
|
5911
|
+
label: 'timestamp-query-readback',
|
|
5912
|
+
size: byteLength,
|
|
5913
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
5914
|
+
});
|
|
5915
|
+
const encoder = this._device.device.createCommandEncoder({
|
|
5916
|
+
label: 'timestamp-query-resolve'
|
|
5917
|
+
});
|
|
5918
|
+
for (const entry of entries){
|
|
5919
|
+
encoder.resolveQuerySet(this._querySet, entry.record.startIndex, TIMESTAMP_QUERIES_PER_SCOPE, resolveBuffer, entry.readbackOffset);
|
|
5920
|
+
}
|
|
5921
|
+
encoder.copyBufferToBuffer(resolveBuffer, 0, readbackBuffer, 0, byteLength);
|
|
5922
|
+
this._pendingSubmitBatches.push({
|
|
5923
|
+
entries,
|
|
5924
|
+
resolveBuffer,
|
|
5925
|
+
readbackBuffer
|
|
5926
|
+
});
|
|
5927
|
+
return encoder.finish();
|
|
5928
|
+
}
|
|
5929
|
+
onSubmitted() {
|
|
5930
|
+
const batches = this._pendingSubmitBatches.splice(0);
|
|
5931
|
+
for (const batch of batches){
|
|
5932
|
+
batch.readbackBuffer.mapAsync(GPUMapMode.READ).then(()=>{
|
|
5933
|
+
this.completeBatch(batch);
|
|
5934
|
+
}).catch((err)=>{
|
|
5935
|
+
this.failBatch(batch, err instanceof Error ? err.message : String(err));
|
|
5936
|
+
});
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
poll(query) {
|
|
5940
|
+
if (!this._supported) {
|
|
5941
|
+
return this.createUnsupportedResult(query);
|
|
5942
|
+
}
|
|
5943
|
+
const record = this.findRecord(query);
|
|
5944
|
+
if (!record) {
|
|
5945
|
+
return this.createInvalidResult(query, 'Timestamp query not found');
|
|
5946
|
+
}
|
|
5947
|
+
return record.result;
|
|
5948
|
+
}
|
|
5949
|
+
resolve(query) {
|
|
5950
|
+
if (!this._supported) {
|
|
5951
|
+
return Promise.resolve(this.createUnsupportedResult(query));
|
|
5952
|
+
}
|
|
5953
|
+
const record = this.findRecord(query);
|
|
5954
|
+
if (!record) {
|
|
5955
|
+
return Promise.resolve(this.createInvalidResult(query, 'Timestamp query not found'));
|
|
5956
|
+
}
|
|
5957
|
+
if (record.result) {
|
|
5958
|
+
return Promise.resolve(record.result);
|
|
5959
|
+
}
|
|
5960
|
+
return new Promise((resolve)=>{
|
|
5961
|
+
record.waiters.push(resolve);
|
|
5962
|
+
});
|
|
5963
|
+
}
|
|
5964
|
+
collect(filter) {
|
|
5965
|
+
return this._resolvedResults.filter((result)=>{
|
|
5966
|
+
return (filter?.label === undefined || result.label === filter.label) && (filter?.frameId === undefined || result.frameId === filter.frameId);
|
|
5967
|
+
});
|
|
5968
|
+
}
|
|
5969
|
+
endRecord(record, autoClosed) {
|
|
5970
|
+
if (!this._querySet) {
|
|
5971
|
+
return;
|
|
5972
|
+
}
|
|
5973
|
+
record.autoClosed = autoClosed;
|
|
5974
|
+
this._openRecords.delete(record.id);
|
|
5975
|
+
this._device.commandQueue.insertTimestampMarker(this._querySet, record.endIndex, record.options.includePendingUploads);
|
|
5976
|
+
record.status = 'ended';
|
|
5977
|
+
this._endedRecords.push(record);
|
|
5978
|
+
if (autoClosed) {
|
|
5979
|
+
console.warn(`GPU timestamp query '${record.label}' was automatically closed at frame end`);
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
completeBatch(batch) {
|
|
5983
|
+
const values = new BigUint64Array(batch.readbackBuffer.getMappedRange());
|
|
5984
|
+
for (const entry of batch.entries){
|
|
5985
|
+
const record = entry.record;
|
|
5986
|
+
const valueOffset = entry.readbackOffset / TIMESTAMP_BYTES_PER_QUERY;
|
|
5987
|
+
const start = values[valueOffset];
|
|
5988
|
+
const end = values[valueOffset + 1];
|
|
5989
|
+
const duration = end >= start ? end - start : 0n;
|
|
5990
|
+
const result = this.createResult(record.id, record.label, record.frameId, start, end, record.autoClosed ? 'auto-closed' : 'resolved', record.autoClosed);
|
|
5991
|
+
result.durationMs = Number(duration) / 1000000;
|
|
5992
|
+
record.status = result.status;
|
|
5993
|
+
record.result = result;
|
|
5994
|
+
this.freeQueryPair(record.startIndex);
|
|
5995
|
+
this.resolveWaiters(record);
|
|
5996
|
+
this.pushResolvedResult(result);
|
|
5997
|
+
}
|
|
5998
|
+
batch.readbackBuffer.unmap();
|
|
5999
|
+
batch.readbackBuffer.destroy();
|
|
6000
|
+
batch.resolveBuffer.destroy();
|
|
6001
|
+
}
|
|
6002
|
+
failBatch(batch, message) {
|
|
6003
|
+
for (const entry of batch.entries){
|
|
6004
|
+
const record = entry.record;
|
|
6005
|
+
const result = this.createResult(record.id, record.label, record.frameId, 0n, 0n, 'failed', record.autoClosed, message);
|
|
6006
|
+
record.status = 'failed';
|
|
6007
|
+
record.result = result;
|
|
6008
|
+
this.freeQueryPair(record.startIndex);
|
|
6009
|
+
this.resolveWaiters(record);
|
|
6010
|
+
this.pushResolvedResult(result);
|
|
6011
|
+
}
|
|
6012
|
+
batch.readbackBuffer.destroy();
|
|
6013
|
+
batch.resolveBuffer.destroy();
|
|
6014
|
+
}
|
|
6015
|
+
freeQueryPair(startIndex) {
|
|
6016
|
+
if (startIndex >= 0 && this._freeQueryPairs.indexOf(startIndex) < 0) {
|
|
6017
|
+
this._freeQueryPairs.push(startIndex);
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
resolveWaiters(record) {
|
|
6021
|
+
for (const resolve of record.waiters.splice(0)){
|
|
6022
|
+
resolve(record.result);
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
pushResolvedResult(result) {
|
|
6026
|
+
this._resolvedResults.push(result);
|
|
6027
|
+
while(this._resolvedResults.length > MAX_RESOLVED_RESULT_HISTORY){
|
|
6028
|
+
const oldResult = this._resolvedResults.shift();
|
|
6029
|
+
if (oldResult) {
|
|
6030
|
+
const oldRecord = this._records.get(oldResult.id);
|
|
6031
|
+
if (oldRecord?.result === oldResult) {
|
|
6032
|
+
this._records.delete(oldResult.id);
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
reportInvalidEnd(id, message) {
|
|
6038
|
+
const record = this._records.get(id);
|
|
6039
|
+
if (record?.options.strict) {
|
|
6040
|
+
throw new Error(message);
|
|
6041
|
+
}
|
|
6042
|
+
console.warn(message);
|
|
6043
|
+
}
|
|
6044
|
+
findRecord(query) {
|
|
6045
|
+
if (typeof query === 'number') {
|
|
6046
|
+
return this._records.get(query) ?? null;
|
|
6047
|
+
}
|
|
6048
|
+
let latest = null;
|
|
6049
|
+
for (const record of this._records.values()){
|
|
6050
|
+
if (record.label === query) {
|
|
6051
|
+
latest = record;
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
return latest;
|
|
6055
|
+
}
|
|
6056
|
+
createUnsupportedResult(query) {
|
|
6057
|
+
return this.createResult(typeof query === 'number' ? query : 0, typeof query === 'string' ? query : '', this._device.frameInfo.frameCounter, 0n, 0n, 'unsupported', false, 'GPU timestamp queries are not supported by this WebGPU device');
|
|
6058
|
+
}
|
|
6059
|
+
createInvalidResult(query, message) {
|
|
6060
|
+
return this.createResult(typeof query === 'number' ? query : 0, typeof query === 'string' ? query : '', this._device.frameInfo.frameCounter, 0n, 0n, 'invalid', false, message);
|
|
6061
|
+
}
|
|
6062
|
+
createResult(id, label, frameId, start, end, status, autoClosed, message) {
|
|
6063
|
+
return {
|
|
6064
|
+
id,
|
|
6065
|
+
label,
|
|
6066
|
+
frameId,
|
|
6067
|
+
durationMs: 0,
|
|
6068
|
+
start,
|
|
6069
|
+
end,
|
|
6070
|
+
status,
|
|
6071
|
+
autoClosed: autoClosed || undefined,
|
|
6072
|
+
message
|
|
6073
|
+
};
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
|
|
5678
6077
|
class WebGPUDevice extends BaseDevice {
|
|
5679
6078
|
_context;
|
|
5680
6079
|
_device;
|
|
@@ -5705,6 +6104,7 @@ class WebGPUDevice extends BaseDevice {
|
|
|
5705
6104
|
_emptyBindGroup;
|
|
5706
6105
|
_captureRenderBundle;
|
|
5707
6106
|
_adapterInfo;
|
|
6107
|
+
_timestampQueries;
|
|
5708
6108
|
constructor(backend, cvs, options){
|
|
5709
6109
|
super(cvs, backend, options?.dpr);
|
|
5710
6110
|
this._reverseWindingOrder = false;
|
|
@@ -5815,7 +6215,25 @@ class WebGPUDevice extends BaseDevice {
|
|
|
5815
6215
|
...this._adapter.features
|
|
5816
6216
|
],
|
|
5817
6217
|
requiredLimits: {
|
|
5818
|
-
|
|
6218
|
+
maxBufferSize: this._adapter.limits.maxBufferSize,
|
|
6219
|
+
maxBindingsPerBindGroup: this._adapter.limits.maxBindingsPerBindGroup,
|
|
6220
|
+
maxTextureDimension2D: this._adapter.limits.maxTextureDimension2D,
|
|
6221
|
+
maxComputeWorkgroupSizeX: this._adapter.limits.maxComputeWorkgroupSizeX,
|
|
6222
|
+
maxComputeWorkgroupSizeY: this._adapter.limits.maxComputeWorkgroupSizeY,
|
|
6223
|
+
maxComputeWorkgroupSizeZ: this._adapter.limits.maxComputeWorkgroupSizeZ,
|
|
6224
|
+
maxSampledTexturesPerShaderStage: this._adapter.limits.maxSampledTexturesPerShaderStage,
|
|
6225
|
+
maxSamplersPerShaderStage: this._adapter.limits.maxSamplersPerShaderStage,
|
|
6226
|
+
maxStorageBuffersPerShaderStage: this._adapter.limits.maxStorageBuffersPerShaderStage,
|
|
6227
|
+
maxStorageTexturesPerShaderStage: this._adapter.limits.maxStorageTexturesPerShaderStage,
|
|
6228
|
+
maxUniformBuffersPerShaderStage: this._adapter.limits.maxUniformBuffersPerShaderStage,
|
|
6229
|
+
maxUniformBufferBindingSize: this._adapter.limits.maxUniformBufferBindingSize,
|
|
6230
|
+
maxStorageBufferBindingSize: this._adapter.limits.maxStorageBufferBindingSize,
|
|
6231
|
+
maxInterStageShaderVariables: this._adapter.limits.maxInterStageShaderVariables,
|
|
6232
|
+
maxColorAttachments: this._adapter.limits.maxColorAttachments,
|
|
6233
|
+
maxColorAttachmentBytesPerSample: this._adapter.limits.maxColorAttachmentBytesPerSample,
|
|
6234
|
+
maxComputeWorkgroupStorageSize: this._adapter.limits.maxComputeWorkgroupStorageSize,
|
|
6235
|
+
maxComputeInvocationsPerWorkgroup: this._adapter.limits.maxComputeInvocationsPerWorkgroup,
|
|
6236
|
+
maxComputeWorkgroupsPerDimension: this._adapter.limits.maxComputeWorkgroupsPerDimension
|
|
5819
6237
|
}
|
|
5820
6238
|
});
|
|
5821
6239
|
console.info('WebGPU device features:');
|
|
@@ -5851,6 +6269,7 @@ class WebGPUDevice extends BaseDevice {
|
|
|
5851
6269
|
this._bindGroupCache = new BindGroupCache(this);
|
|
5852
6270
|
this._vertexLayoutCache = new VertexLayoutCache();
|
|
5853
6271
|
this._commandQueue = new CommandQueueImmediate(this);
|
|
6272
|
+
this._timestampQueries = new WebGPUTimestampQueryManager(this);
|
|
5854
6273
|
this._canRender = true;
|
|
5855
6274
|
this.setViewport(null);
|
|
5856
6275
|
this.setScissor(null);
|
|
@@ -5885,6 +6304,21 @@ class WebGPUDevice extends BaseDevice {
|
|
|
5885
6304
|
// throw new Error('not implemented');
|
|
5886
6305
|
return null;
|
|
5887
6306
|
}
|
|
6307
|
+
beginTimestampQuery(label, options) {
|
|
6308
|
+
return this._timestampQueries.begin(label, options);
|
|
6309
|
+
}
|
|
6310
|
+
endTimestampQuery(id) {
|
|
6311
|
+
this._timestampQueries.end(id);
|
|
6312
|
+
}
|
|
6313
|
+
pollTimestampQuery(query) {
|
|
6314
|
+
return this._timestampQueries.poll(query);
|
|
6315
|
+
}
|
|
6316
|
+
resolveTimestampQuery(query) {
|
|
6317
|
+
return this._timestampQueries.resolve(query);
|
|
6318
|
+
}
|
|
6319
|
+
collectTimestampQueries(filter) {
|
|
6320
|
+
return this._timestampQueries.collect(filter);
|
|
6321
|
+
}
|
|
5888
6322
|
createRenderStateSet() {
|
|
5889
6323
|
return new WebGPURenderStateSet(this);
|
|
5890
6324
|
}
|
|
@@ -6300,6 +6734,12 @@ class WebGPUDevice extends BaseDevice {
|
|
|
6300
6734
|
flushUploads() {
|
|
6301
6735
|
this._commandQueue.flushUploads();
|
|
6302
6736
|
}
|
|
6737
|
+
/** @internal */ createTimestampResolveCommandBuffer() {
|
|
6738
|
+
return this._timestampQueries.createResolveCommandBuffer();
|
|
6739
|
+
}
|
|
6740
|
+
/** @internal */ onTimestampCommandBuffersSubmitted() {
|
|
6741
|
+
this._timestampQueries.onSubmitted();
|
|
6742
|
+
}
|
|
6303
6743
|
/** @internal */ _setFramebuffer(rt) {
|
|
6304
6744
|
this._commandQueue.setFramebuffer(rt);
|
|
6305
6745
|
}
|
|
@@ -6312,6 +6752,7 @@ class WebGPUDevice extends BaseDevice {
|
|
|
6312
6752
|
}
|
|
6313
6753
|
}
|
|
6314
6754
|
/** @internal */ onEndFrame() {
|
|
6755
|
+
this._timestampQueries.autoCloseOpenScopes();
|
|
6315
6756
|
this._commandQueue.endFrame();
|
|
6316
6757
|
}
|
|
6317
6758
|
/** @internal */ _draw(primitiveType, first, count) {
|