Skip to content

Commit

Permalink
prototye big buffer
Browse files Browse the repository at this point in the history
refactored and remove global state
  • Loading branch information
aardgoose committed May 16, 2024
1 parent 097d959 commit 4d38a9d
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 20 deletions.
54 changes: 54 additions & 0 deletions examples/jsm/renderers/common/CommonUniformBuffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
class CommonUniformBuffer {

constructor( bufferSize = 0, alignment = 0 ) {

let buffer = null;

if ( bufferSize > 0 ) {

buffer = new Float32Array( bufferSize );

}

// offset in bytes to first free buffer entry

this.startFree = 0;
this.buffer = buffer;
this.aligment = alignment;

}

allocate( byteLength ) {

if ( this.startFree + byteLength > this.byteLength ) {

return false;

}

// uniformGroups within buffer must be aligned correctly per WebGPU spec.
const paddedByteLength = Math.ceil( byteLength / this.aligment ) * this.aligment;
const bpe = this.buffer.BYTES_PER_ELEMENT;
const buffer = this.buffer.subarray( this.startFree / bpe , ( this.startFree + byteLength ) / bpe );

this.startFree += paddedByteLength;

return buffer;

}

get byteLength() {

return this.buffer === null ? 0 : this.buffer.byteLength;

}

get arrayBuffer() {

return this.buffer.buffer;

}

}

