Skip to content

Commit 4d38a9d

Browse files
author
aardgoose
committed
prototye big buffer
refactored and remove global state
1 parent 097d959 commit 4d38a9d

File tree

7 files changed

+205
-20
lines changed

7 files changed

+205
-20
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
class CommonUniformBuffer {
2+
3+
constructor( bufferSize = 0, alignment = 0 ) {
4+
5+
let buffer = null;
6+
7+
if ( bufferSize > 0 ) {
8+
9+
buffer = new Float32Array( bufferSize );
10+
11+
}
12+
13+
// offset in bytes to first free buffer entry
14+
15+
this.startFree = 0;
16+
this.buffer = buffer;
17+
this.aligment = alignment;
18+
19+
}
20+
21+
allocate( byteLength ) {
22+
23+
if ( this.startFree + byteLength > this.byteLength ) {
24+
25+
return false;
26+
27+
}
28+
29+
// uniformGroups within buffer must be aligned correctly per WebGPU spec.
30+
const paddedByteLength = Math.ceil( byteLength / this.aligment ) * this.aligment;
31+
const bpe = this.buffer.BYTES_PER_ELEMENT;
32+
const buffer = this.buffer.subarray( this.startFree / bpe , ( this.startFree + byteLength ) / bpe );
33+
34+
this.startFree += paddedByteLength;
35+
36+
return buffer;
37+
38+
}
39+
40+
get byteLength() {
41+
42+
return this.buffer === null ? 0 : this.buffer.byteLength;
43+
44+
}
45+
46+
get arrayBuffer() {
47+
48+
return this.buffer.buffer;
49+
50+
}
51+
52+
}
53+
54+
export default CommonUniformBuffer;

