Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix to WebGPU vertex buffer setup #6274

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/src/examples/graphics/mesh-morph-many/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
* @type {import('../../../../types.mjs').ExampleConfig}
*/
export default {
WEBGPU_ENABLED: false
WEBGPU_ENABLED: true
};
36 changes: 34 additions & 2 deletions src/platform/graphics/webgpu/webgpu-graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { path } from '../../../core/path.js';

import {
PIXELFORMAT_RGBA32F, PIXELFORMAT_RGBA8, PIXELFORMAT_BGRA8, DEVICETYPE_WEBGPU,
BUFFERUSAGE_READ, BUFFERUSAGE_COPY_DST
BUFFERUSAGE_READ, BUFFERUSAGE_COPY_DST, semanticToLocation
} from '../constants.js';
import { GraphicsDevice } from '../graphics-device.js';
import { DebugGraphics } from '../debug-graphics.js';
Expand All @@ -30,6 +30,8 @@ import { WebgpuResolver } from './webgpu-resolver.js';
import { WebgpuCompute } from './webgpu-compute.js';
import { WebgpuBuffer } from './webgpu-buffer.js';

const _uniqueLocations = new Map();

class WebgpuGraphicsDevice extends GraphicsDevice {
/**
* Object responsible for caching and creation of render pipelines.
Expand Down Expand Up @@ -452,16 +454,45 @@ class WebgpuGraphicsDevice extends GraphicsDevice {

submitVertexBuffer(vertexBuffer, slot) {

const elements = vertexBuffer.format.elements;
const format = vertexBuffer.format;
const { interleaved, elements } = format;
const elementCount = elements.length;
const vbBuffer = vertexBuffer.impl.buffer;

if (interleaved) {
// for interleaved buffers, we use a single vertex buffer, and attributes are specified using the layout
this.passEncoder.setVertexBuffer(slot, vbBuffer);
return 1;
}

// non-interleaved - vertex buffer per attribute
for (let i = 0; i < elementCount; i++) {
this.passEncoder.setVertexBuffer(slot + i, vbBuffer, elements[i].offset);
}

return elementCount;
}

validateVBLocations(vb0, vb1) {

// in case of multiple VBs, validate all elements use unique locations
const validateVB = (vb) => {
const { elements } = vb.format;
for (let i = 0; i < elements.length; i++) {
const name = elements[i].name;
const location = semanticToLocation[name];
if (_uniqueLocations.has(location)) {
Debug.errorOnce(`Vertex buffer element location ${location} used by [${name}] is already used by element [${_uniqueLocations.get(location)}], while rendering [${DebugGraphics.toString()}]`);
}
_uniqueLocations.set(location, name);
}
};

validateVB(vb0);
validateVB(vb1);
_uniqueLocations.clear();
}

draw(primitive, numInstances = 1, keepBuffers) {

if (this.shader.ready && !this.shader.failed) {
Expand All @@ -479,6 +510,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
if (vb0) {
const vbSlot = this.submitVertexBuffer(vb0, 0);
if (vb1) {
Debug.call(() => this.validateVBLocations(vb0, vb1));
this.submitVertexBuffer(vb1, vbSlot);
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/platform/graphics/webgpu/webgpu-vertex-buffer-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ class WebgpuVertexBufferLayout {
// type {GPUVertexBufferLayout[]}
const layout = [];

// Note: If the VertexFormat is interleaved, we use a single vertex buffer with multiple
// attributes. This uses a smaller number of vertex buffers (1), which has performance
// benefits when setting it up on the device.
// If the VertexFormat is not interleaved, we use multiple vertex buffers, one per
// attribute. This is less efficient, but is required as there is a pretty small
// limit on the attribute offsets in the vertex buffer layout.
const addFormat = (format) => {
const interleaved = format.interleaved;
const stepMode = format.instancing ? 'instance' : 'vertex';
Expand Down