Skip to content

Commit

Permalink
Optimized the creation of CommandEncoders on WebGPU (#7037)
Browse files Browse the repository at this point in the history
* Optimized the creation of CommandEncoders on WebGPU

* lint

---------

Co-authored-by: Martin Valigursky <[email protected]>
  • Loading branch information
mvaligursky and Martin Valigursky authored Oct 15, 2024
1 parent 6e997f2 commit 2f0b136
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 87 deletions.
98 changes: 38 additions & 60 deletions src/platform/graphics/webgpu/webgpu-graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,6 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
/** @type {WebgpuRenderTarget} */
const wrt = rt.impl;

// create a new encoder for each pass
this.commandEncoder = this.wgpu.createCommandEncoder();
DebugHelper.setLabel(this.commandEncoder, `${renderPass.name}-CommandEncoder RT:${rt.name}`);

// framebuffer is initialized at the start of the frame
if (rt !== this.backBuffer) {
this.initRenderTarget(rt);
Expand All @@ -720,7 +716,8 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
this.setupTimeStampWrites(renderPassDesc, renderPass.name);

// start the pass
this.passEncoder = this.commandEncoder.beginRenderPass(renderPassDesc);
const commandEncoder = this.getCommandEncoder();
this.passEncoder = commandEncoder.beginRenderPass(renderPassDesc);
this.passEncoder.label = `${renderPass.name}-PassEncoder RT:${rt.name}`;

// push marker to the passEncoder
Expand Down Expand Up @@ -782,13 +779,6 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
}
}

// schedule command buffer submission
const cb = this.commandEncoder.finish();
DebugHelper.setLabel(cb, `${renderPass.name}-CommandBuffer`);

this.addCommandBuffer(cb);
this.commandEncoder = null;

WebgpuDebug.end(this, { renderPass });
WebgpuDebug.end(this, { renderPass });
}
Expand All @@ -798,18 +788,15 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
WebgpuDebug.internal(this);
WebgpuDebug.validate(this);

// create a new encoder for each pass
this.commandEncoder = this.wgpu.createCommandEncoder();
DebugHelper.setLabel(this.commandEncoder, `${name}-ComputePass-Encoder`);

// clear cached encoder state
this.pipeline = null;

// timestamp
const computePassDesc = this.setupTimeStampWrites(undefined, name);

// start the pass
this.passEncoder = this.commandEncoder.beginComputePass(computePassDesc);
const commandEncoder = this.getCommandEncoder();
this.passEncoder = commandEncoder.beginComputePass(computePassDesc);
DebugHelper.setLabel(this.passEncoder, `ComputePass-${name}`);

Debug.assert(!this.insideRenderPass, 'ComputePass cannot be started while inside another pass.');
Expand All @@ -826,14 +813,6 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
// each render pass can use different number of bind groups
this.bindGroupFormats.length = 0;

// schedule command buffer submission
const cb = this.commandEncoder.finish();
// DebugHelper.setLabel(cb, `${renderPass.name}-CommandBuffer`);
DebugHelper.setLabel(cb, 'ComputePass-CommandBuffer');

this.addCommandBuffer(cb);
this.commandEncoder = null;

WebgpuDebug.end(this);
WebgpuDebug.end(this);
}
Expand All @@ -858,6 +837,33 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
this.endComputePass();
}

getCommandEncoder() {

// use existing or create new encoder
let commandEncoder = this.commandEncoder;
if (!commandEncoder) {
commandEncoder = this.wgpu.createCommandEncoder();
DebugHelper.setLabel(commandEncoder, 'CommandEncoder-Shared');

this.commandEncoder = commandEncoder;
}

return commandEncoder;
}

endCommandEncoder() {

const { commandEncoder } = this;
if (commandEncoder) {

const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'CommandBuffer-Shared');

this.addCommandBuffer(cb);
this.commandEncoder = null;
}
}

