Skip to content

Commit

Permalink
Add timestamp query support to compute boids
Browse files Browse the repository at this point in the history
  • Loading branch information
beaufortfrancois committed Nov 9, 2023
1 parent d67ae2a commit cbe9f4c
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 7 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@types/stats.js": "^0.17.0",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"@webgpu/types": "^0.1.21",
"@webgpu/types": "^0.1.38",
"eslint": "^8.26.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
Expand Down
78 changes: 76 additions & 2 deletions src/sample/computeBoids/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,27 @@ import updateSpritesWGSL from './updateSprites.wgsl';
const init: SampleInit = async ({ canvas, pageState, gui }) => {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter, 'requestAdapter returned null');
const device = await adapter.requestDevice();

const hasTimestampQuery = adapter.features.has('timestamp-query');
const device = await adapter.requestDevice({
requiredFeatures: hasTimestampQuery ? ['timestamp-query'] : [],
});

const perfDisplayContainer = document.createElement('div');
perfDisplayContainer.style.color = 'white';
perfDisplayContainer.style.background = 'black';
perfDisplayContainer.style.position = 'absolute';
perfDisplayContainer.style.top = '10px';
perfDisplayContainer.style.left = '10px';
perfDisplayContainer.style.textAlign = 'left';

const perfDisplay = document.createElement('pre');
perfDisplayContainer.appendChild(perfDisplay);
if (canvas.parentNode) {
canvas.parentNode.appendChild(perfDisplayContainer);
} else {
console.error('canvas.parentNode is null');
}

if (!pageState.active) return;
const context = canvas.getContext('webgpu') as GPUCanvasContext;
Expand Down Expand Up @@ -97,6 +117,36 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
],
};

const computePassDescriptor = {};

let querySet: GPUQuerySet | undefined = undefined;
let resolveBuffer: GPUBuffer | undefined = undefined;
let resultBuffer: GPUBuffer | undefined = undefined;
if (hasTimestampQuery) {
querySet = device.createQuerySet({
type: 'timestamp',
count: 4,
});
resolveBuffer = device.createBuffer({
size: 4 * BigInt64Array.BYTES_PER_ELEMENT,
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
});
resultBuffer = device.createBuffer({
size: 4 * BigInt64Array.BYTES_PER_ELEMENT,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});
renderPassDescriptor.timestampWrites = {
querySet,
beginningOfPassWriteIndex: 0,
endOfPassWriteIndex: 1,
};
computePassDescriptor.timestampWrites = {
querySet,
beginningOfPassWriteIndex: 2,
endOfPassWriteIndex: 3,
};
}

// prettier-ignore
const vertexBufferData = new Float32Array([
-0.01, -0.02, 0.01,
Expand Down Expand Up @@ -217,7 +267,9 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {

const commandEncoder = device.createCommandEncoder();
{
const passEncoder = commandEncoder.beginComputePass();
const passEncoder = commandEncoder.beginComputePass(
computePassDescriptor
);
passEncoder.setPipeline(computePipeline);
passEncoder.setBindGroup(0, particleBindGroups[t % 2]);
passEncoder.dispatchWorkgroups(Math.ceil(numParticles / 64));
Expand All @@ -231,8 +283,30 @@ const init: SampleInit = async ({ canvas, pageState, gui }) => {
passEncoder.draw(3, numParticles, 0, 0);
passEncoder.end();
}

if (hasTimestampQuery && resultBuffer.mapState == 'unmapped') {
commandEncoder.resolveQuerySet(querySet, 0, 4, resolveBuffer, 0);
commandEncoder.copyBufferToBuffer(
resolveBuffer,
0,
resultBuffer,
0,
resultBuffer.size
);
}

device.queue.submit([commandEncoder.finish()]);

if (hasTimestampQuery && resultBuffer.mapState == 'unmapped') {
resultBuffer.mapAsync(GPUMapMode.READ).then(() => {
const times = new BigInt64Array(resultBuffer.getMappedRange());
const renderPassDuration = Number(times[1] - times[0]);
const computePassDuration = Number(times[3] - times[2]);
perfDisplay.textContent = ` render pass duration: ${renderPassDuration}ns\ncompute pass duration: ${computePassDuration}ns\n`;
resultBuffer.unmap();
});
}

++t;
requestAnimationFrame(frame);
}
Expand Down

0 comments on commit cbe9f4c

Please sign in to comment.