Skip to content

Commit

Permalink
Deploying to gh-pages from @ 530c33a 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
kainino0x committed Nov 19, 2024
1 parent 36f3d69 commit 4c3fb08
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 144 deletions.
88 changes: 50 additions & 38 deletions sample/timestampQuery/TimestampQueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,106 @@ export default class TimestampQueryManager {
// 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;
#timestampQuerySet: GPUQuerySet;

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

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

// State used to avoid firing concurrent readback of timestamp values
hasOngoingTimestampReadback: boolean;
// Last queried elapsed time of the pass in nanoseconds.
passDurationMeasurementNs: number;

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

this.timestampCount = timestampCount;
this.passDurationMeasurementNs = 0;

// Create timestamp queries
this.timestampQuerySet = device.createQuerySet({
this.#timestampQuerySet = device.createQuerySet({
type: 'timestamp',
count: timestampCount, // begin and end
count: 2, // 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,
this.#timestampBuffer = device.createBuffer({
size: this.#timestampQuerySet.count * timestampByteSize,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.QUERY_RESOLVE,
});

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

this.hasOngoingTimestampReadback = false;
// Add both a start and end timestamp.
addTimestampWrite(
passDescriptor: GPURenderPassDescriptor | GPUComputePassDescriptor
) {
if (this.timestampSupported) {
// We instruct the render pass to write to the timestamp query before/after
passDescriptor.timestampWrites = {
querySet: this.#timestampQuerySet,
beginningOfPassWriteIndex: 0,
endOfPassWriteIndex: 1,
};
}
return passDescriptor;
}

// Resolve all timestamp queries and copy the result into the map buffer
resolveAll(commandEncoder: GPUCommandEncoder) {
// Resolve the timestamp queries and copy the result into the mappable buffer if possible.
resolve(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,
this.#timestampQuerySet,
0 /* firstQuery */,
this.timestampCount /* queryCount */,
this.timestampBuffer,
this.#timestampQuerySet.count /* queryCount */,
this.#timestampBuffer,
0 /* destinationOffset */
);

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

// Once resolved, we can read back the value of timestamps
readAsync(onTimestampReadBack: (timestamps: BigUint64Array) => void): void {
// Read the values of timestamps.
tryInitiateTimestampDownload(): void {
if (!this.timestampSupported) return;
if (this.hasOngoingTimestampReadback) return;
if (this.#timestampMapBuffer.mapState !== 'unmapped') return;

this.hasOngoingTimestampReadback = true;

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

onTimestampReadBack(timestamps);

// Subtract the begin time from the end time.
// Cast into number. Number can be 9007199254740991 as max integer
// which is 109 days of nano seconds.
const elapsedNs = Number(timestamps[1] - timestamps[0]);
// It's possible elapsedNs is negative which means it's invalid
// (see spec https://gpuweb.github.io/gpuweb/#timestamp)
if (elapsedNs >= 0) {
this.passDurationMeasurementNs = elapsedNs;
}
buffer.unmap();
this.hasOngoingTimestampReadback = false;
});
}
}
13 changes: 13 additions & 0 deletions sample/timestampQuery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,24 @@
max-width: 100%;
display: block;
}
#info {
color: white;
background-color: black;
position: absolute;
top: 10px;
left: 10px;
}
#info pre {
margin: 0.5em;
}
</style>
<script defer src="main.js" type="module"></script>
<script defer type="module" src="../../js/iframe-helper.js"></script>
</head>
<body>
<canvas></canvas>
<div id="info">
<pre></pre>
</div>
</body>
</html>
120 changes: 55 additions & 65 deletions sample/timestampQuery/main.js

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

2 changes: 1 addition & 1 deletion sample/timestampQuery/main.js.map

Large diffs are not rendered by default.

Loading

0 comments on commit 4c3fb08

Please sign in to comment.