Skip to content

Commit

Permalink
Add timestamp query example (#472)
Browse files Browse the repository at this point in the history
  • Loading branch information
eliemichel authored Nov 14, 2024
1 parent 3e6e458 commit f8ed72a
Show file tree
Hide file tree
Showing 7 changed files with 430 additions and 0 deletions.
29 changes: 29 additions & 0 deletions sample/timestampQuery/PerfCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// A minimalistic perf timer class that computes mean + stddev online
export default class PerfCounter {
sampleCount: number;
accumulated: number;
accumulatedSq: number;

constructor() {
this.sampleCount = 0;
this.accumulated = 0;
this.accumulatedSq = 0;
}

addSample(value: number) {
this.sampleCount += 1;
this.accumulated += value;
this.accumulatedSq += value * value;
}

getAverage(): number {
return this.sampleCount === 0 ? 0 : this.accumulated / this.sampleCount;
}

getStddev(): number {
if (this.sampleCount === 0) return 0;
const avg = this.getAverage();
const variance = this.accumulatedSq / this.sampleCount - avg * avg;
return Math.sqrt(Math.max(0.0, variance));
}
}
97 changes: 97 additions & 0 deletions sample/timestampQuery/TimestampQueryManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Regroups all timestamp-related operations and resources.
export default class TimestampQueryManager {
// The device may not support timestamp queries, on which case this whole
// class does nothing.
timestampSupported: boolean;

// Number of timestamp counters
timestampCount: number;

// The query objects. This is meant to be used in a ComputePassDescriptor's
// or RenderPassDescriptor's 'timestampWrites' field.
timestampQuerySet: GPUQuerySet;

// A buffer where to store query results
timestampBuffer: GPUBuffer;

// A buffer to map this result back to CPU
timestampMapBuffer: GPUBuffer;

// State used to avoid firing concurrent readback of timestamp values
hasOngoingTimestampReadback: boolean;

// Device must have the "timestamp-query" feature
constructor(device: GPUDevice, timestampCount: number) {
this.timestampSupported = device.features.has('timestamp-query');
if (!this.timestampSupported) return;

this.timestampCount = timestampCount;

// Create timestamp queries
this.timestampQuerySet = device.createQuerySet({
type: 'timestamp',
count: timestampCount, // begin and end
});

// Create a buffer where to store the result of GPU queries
const timestampByteSize = 8; // timestamps are uint64
const timestampBufferSize = timestampCount * timestampByteSize;
this.timestampBuffer = device.createBuffer({
size: timestampBufferSize,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.QUERY_RESOLVE,
});

// Create a buffer to map the result back to the CPU
this.timestampMapBuffer = device.createBuffer({
size: timestampBufferSize,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});

this.hasOngoingTimestampReadback = false;
}

// Resolve all timestamp queries and copy the result into the map buffer
resolveAll(commandEncoder: GPUCommandEncoder) {
if (!this.timestampSupported) return;

// After the end of the measured render pass, we resolve queries into a
// dedicated buffer.
commandEncoder.resolveQuerySet(
this.timestampQuerySet,
0 /* firstQuery */,
this.timestampCount /* queryCount */,
this.timestampBuffer,
0 /* destinationOffset */
);

if (!this.hasOngoingTimestampReadback) {
// Copy values to the mapped buffer
commandEncoder.copyBufferToBuffer(
this.timestampBuffer,
0,
this.timestampMapBuffer,
0,
this.timestampBuffer.size
);
}
}

// Once resolved, we can read back the value of timestamps
readAsync(onTimestampReadBack: (timestamps: BigUint64Array) => void): void {
if (!this.timestampSupported) return;
if (this.hasOngoingTimestampReadback) return;

this.hasOngoingTimestampReadback = true;

const buffer = this.timestampMapBuffer;
void buffer.mapAsync(GPUMapMode.READ).then(() => {
const rawData = buffer.getMappedRange();
const timestamps = new BigUint64Array(rawData);

onTimestampReadBack(timestamps);

buffer.unmap();
this.hasOngoingTimestampReadback = false;
});
}
}
30 changes: 30 additions & 0 deletions sample/timestampQuery/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>webgpu-samples: timestampQuery</title>
<style>
:root {
color-scheme: light dark;
}
html, body {
margin: 0; /* remove default margin */
height: 100%; /* make body fill the browser window */
display: flex;
place-content: center center;
}
canvas {
width: 600px;
height: 600px;
max-width: 100%;
display: block;
}
</style>
<script defer src="main.js" type="module"></script>
<script defer type="module" src="../../js/iframe-helper.js"></script>
</head>
<body>
<canvas></canvas>
</body>
</html>
Loading

0 comments on commit f8ed72a

Please sign in to comment.