diff --git a/sample/timestampQuery/TimestampQueryManager.ts b/sample/timestampQuery/TimestampQueryManager.ts index 54be3c42..1bdb7fc4 100644 --- a/sample/timestampQuery/TimestampQueryManager.ts +++ b/sample/timestampQuery/TimestampQueryManager.ts @@ -6,106 +6,102 @@ export default class TimestampQueryManager { // 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; - // Last times - timestamps: number[]; + // Last queried elapsed time of the pass. + passElapsedTime: number; // Device must have the "timestamp-query" feature - constructor(device: GPUDevice, timestampPairCount: number) { + constructor(device: GPUDevice) { this.timestampSupported = device.features.has('timestamp-query'); if (!this.timestampSupported) return; - this.timestamps = Array(timestampPairCount).fill(0); + this.passElapsedTime = 0; // Create timestamp queries - this.timestampQuerySet = device.createQuerySet({ + this.#timestampQuerySet = device.createQuerySet({ type: 'timestamp', - count: timestampPairCount * 2, // 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 - this.timestampBuffer = device.createBuffer({ - size: this.timestampQuerySet.count * timestampByteSize, + 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: this.timestampBuffer.size, + this.#timestampMapBuffer = device.createBuffer({ + size: this.#timestampBuffer.size, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, }); } // Add both a start and end timestamp. addTimestampWrite( - renderPassDescriptor: GPURenderPassDescriptor, - pairId: number + passDescriptor: GPURenderPassDescriptor | GPUComputePassDescriptor ) { if (this.timestampSupported) { // We instruct the render pass to write to the timestamp query before/after - const ndx = pairId * 2; - renderPassDescriptor.timestampWrites = { - querySet: this.timestampQuerySet, - beginningOfPassWriteIndex: ndx, - endOfPassWriteIndex: ndx + 1, + passDescriptor.timestampWrites = { + querySet: this.#timestampQuerySet, + beginningOfPassWriteIndex: 0, + endOfPassWriteIndex: 1, }; } - return renderPassDescriptor; + 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.timestampQuerySet.count /* queryCount */, - this.timestampBuffer, + this.#timestampQuerySet.count /* queryCount */, + this.#timestampBuffer, 0 /* destinationOffset */ ); - if (this.timestampMapBuffer.mapState === 'unmapped') { + 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 ); } } - // Read the value of timestamps. - update(): void { + // Read the values of timestamps. + tryInitiateTimestampDownload(): void { if (!this.timestampSupported) return; - if (this.timestampMapBuffer.mapState !== 'unmapped') return; + if (this.#timestampMapBuffer.mapState !== 'unmapped') return; - const buffer = this.timestampMapBuffer; + const buffer = this.#timestampMapBuffer; void buffer.mapAsync(GPUMapMode.READ).then(() => { const rawData = buffer.getMappedRange(); const timestamps = new BigUint64Array(rawData); - for (let i = 0; i < this.timestamps.length; ++i) { - const ndx = i * 2; - // Cast into number. Number can be 9007199254740991 as max integer - // which is 109 days of nano seconds. - const elapsedNs = Number(timestamps[ndx + 1] - timestamps[ndx]); - // It's possible elapsedNs is negative which means it's invalid - // (see spec https://gpuweb.github.io/gpuweb/#timestamp) - if (elapsedNs >= 0) { - this.timestamps[i] = elapsedNs; - } + // 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.passElapsedTime = elapsedNs; } buffer.unmap(); }); diff --git a/sample/timestampQuery/index.html b/sample/timestampQuery/index.html index 5094605f..a25ccfbb 100644 --- a/sample/timestampQuery/index.html +++ b/sample/timestampQuery/index.html @@ -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; + }
+