Original https://github.com/OmarShehata/webgpu-compute-rasterizer/blob/main/how-to-use-timestamp-queries.md


This article shows how to use WebGPU's timestamp-query function to calculate the execution time of your GPU instructions.

In WebGPU, timestamp querying is an optional feature, not necessarily available in all implementations. As of this writing, it is disabled on browsers for security reasons (see gpuweb/gpuweb #2218 for specific reasons)

2022-08-31-01-14-49-S[L8TH~{$4855~B8YZK(BKA.png

Overview

The following is a brief introduction to the process of timestamp query:

  • When requesting a device object, add timestamp-query function request
  • Create a query set with a capacity of N (how many timestamps should be stored in the current frame)
  • Create a storage-buffer object (storage-buffer) with a size of 8×N bytes, because the result of the timestamp query needs to be stored in 64 bits;
  • Call commandEncoder.writeTimestamp method to record the timestamp; it will record the timestamp after all instructions have ended;
  • Call the commandEncoder.resolveQuerySet method to write the timestamp query result into the storage buffer
  • Copies the storage buffer into CPU-readable memory and decodes it as a BigInt64Array (see BigInt - JavaScript | MDN )

Teaching step by step

The full example can be found in this PR: Example usage of timestamp queries by OmarShehata Pull Request #5 OmarShehata/webgpu-compute-rasterizer GitHub

0. Let the browser have the timestamp query function

The default browser is to close the unsafe API, start the Chrome browser with the following parameters:

 --disable-dawn-features=disallow_unsafe_apis
Translator's Note: From --disable-dawn-features it can be seen that this startup parameter is unique to the Chrome system, and is not common to Firefox and Safari. The specific operation can be right-clicked on the shortcut of Chrome or Edge, followed by this string of strings after "Properties" - "Target".

1. Create Queryset and buffer objects

When requesting a device object, you need to add timestamp-query to the requiredFeatures array:

 const device = await adapter.requestDevice({
  requiredFeatures: ["timestamp-query"],
})

If the browser does not enable timestamp query or does not support it, an error will be reported:

 Uncaught (in promise) TypeError: Failed to execute 'requestDevice' on 'GPUAdapter': Unsupported feature: timestamp-query

Then, create a queryset and querybuffer objects:

 const capacity = 3 // 要存多少个查询结果
const querySet = device.createQuerySet({
  type: "timestamp",
  count: capacity,
})
const queryBuffer = device.createBuffer({
  size: 8 * capacity,
  usage: GPUBufferUsage.QUERY_RESOLVE 
    | GPUBufferUsage.STORAGE
    | GPUBufferUsage.COPY_SRC
    | GPUBufferUsage.COPY_DST,
})

2. Write the timestamp

Between the code that allocates the rendering pipeline, call the commandEncoder.writeTimestamp(querySet, index) method to record the timestamp:

 // 在各种指令编码过程中记录时间戳
commandEncoder.writeTimestamp(querySet, 0) // 初始时间戳
// draw(...)
commandEncoder.writeTimestamp(querySet, 1)

index <= defined capacity value - 1.

3. Parse the timestamp into the buffer object

At the end of each frame of code, call the commandEncoder.resolveQuerySet method to correctly write the storage buffer object:

 commandEncoder.resolveQuerySet(
  querySet, 
  0, // 从哪个查询开始
  capacity, // 要写入多少个查询
  queryBuffer, 
  0 // 写入缓冲对象的偏移值
)

4. Read the query result

That is, read data from a storage buffer object. After obtaining the ArrayBuffer, use the BigInt type array to read it. Timestamp values are in nanoseconds.

 // === `commandEncoder.finish()` 调用之后 ===
// 使用下面的 readBuffer 函数读取 queryBuffer 对象中的数据
const arrayBuffer = await readBuffer(device, queryBuffer);
// 使用 BigInt 类型数组读取数据
const timingsNanoseconds = new BigInt64Array(arrayBuffer);

const readBuffer = async (device, buffer) => {
  const size = buffer.size
  const gpuReadBuffer = device.createBuffer({
    size,
    usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ 
  })

  const copyEncoder = device.createCommandEncoder()
  copyEncoder.copyBufferToBuffer(buffer, 0, gpuReadBuffer, 0, size)

  const copyCommands = copyEncoder.finish()
  device.queue.submit([copyCommands])

  await gpuReadBuffer.mapAsync(GPUMapMode.READ)
  return gpuReadBuffer.getMappedRange()
}

5. (Optional) Add tags

In order to make the output information more friendly, each timestamp can be labeled, which is helpful to distinguish the difference when outputting. Converting nanoseconds to milliseconds is also useful.

Thanks

Many thanks to Markus Schütz for the timestamp query example in Potree's About WebGPU implementation .

Thanks to Gu Yang in issue 3354 for explaining how to enable timestamp queries in Chrome.


岭南灯火
83 声望54 粉丝

一介草民