addCommandBuffer(commandBuffer, front = false) {
if (front) {
this.commandBuffers.unshift(commandBuffer);
Expand All @@ -867,6 +873,10 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
}

submit() {

// end the current encoder
this.endCommandEncoder();

if (this.commandBuffers.length > 0) {

// copy dynamic buffers data to the GPU (this schedules the copy CB to run before all other CBs)
Expand Down Expand Up @@ -944,18 +954,8 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
*/
clearStorageBuffer(storageBuffer, offset = 0, size = storageBuffer.byteSize) {

// use existing or create new encoder
const commandEncoder = this.commandEncoder ?? this.wgpu.createCommandEncoder();

const commandEncoder = this.getCommandEncoder();
commandEncoder.clearBuffer(storageBuffer.buffer, offset, size);

// if we created the encoder
if (!this.commandEncoder) {
DebugHelper.setLabel(commandEncoder, 'ReadStorageBuffer-Encoder');
const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'ReadStorageBuffer-CommandBuffer');
this.addCommandBuffer(cb);
}
}

/**
Expand Down Expand Up @@ -983,20 +983,10 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
stagingBuffer.allocate(this, size);
const destBuffer = stagingBuffer.buffer;

// use existing or create new encoder
const commandEncoder = this.commandEncoder ?? this.wgpu.createCommandEncoder();

// copy the GPU buffer to the staging buffer
const commandEncoder = this.getCommandEncoder();
commandEncoder.copyBufferToBuffer(storageBuffer.buffer, offset, destBuffer, 0, size);

// if we created new encoder
if (!this.commandEncoder) {
DebugHelper.setLabel(commandEncoder, 'ReadStorageBuffer-Encoder');
const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'ReadStorageBuffer-CommandBuffer');
this.addCommandBuffer(cb);
}

return this.readBuffer(stagingBuffer, size, data, immediate);
}

Expand Down Expand Up @@ -1075,8 +1065,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
depthOrArrayLayers: 1
};

// use existing or create new encoder if not in a render pass
const commandEncoder = this.commandEncoder ?? this.wgpu.createCommandEncoder();
const commandEncoder = this.getCommandEncoder();

DebugGraphics.pushGpuMarker(this, 'COPY-RT');

Expand Down Expand Up @@ -1136,17 +1125,6 @@ class WebgpuGraphicsDevice extends GraphicsDevice {

DebugGraphics.popGpuMarker(this);

// if we created the encoder
if (!this.commandEncoder) {

DebugHelper.setLabel(commandEncoder, 'CopyRenderTarget-Encoder');

// copy operation runs next
const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'CopyRenderTarget-CommandBuffer');
this.addCommandBuffer(cb);
}

return true;
}

Expand Down
11 changes: 1 addition & 10 deletions src/platform/graphics/webgpu/webgpu-mipmap-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ class WebgpuMipmapRenderer {
}

// loop through each mip level and render the previous level's contents into it.
const commandEncoder = device.commandEncoder ?? wgpu.createCommandEncoder();
DebugHelper.setLabel(commandEncoder, 'MipmapRendererEncoder');
const commandEncoder = device.getCommandEncoder();

DebugGraphics.pushGpuMarker(device, 'MIPMAP-RENDERER');

Expand Down Expand Up @@ -171,14 +170,6 @@ class WebgpuMipmapRenderer {

DebugGraphics.popGpuMarker(device);

// submit the encoded commands if we created the encoder
if (!device.commandEncoder) {

const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'MipmapRenderer-CommandBuffer');
device.addCommandBuffer(cb);
}

// clear invalidated state
device.pipeline = null;
}
Expand Down
7 changes: 1 addition & 6 deletions src/platform/graphics/webgpu/webgpu-query-set.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ class WebgpuQuerySet {

resolve(count) {
const device = this.device;
const commandEncoder = device.wgpu.createCommandEncoder();
DebugHelper.setLabel(commandEncoder, 'ResolveQuerySet-Encoder');
const commandEncoder = device.getCommandEncoder();

// copy times to the gpu buffer
commandEncoder.resolveQuerySet(this.querySet, 0, count, this.queryBuffer, 0);
Expand All @@ -79,10 +78,6 @@ class WebgpuQuerySet {
this.activeStagingBuffer = activeStagingBuffer;

commandEncoder.copyBufferToBuffer(this.queryBuffer, 0, activeStagingBuffer, 0, this.bytesPerSlot * count);

const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'ResolveQuerySet');
device.addCommandBuffer(cb);
}

request(count, renderVersion) {
Expand Down
12 changes: 1 addition & 11 deletions src/platform/graphics/webgpu/webgpu-texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,6 @@ class WebgpuTexture {
const stagingBuffer = device.createBufferImpl(BUFFERUSAGE_READ | BUFFERUSAGE_COPY_DST);
stagingBuffer.allocate(device, size);

// use existing or create new encoder
const commandEncoder = device.commandEncoder ?? device.wgpu.createCommandEncoder();

const src = {
texture: this.gpuTexture,
mipLevel: mipLevel,
Expand All @@ -548,16 +545,9 @@ class WebgpuTexture {
};

// copy the GPU texture to the staging buffer
const commandEncoder = device.getCommandEncoder();
commandEncoder.copyTextureToBuffer(src, dst, copySize);

// if we created new encoder
if (!device.commandEncoder) {
DebugHelper.setLabel(commandEncoder, 'copyTextureToBuffer-Encoder');
const cb = commandEncoder.finish();
DebugHelper.setLabel(cb, 'copyTextureToBuffer-CommandBuffer');
device.addCommandBuffer(cb);
}

// async read data from the staging buffer to a temporary array
return device.readBuffer(stagingBuffer, size, null, immediate).then((temp) => {

Expand Down

0 comments on commit 2f0b136

Please sign in to comment.