Skip to content

Commit d0308b4

Browse files
WebGPURenderer: Eliminate remaining per-frame allocations.
- beginCompute: cache pass descriptor and label; avoid per-dispatch map/join. - clear: drop unused colorAttachments array literal. - beginRender: cache occlusionQuerySet descriptor across frames. - _getRenderPassDescriptor: reuse scratch view descriptors instead of fresh literals. - _createDepthLayerDescriptors: reuse per-layer descriptors in place; truncate stale depth views and layer descriptors when cameras.length shrinks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3282f4f commit d0308b4

1 file changed

Lines changed: 131 additions & 63 deletions

File tree

src/renderers/webgpu/WebGPUBackend.js

Lines changed: 131 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ const _indirectOffsets = [ 0 ];
4444
const _bundles = [];
4545
const _singleBundleArray = [ null ];
4646

47+
const _viewDescriptor = { label: '', baseMipLevel: 0, mipLevelCount: 1, baseArrayLayer: 0, arrayLayerCount: 1, dimension: null, depthOrArrayLayers: undefined };
48+
const _depthViewOptions = { dimension: undefined, arrayLayerCount: undefined, baseArrayLayer: undefined };
49+
4750
function _resetCurrentSets( sets ) {
4851

4952
for ( const key in sets.attributes ) delete sets.attributes[ key ];
@@ -531,22 +534,21 @@ class WebGPUBackend extends Backend {
531534

532535
const textureData = this.get( textures[ i ] );
533536

534-
const viewDescriptor = {
535-
label: `colorAttachment_${ i }`,
536-
baseMipLevel: renderContext.activeMipmapLevel,
537-
mipLevelCount: 1,
538-
baseArrayLayer: renderContext.activeCubeFace,
539-
arrayLayerCount: 1,
540-
dimension: GPUTextureViewDimension.TwoD
541-
};
537+
_viewDescriptor.label = 'colorAttachment_' + i;
538+
_viewDescriptor.baseMipLevel = renderContext.activeMipmapLevel;
539+
_viewDescriptor.mipLevelCount = 1;
540+
_viewDescriptor.baseArrayLayer = renderContext.activeCubeFace;
541+
_viewDescriptor.arrayLayerCount = 1;
542+
_viewDescriptor.dimension = GPUTextureViewDimension.TwoD;
543+
_viewDescriptor.depthOrArrayLayers = undefined;
542544

543545
if ( renderTarget.isRenderTarget3D ) {
544546

545547
sliceIndex = renderContext.activeCubeFace;
546548

547-
viewDescriptor.baseArrayLayer = 0;
548-
viewDescriptor.dimension = GPUTextureViewDimension.ThreeD;
549-
viewDescriptor.depthOrArrayLayers = textures[ i ].image.depth;
549+
_viewDescriptor.baseArrayLayer = 0;
550+
_viewDescriptor.dimension = GPUTextureViewDimension.ThreeD;
551+
_viewDescriptor.depthOrArrayLayers = textures[ i ].image.depth;
550552

551553
} else if ( renderTarget.isRenderTarget && textures[ i ].image.depth > 1 ) {
552554

@@ -555,13 +557,12 @@ class WebGPUBackend extends Backend {
555557
const cameras = renderContext.camera.cameras;
556558
for ( let layer = 0; layer < cameras.length; layer ++ ) {
557559

558-
const layerViewDescriptor = {
559-
...viewDescriptor,
560-
baseArrayLayer: layer,
561-
arrayLayerCount: 1,
562-
dimension: GPUTextureViewDimension.TwoD
563-
};
564-
const textureView = textureData.texture.createView( layerViewDescriptor );
560+
_viewDescriptor.baseArrayLayer = layer;
561+
_viewDescriptor.arrayLayerCount = 1;
562+
_viewDescriptor.dimension = GPUTextureViewDimension.TwoD;
563+
_viewDescriptor.depthOrArrayLayers = undefined;
564+
565+
const textureView = textureData.texture.createView( _viewDescriptor );
565566
textureViews.push( {
566567
view: textureView,
567568
resolveTarget: undefined,
@@ -572,16 +573,16 @@ class WebGPUBackend extends Backend {
572573

573574
} else {
574575

575-
viewDescriptor.dimension = GPUTextureViewDimension.TwoDArray;
576-
viewDescriptor.depthOrArrayLayers = textures[ i ].image.depth;
576+
_viewDescriptor.dimension = GPUTextureViewDimension.TwoDArray;
577+
_viewDescriptor.depthOrArrayLayers = textures[ i ].image.depth;
577578

578579
}
579580

580581
}
581582

582583
if ( isRenderCameraDepthArray !== true ) {
583584

584-
const textureView = textureData.texture.createView( viewDescriptor );
585+
const textureView = textureData.texture.createView( _viewDescriptor );
585586

586587
let view, resolveTarget;
587588

@@ -612,16 +613,22 @@ class WebGPUBackend extends Backend {
612613
if ( renderContext.depth ) {
613614

614615
const depthTextureData = this.get( renderContext.depthTexture );
615-
const options = {};
616+
617+
// Reset then apply only when needed so createView() sees undefined for
618+
// unset fields rather than leftovers from a prior call.
619+
_depthViewOptions.dimension = undefined;
620+
_depthViewOptions.arrayLayerCount = undefined;
621+
_depthViewOptions.baseArrayLayer = undefined;
622+
616623
if ( renderContext.depthTexture.isArrayTexture || renderContext.depthTexture.isCubeTexture ) {
617624

618-
options.dimension = GPUTextureViewDimension.TwoD;
619-
options.arrayLayerCount = 1;
620-
options.baseArrayLayer = renderContext.activeCubeFace;
625+
_depthViewOptions.dimension = GPUTextureViewDimension.TwoD;
626+
_depthViewOptions.arrayLayerCount = 1;
627+
_depthViewOptions.baseArrayLayer = renderContext.activeCubeFace;
621628

622629
}
623630

624-
descriptorBase.depthStencilView = depthTextureData.texture.createView( options );
631+
descriptorBase.depthStencilView = depthTextureData.texture.createView( _depthViewOptions );
625632

626633
}
627634

@@ -724,7 +731,14 @@ class WebGPUBackend extends Backend {
724731

725732
//
726733

727-
occlusionQuerySet = device.createQuerySet( { type: 'occlusion', count: occlusionQueryCount, label: `occlusionQuerySet_${ renderContext.id }` } );
734+
if ( renderContextData.occlusionQuerySetDescriptor === undefined ) {
735+
736+
renderContextData.occlusionQuerySetDescriptor = { type: 'occlusion', count: 0, label: 'occlusionQuerySet_' + renderContext.id };
737+
738+
}
739+
740+
renderContextData.occlusionQuerySetDescriptor.count = occlusionQueryCount;
741+
occlusionQuerySet = device.createQuerySet( renderContextData.occlusionQuerySetDescriptor );
728742

729743
renderContextData.occlusionQuerySet = occlusionQuerySet;
730744
renderContextData.occlusionQueryIndex = 0;
@@ -974,7 +988,15 @@ class WebGPUBackend extends Backend {
974988
_createDepthLayerDescriptors( renderContext, renderContextData, descriptor, cameras ) {
975989

976990
const depthStencilAttachment = descriptor.depthStencilAttachment;
977-
renderContextData.layerDescriptors = [];
991+
const baseColorAttachment = descriptor.colorAttachments[ 0 ];
992+
993+
if ( renderContextData.layerDescriptors === undefined ) {
994+
995+
renderContextData.layerDescriptors = [];
996+
997+
}
998+
999+
const layerDescriptors = renderContextData.layerDescriptors;
9781000

9791001
const depthTextureData = this.get( renderContext.depthTexture );
9801002
if ( ! depthTextureData.viewCache ) {
@@ -985,53 +1007,94 @@ class WebGPUBackend extends Backend {
9851007

9861008
for ( let i = 0; i < cameras.length; i ++ ) {
9871009

988-
const layerDescriptor = {
989-
...descriptor,
990-
colorAttachments: [ {
991-
...descriptor.colorAttachments[ 0 ],
992-
view: descriptor.colorAttachments[ i ].view
993-
} ]
994-
};
1010+
let layerDescriptor = layerDescriptors[ i ];
9951011

996-
if ( descriptor.depthStencilAttachment ) {
1012+
if ( layerDescriptor === undefined ) {
9971013

998-
const layerIndex = i;
1014+
layerDescriptor = { colorAttachments: [ {} ] };
1015+
layerDescriptors[ i ] = layerDescriptor;
9991016

1000-
if ( ! depthTextureData.viewCache[ layerIndex ] ) {
1017+
}
10011018

1002-
depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( {
1003-
dimension: GPUTextureViewDimension.TwoD,
1004-
baseArrayLayer: i,
1005-
arrayLayerCount: 1
1006-
} );
1019+
// Propagate base descriptor fields that aren't color/depth-stencil.
1020+
layerDescriptor.occlusionQuerySet = descriptor.occlusionQuerySet;
1021+
layerDescriptor.timestampWrites = descriptor.timestampWrites;
1022+
1023+
// Single color attachment per layer, with this layer's view.
1024+
const colorAttachment = layerDescriptor.colorAttachments[ 0 ];
1025+
colorAttachment.view = descriptor.colorAttachments[ i ].view;
1026+
colorAttachment.depthSlice = baseColorAttachment.depthSlice;
1027+
colorAttachment.resolveTarget = baseColorAttachment.resolveTarget;
1028+
colorAttachment.loadOp = baseColorAttachment.loadOp;
1029+
colorAttachment.storeOp = baseColorAttachment.storeOp;
1030+
colorAttachment.clearValue = baseColorAttachment.clearValue;
1031+
1032+
if ( depthStencilAttachment ) {
1033+
1034+
if ( ! depthTextureData.viewCache[ i ] ) {
1035+
1036+
_depthViewOptions.dimension = GPUTextureViewDimension.TwoD;
1037+
_depthViewOptions.baseArrayLayer = i;
1038+
_depthViewOptions.arrayLayerCount = 1;
1039+
1040+
depthTextureData.viewCache[ i ] = depthTextureData.texture.createView( _depthViewOptions );
10071041

10081042
}
10091043

1010-
layerDescriptor.depthStencilAttachment = {
1011-
view: depthTextureData.viewCache[ layerIndex ],
1012-
depthLoadOp: depthStencilAttachment.depthLoadOp || GPULoadOp.Clear,
1013-
depthStoreOp: depthStencilAttachment.depthStoreOp || GPUStoreOp.Store,
1014-
depthClearValue: depthStencilAttachment.depthClearValue || 1.0
1015-
};
1044+
let dsa = layerDescriptor.depthStencilAttachment;
1045+
1046+
if ( dsa === undefined ) {
1047+
1048+
dsa = {};
1049+
layerDescriptor.depthStencilAttachment = dsa;
1050+
1051+
}
1052+
1053+
dsa.view = depthTextureData.viewCache[ i ];
1054+
dsa.depthLoadOp = depthStencilAttachment.depthLoadOp || GPULoadOp.Clear;
1055+
dsa.depthStoreOp = depthStencilAttachment.depthStoreOp || GPUStoreOp.Store;
1056+
dsa.depthClearValue = depthStencilAttachment.depthClearValue || 1.0;
10161057

10171058
if ( renderContext.stencil ) {
10181059

1019-
layerDescriptor.depthStencilAttachment.stencilLoadOp = depthStencilAttachment.stencilLoadOp;
1020-
layerDescriptor.depthStencilAttachment.stencilStoreOp = depthStencilAttachment.stencilStoreOp;
1021-
layerDescriptor.depthStencilAttachment.stencilClearValue = depthStencilAttachment.stencilClearValue;
1060+
dsa.stencilLoadOp = depthStencilAttachment.stencilLoadOp;
1061+
dsa.stencilStoreOp = depthStencilAttachment.stencilStoreOp;
1062+
dsa.stencilClearValue = depthStencilAttachment.stencilClearValue;
1063+
1064+
} else {
1065+
1066+
dsa.stencilLoadOp = undefined;
1067+
dsa.stencilStoreOp = undefined;
1068+
dsa.stencilClearValue = undefined;
10221069

10231070
}
10241071

10251072
} else {
10261073

1027-
layerDescriptor.depthStencilAttachment = { ...depthStencilAttachment };
1074+
// Preserve prior behavior of spreading `depthStencilAttachment` when falsy (results in an empty object).
1075+
let dsa = layerDescriptor.depthStencilAttachment;
10281076

1029-
}
1077+
if ( dsa === undefined ) {
10301078

1031-
renderContextData.layerDescriptors.push( layerDescriptor );
1079+
dsa = {};
1080+
layerDescriptor.depthStencilAttachment = dsa;
1081+
1082+
} else {
1083+
1084+
for ( const key in dsa ) delete dsa[ key ];
1085+
1086+
}
1087+
1088+
}
10321089

10331090
}
10341091

1092+
// Drop stale entries if cameras.length shrank.
1093+
if ( layerDescriptors.length > cameras.length ) layerDescriptors.length = cameras.length;
1094+
1095+
// Also drop stale cached depth views beyond current camera count.
1096+
if ( depthTextureData.viewCache.length > cameras.length ) depthTextureData.viewCache.length = cameras.length;
1097+
10351098
}
10361099

10371100
/**
@@ -1360,7 +1423,7 @@ class WebGPUBackend extends Backend {
13601423
const device = this.device;
13611424
const renderer = this.renderer;
13621425

1363-
let colorAttachments = [];
1426+
let colorAttachments;
13641427
let depthStencilAttachment;
13651428

13661429
let supportsDepth;
@@ -1503,24 +1566,29 @@ class WebGPUBackend extends Backend {
15031566
beginCompute( computeGroup ) {
15041567

15051568
const groupGPU = this.get( computeGroup );
1506-
const isComputeGroup = Array.isArray( computeGroup ) === true;
1507-
const computeGroupId = isComputeGroup ? computeGroup.map( computeNode => computeNode.id ).join( '_' ) : computeGroup.id;
15081569
const traceComputeSteps = groupGPU.traceComputeSteps === true;
15091570

15101571
if ( groupGPU.encoderOptions === undefined ) {
15111572

1512-
groupGPU.encoderOptions = { label: 'computeGroup_' + computeGroupId };
1573+
const isComputeGroup = Array.isArray( computeGroup ) === true;
1574+
const computeGroupId = isComputeGroup ? computeGroup.map( computeNode => computeNode.id ).join( '_' ) : computeGroup.id;
1575+
const label = 'computeGroup_' + computeGroupId;
1576+
1577+
groupGPU.encoderOptions = { label };
1578+
groupGPU.passDescriptor = { label };
1579+
groupGPU.isComputeGroup = isComputeGroup;
15131580

15141581
}
15151582

15161583
groupGPU.cmdEncoderGPU = this.device.createCommandEncoder( groupGPU.encoderOptions );
15171584
groupGPU.passEncoderGPU = null;
15181585

1519-
if ( isComputeGroup === false || traceComputeSteps === false ) {
1586+
if ( groupGPU.isComputeGroup === false || traceComputeSteps === false ) {
15201587

1521-
const descriptor = {
1522-
label: 'computeGroup_' + computeGroupId
1523-
};
1588+
const descriptor = groupGPU.passDescriptor;
1589+
1590+
// Clear properties that may have been set by initTimestampQuery on a previous call.
1591+
descriptor.timestampWrites = undefined;
15241592

15251593
this.initTimestampQuery( TimestampQuery.COMPUTE, this.getTimestampUID( computeGroup ), descriptor );
15261594

0 commit comments

Comments
 (0)