examples/jsm/renderers/common/Renderer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class Renderer {
6969
// nodes
7070

7171
this.toneMappingNode = null;
72+
this.commonBufferSize = 0;
7273

7374
// internals
7475

examples/jsm/renderers/common/UniformsGroup.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ class UniformsGroup extends UniformBuffer {
1313

1414
this.uniforms = [];
1515

16+
this._buffer = null;
17+
this._byteLength = null;
18+
1619
}
1720

1821
addUniform( uniform ) {
@@ -57,6 +60,8 @@ class UniformsGroup extends UniformBuffer {
5760

5861
get byteLength() {
5962

63+
if ( this._byteLength !== null ) return this._byteLength;
64+
6065
let offset = 0; // global buffer offset in bytes
6166

6267
for ( let i = 0, l = this.uniforms.length; i < l; i ++ ) {
@@ -92,7 +97,9 @@ class UniformsGroup extends UniformBuffer {
9297

9398
}
9499

95-
return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;
100+
this._byteLength = Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;
101+
102+
return this._byteLength;
96103

97104
}
98105

examples/jsm/renderers/common/nodes/NodeUniformsGroup.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ let id = 0;
44

55
class NodeUniformsGroup extends UniformsGroup {
66

7-
constructor( name, groupNode ) {
7+
constructor( name, groupNode, commonUniformBuffer = null ) {
88

99
super( name );
1010

1111
this.id = id ++;
1212
this.groupNode = groupNode;
1313

1414
this.isNodeUniformsGroup = true;
15+
this.commonUniformBuffer = commonUniformBuffer;
16+
this._isCommon = null;
1517

1618
}
1719

@@ -21,6 +23,47 @@ class NodeUniformsGroup extends UniformsGroup {
2123

2224
}
2325

26+
allocateCommon() {
27+
28+
if ( this._isCommon === null ) {
29+
30+
this._isCommon = false;
31+
32+
if ( this.commonUniformBuffer !== null ) {
33+
34+
const buffer = this.commonUniformBuffer.allocate( this.byteLength );
35+
36+
if ( buffer ) {
37+
38+
this._buffer = buffer;
39+
this._isCommon = true;
40+
41+
}
42+
43+
}
44+
45+
}
46+
47+
return this._isCommon;
48+
49+
}
50+
51+
get buffer() {
52+
53+
if ( this._buffer === null ) {
54+
55+
if ( ! this.allocateCommon() ) {
56+
57+
return super.buffer;
58+
59+
}
60+
61+
}
62+
63+
return this._buffer;
64+
65+
}
66+
2467
getNodes() {
2568

2669
const nodes = [];

examples/jsm/renderers/webgpu/WebGPUBackend.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat
88

99
import WGSLNodeBuilder from './nodes/WGSLNodeBuilder.js';
1010
import Backend from '../common/Backend.js';
11+
import CommonUniformBuffer from '../common/CommonUniformBuffer.js';
1112

1213
import WebGPUUtils from './utils/WebGPUUtils.js';
1314
import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js';
@@ -55,6 +56,7 @@ class WebGPUBackend extends Backend {
5556
this.pipelineUtils = new WebGPUPipelineUtils( this );
5657
this.textureUtils = new WebGPUTextureUtils( this );
5758
this.occludedResolveCache = new Map();
59+
this.commonUniformBuffer = null;
5860

5961
}
6062

@@ -505,8 +507,9 @@ class WebGPUBackend extends Backend {
505507

506508
this.prepareTimestampBuffer( renderContext, renderContextData.encoder );
507509

508-
this.device.queue.submit( [ renderContextData.encoder.finish() ] );
510+
this.bindingUtils.endPass();
509511

512+
this.device.queue.submit( [ renderContextData.encoder.finish() ] );
510513

511514
//
512515

@@ -775,6 +778,7 @@ class WebGPUBackend extends Backend {
775778
groupData.passEncoderGPU.end();
776779

777780
this.prepareTimestampBuffer( computeGroup, groupData.cmdEncoderGPU );
781+
this.bindingUtils.endPass();
778782

779783
this.device.queue.submit( [ groupData.cmdEncoderGPU.finish() ] );
780784

@@ -1117,13 +1121,21 @@ class WebGPUBackend extends Backend {
11171121
buffer.unmap();
11181122
}
11191123
}
1120-
1124+
11211125

11221126
// node builder
11231127

11241128
createNodeBuilder( object, renderer, scene = null ) {
11251129

1126-
return new WGSLNodeBuilder( object, renderer, scene );
1130+
const size = this.renderer.commonBufferSize;
1131+
1132+
if ( size > 0 && this.commonUniformBuffer === null ) {
1133+
1134+
this.commonUniformBuffer = new CommonUniformBuffer( 256 * size, this.device.limits.minUniformBufferOffsetAlignment );
1135+
1136+
}
1137+
1138+
return new WGSLNodeBuilder( object, renderer, scene, this.commonUniformBuffer );
11271139

11281140
}
11291141

examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,16 @@ fn threejs_repeatWrapping( uv : vec2<f32>, dimension : vec2<u32> ) -> vec2<u32>
130130

131131
class WGSLNodeBuilder extends NodeBuilder {
132132

133-
constructor( object, renderer, scene = null ) {
133+
constructor( object, renderer, scene = null, commonUniformBuffer = null ) {
134134

135135
super( object, renderer, new WGSLNodeParser(), scene );
136136

137137
this.uniformGroups = {};
138138

139139
this.builtins = {};
140140

141+
this.commonUniformBuffer = commonUniformBuffer;
142+
141143
}
142144

143145
needsColorSpaceToLinear( texture ) {
@@ -424,7 +426,7 @@ class WGSLNodeBuilder extends NodeBuilder {
424426

425427
if ( uniformsGroup === undefined ) {
426428

427-
uniformsGroup = new NodeUniformsGroup( groupName, group );
429+
uniformsGroup = new NodeUniformsGroup( groupName, group, this.commonUniformBuffer );
428430
uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] );
429431

430432
uniformsStage[ groupName ] = uniformsGroup;

examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,32 @@ class WebGPUBindingUtils {
99

1010
this.backend = backend;
1111

12+
this.lowwaterMark = Infinity;
13+
this.highwaterMark = 0;
14+
15+
this.commonBufferGPU = null;
16+
17+
}
18+
19+
getCommonBuffer( commonUniformBuffer ) {
20+
21+
let bufferGPU = this.commonBufferGPU;
22+
23+
if ( bufferGPU === null ) {
24+
25+
bufferGPU = this.backend.device.createBuffer( {
26+
label: 'bindingBuffer_common',
27+
size: commonUniformBuffer.byteLength,
28+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
29+
} );
30+
31+
this.commonBufferGPU = bufferGPU;
32+
this.commonUniformBuffer = commonUniformBuffer;
33+
34+
}
35+
36+
return bufferGPU
37+
1238
}
1339

1440
createBindingsLayout( bindings ) {
@@ -142,10 +168,18 @@ class WebGPUBindingUtils {
142168
const backend = this.backend;
143169
const device = backend.device;
144170

145-
const buffer = binding.buffer;
146-
const bufferGPU = backend.get( binding ).buffer;
171+
if ( binding.isNodeUniformsGroup && binding.allocateCommon() ) {
172+
173+
const buffer = binding.buffer;
147174

148-
device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
175+
this.lowwaterMark = Math.min( this.lowwaterMark, buffer.byteOffset );
176+
this.highwaterMark = Math.max( this.highwaterMark, buffer.byteOffset + buffer.byteLength );
177+
178+
} else {
179+
180+
const bufferGPU = backend.get( binding ).buffer;
181+
device.queue.writeBuffer( bufferGPU, 0, binding.buffer, 0 );
182+
}
149183

150184
}
151185

@@ -163,23 +197,42 @@ class WebGPUBindingUtils {
163197

164198
const bindingData = backend.get( binding );
165199

166-
if ( bindingData.buffer === undefined ) {
200+
let resource;
167201

168-
const byteLength = binding.byteLength;
202+
if ( binding.isNodeUniformsGroup && binding.allocateCommon() ) {
169203

170-
const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
204+
const buffer = binding.buffer;
171205

172-
const bufferGPU = device.createBuffer( {
173-
label: 'bindingBuffer_' + binding.name,
174-
size: byteLength,
175-
usage: usage
176-
} );
206+
resource = {
207+
label: 'bindingBufferCommon_' + binding.name,
208+
buffer: this.getCommonBuffer( binding.commonUniformBuffer ),
209+
offset: buffer.byteOffset,
210+
size: buffer.byteLength
211+
};
177212

178-
bindingData.buffer = bufferGPU;
213+
} else {
214+
215+
if ( bindingData.buffer === undefined ) {
216+
217+
const byteLength = binding.byteLength;
218+
219+
const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
220+
221+
const bufferGPU = device.createBuffer( {
222+
label: 'bindingBuffer_' + binding.name,
223+
size: byteLength,
224+
usage: usage
225+
} );
226+
227+
bindingData.buffer = bufferGPU;
228+
229+
}
230+
231+
resource = { buffer: bindingData.buffer };
179232

180233
}
181234

182-
entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
235+
entriesGPU.push( { binding: bindingPoint, resource } );
183236

184237
} else if ( binding.isStorageBuffer ) {
185238

@@ -253,6 +306,19 @@ class WebGPUBindingUtils {
253306

254307
}
255308

309+
endPass() {
310+
311+
if ( this.commonBufferGPU === null || this.lowwaterMark === Infinity ) return;
312+
313+
const device = this.backend.device;
314+
315+
device.queue.writeBuffer( this.commonBufferGPU, this.lowwaterMark, this.commonUniformBuffer.arrayBuffer, this.lowwaterMark, this.highwaterMark - this.lowwaterMark );
316+
317+
this.lowwaterMark = Infinity;
318+
this.highwaterMark = 0;
319+
320+
}
321+
256322
}
257323

258324
export default WebGPUBindingUtils;

0 commit comments

Comments
 (0)