export default CommonUniformBuffer;
1 change: 1 addition & 0 deletions examples/jsm/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Renderer {
// nodes

this.toneMappingNode = null;
this.commonBufferSize = 0;

// internals

Expand Down
9 changes: 8 additions & 1 deletion examples/jsm/renderers/common/UniformsGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class UniformsGroup extends UniformBuffer {

this.uniforms = [];

this._buffer = null;
this._byteLength = null;

}

addUniform( uniform ) {
Expand Down Expand Up @@ -57,6 +60,8 @@ class UniformsGroup extends UniformBuffer {

get byteLength() {

if ( this._byteLength !== null ) return this._byteLength;

let offset = 0; // global buffer offset in bytes

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

}

return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;
this._byteLength = Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;

return this._byteLength;

}

Expand Down
45 changes: 44 additions & 1 deletion examples/jsm/renderers/common/nodes/NodeUniformsGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ let id = 0;

class NodeUniformsGroup extends UniformsGroup {

constructor( name, groupNode ) {
constructor( name, groupNode, commonUniformBuffer = null ) {

super( name );

this.id = id ++;
this.groupNode = groupNode;

this.isNodeUniformsGroup = true;
this.commonUniformBuffer = commonUniformBuffer;
this._isCommon = null;

}

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

}

allocateCommon() {

if ( this._isCommon === null ) {

this._isCommon = false;

if ( this.commonUniformBuffer !== null ) {

const buffer = this.commonUniformBuffer.allocate( this.byteLength );

if ( buffer ) {

this._buffer = buffer;
this._isCommon = true;

}

}

}

return this._isCommon;

}

get buffer() {

if ( this._buffer === null ) {

if ( ! this.allocateCommon() ) {

return super.buffer;

}

}

return this._buffer;

}

getNodes() {

const nodes = [];
Expand Down
18 changes: 15 additions & 3 deletions examples/jsm/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat

import WGSLNodeBuilder from './nodes/WGSLNodeBuilder.js';
import Backend from '../common/Backend.js';
import CommonUniformBuffer from '../common/CommonUniformBuffer.js';

import WebGPUUtils from './utils/WebGPUUtils.js';
import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js';
Expand Down Expand Up @@ -55,6 +56,7 @@ class WebGPUBackend extends Backend {
this.pipelineUtils = new WebGPUPipelineUtils( this );
this.textureUtils = new WebGPUTextureUtils( this );
this.occludedResolveCache = new Map();
this.commonUniformBuffer = null;

}

Expand Down Expand Up @@ -505,8 +507,9 @@ class WebGPUBackend extends Backend {

this.prepareTimestampBuffer( renderContext, renderContextData.encoder );

this.device.queue.submit( [ renderContextData.encoder.finish() ] );
this.bindingUtils.endPass();

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

//

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

this.prepareTimestampBuffer( computeGroup, groupData.cmdEncoderGPU );
this.bindingUtils.endPass();

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

Expand Down Expand Up @@ -1117,13 +1121,21 @@ class WebGPUBackend extends Backend {
buffer.unmap();
}
}


// node builder

createNodeBuilder( object, renderer, scene = null ) {

return new WGSLNodeBuilder( object, renderer, scene );
const size = this.renderer.commonBufferSize;

if ( size > 0 && this.commonUniformBuffer === null ) {

this.commonUniformBuffer = new CommonUniformBuffer( 256 * size, this.device.limits.minUniformBufferOffsetAlignment );

}

return new WGSLNodeBuilder( object, renderer, scene, this.commonUniformBuffer );

}

Expand Down
6 changes: 4 additions & 2 deletions examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,16 @@ fn threejs_repeatWrapping( uv : vec2<f32>, dimension : vec2<u32> ) -> vec2<u32>

class WGSLNodeBuilder extends NodeBuilder {

constructor( object, renderer, scene = null ) {
constructor( object, renderer, scene = null, commonUniformBuffer = null ) {

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

this.uniformGroups = {};

this.builtins = {};

this.commonUniformBuffer = commonUniformBuffer;

}

needsColorSpaceToLinear( texture ) {
Expand Down Expand Up @@ -424,7 +426,7 @@ class WGSLNodeBuilder extends NodeBuilder {

if ( uniformsGroup === undefined ) {

uniformsGroup = new NodeUniformsGroup( groupName, group );
uniformsGroup = new NodeUniformsGroup( groupName, group, this.commonUniformBuffer );
uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] );

uniformsStage[ groupName ] = uniformsGroup;
Expand Down
92 changes: 79 additions & 13 deletions examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ class WebGPUBindingUtils {

this.backend = backend;

this.lowwaterMark = Infinity;
this.highwaterMark = 0;

this.commonBufferGPU = null;

}

getCommonBuffer( commonUniformBuffer ) {

let bufferGPU = this.commonBufferGPU;

if ( bufferGPU === null ) {

bufferGPU = this.backend.device.createBuffer( {
label: 'bindingBuffer_common',
size: commonUniformBuffer.byteLength,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
} );

this.commonBufferGPU = bufferGPU;
this.commonUniformBuffer = commonUniformBuffer;

}

return bufferGPU

}

createBindingsLayout( bindings ) {
Expand Down Expand Up @@ -142,10 +168,18 @@ class WebGPUBindingUtils {
const backend = this.backend;
const device = backend.device;

const buffer = binding.buffer;
const bufferGPU = backend.get( binding ).buffer;
if ( binding.isNodeUniformsGroup && binding.allocateCommon() ) {

const buffer = binding.buffer;

device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
this.lowwaterMark = Math.min( this.lowwaterMark, buffer.byteOffset );
this.highwaterMark = Math.max( this.highwaterMark, buffer.byteOffset + buffer.byteLength );

} else {

const bufferGPU = backend.get( binding ).buffer;
device.queue.writeBuffer( bufferGPU, 0, binding.buffer, 0 );
}

}

Expand All @@ -163,23 +197,42 @@ class WebGPUBindingUtils {

const bindingData = backend.get( binding );

if ( bindingData.buffer === undefined ) {
let resource;

const byteLength = binding.byteLength;
if ( binding.isNodeUniformsGroup && binding.allocateCommon() ) {

const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
const buffer = binding.buffer;

const bufferGPU = device.createBuffer( {
label: 'bindingBuffer_' + binding.name,
size: byteLength,
usage: usage
} );
resource = {
label: 'bindingBufferCommon_' + binding.name,
buffer: this.getCommonBuffer( binding.commonUniformBuffer ),
offset: buffer.byteOffset,
size: buffer.byteLength
};

bindingData.buffer = bufferGPU;
} else {

if ( bindingData.buffer === undefined ) {

const byteLength = binding.byteLength;

const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;

const bufferGPU = device.createBuffer( {
label: 'bindingBuffer_' + binding.name,
size: byteLength,
usage: usage
} );

bindingData.buffer = bufferGPU;

}

resource = { buffer: bindingData.buffer };

}

entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
entriesGPU.push( { binding: bindingPoint, resource } );

} else if ( binding.isStorageBuffer ) {

Expand Down Expand Up @@ -253,6 +306,19 @@ class WebGPUBindingUtils {

}

endPass() {

if ( this.commonBufferGPU === null || this.lowwaterMark === Infinity ) return;

const device = this.backend.device;

device.queue.writeBuffer( this.commonBufferGPU, this.lowwaterMark, this.commonUniformBuffer.arrayBuffer, this.lowwaterMark, this.highwaterMark - this.lowwaterMark );

this.lowwaterMark = Infinity;
this.highwaterMark = 0;

}

}

export default WebGPUBindingUtils;

0 comments on commit 4d38a9d

Please sign in to comment.