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 Jan 11, 2024
1 parent 66aa8ff commit 1bc9388
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 19 deletions.
53 changes: 53 additions & 0 deletions examples/jsm/renderers/common/CommonUniformBuffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

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 + length > 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;
2 changes: 2 additions & 0 deletions examples/jsm/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class Renderer {

this.info = new Info();

this.commonBufferSize = 0;

// internals

this._pixelRatio = 1;
Expand Down
10 changes: 8 additions & 2 deletions 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 @@ -85,12 +90,13 @@ class UniformsGroup extends UniformBuffer {
}

uniform.offset = ( offset / this.bytesPerElement );

offset += ( uniform.itemSize * this.bytesPerElement );

}

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
24 changes: 23 additions & 1 deletion examples/jsm/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,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 { DepthFormat, WebGPUCoordinateSystem } from 'three';

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 @@ -494,8 +496,12 @@ class WebGPUBackend extends Backend {

}

this.bindingUtils.endPass();

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

//this.device.queue.onSubmittedWorkDone().then( () => { performance.mark( 'render-end' ); } );

//

if ( renderContext.textures !== null ) {
Expand Down Expand Up @@ -770,6 +776,9 @@ class WebGPUBackend extends Backend {
const groupData = this.get( computeGroup );

groupData.passEncoderGPU.end();

this.bindingUtils.endPass();

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

}
Expand Down Expand Up @@ -1035,7 +1044,20 @@ class WebGPUBackend extends Backend {

createNodeBuilder( object, renderer, scene = null ) {

return new WGSLNodeBuilder( object, renderer, scene );
if ( this.commonUniformBuffer === null ) {

const alignment = this.device.limits.minUniformBufferOffsetAlignment;
const size = this.renderer.commonBufferSize;

if ( size > 0 ) {

this.commonUniformBuffer = new CommonUniformBuffer( 256 * size, alignment );

}

}

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 @@ -118,14 +118,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 @@ -391,7 +393,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 @@ -128,10 +154,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 @@ -149,23 +183,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 @@ -239,6 +292,19 @@ class WebGPUBindingUtils {

}

endPass() {

const device = this.backend.device;

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

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

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

}

}

export default WebGPUBindingUtils;

0 comments on commit 1bc9388

Please sign in to comment.