You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Now that we have a `WGPUDevice` object in our hands, we can use it to **sent data and instructions** to the GPU. We learn in this chapter a **key concept** of WebGPU (and of most modern graphics APIs as well), namely **the command queue**.
10
12
11
13
```{important}
@@ -33,7 +35,7 @@ They are not too far, but for high performance applications like real time graph
The bandwidth tells how much information can travel at the same time.
38
+
The bandwidth tells **how much information** can travel at the same time.
37
39
```
38
40
39
41
Since the GPU is meant for **massive parallel data processing**, its performance can easily be **bound by the memory transfers** rather than the actual computation.
@@ -49,7 +51,7 @@ The connection between the **CPU memory** (RAM) and **GPU memory (vRAM)** depend
The latency is the time it takes for each bit to travel.
54
+
The latency is **the time it takes** for each bit to travel.
53
55
```
54
56
55
57
**Even the smallest bit of information** needs some time for the round trip to and back from the GPU. As a consequence, functions that send instructions to the GPU return almost immediately: they **do not wait for the instruction to have actually been executed** because that would require to wait for the GPU to transfer back the "I'm done" information.
**WIP line***We use `wgpuQueueOnSubmittedWorkDone`*
222
+
As repeated and illustrated above, instructions that are submitted to the GPU get executed at their own pace. In many cases, it is not a big issue (as long as the instructions are executed in the right order).
223
+
224
+
Sometimes however, we really want to **have the CPU wait until submitted instructions have been executed**. For that, we may use the function `wgpuQueueOnSubmittedWorkDone`. This creates **an asynchronous operation that does nothing on the GPU side**. Or, more exactly, nothing else than signaling that everything received before has been executed.
225
+
226
+
Like any asynchronous operation, it takes a **callback info** as argument and returns a `WGPUFuture`. In this case, it only takes one other argument, namely the queue into which we push the operation:
227
+
228
+
```C++
229
+
// Signature of the wgpuQueueOnSubmittedWorkDone function in webgpu.h
230
+
WGPUFuture wgpuQueueOnSubmittedWorkDone(
231
+
WGPUQueue queue,
232
+
WGPUQueueWorkDoneCallbackInfo callbackInfo
233
+
);
234
+
```
235
+
236
+
As usual, the callback info contains a `nextInChain` pointer for extensions, a `mode`, two `userdata` pointers and a `callback` function pointer. The latter must have the following type:
237
+
238
+
```C++
239
+
// Definition of the WGPUQueueWorkDoneCallback function type in webgpu.h
240
+
typedefvoid (*WGPUQueueWorkDoneCallback)(
241
+
WGPUQueueWorkDoneStatus status,
242
+
void* userdata1,
243
+
void* userdata2
244
+
);
245
+
```
246
+
247
+
In other words, all it receives besides our potential `userdata` pointers is a status.
248
+
249
+
````{warning}
250
+
The returned status **does not** tell about the success of **other** operations. All it says is whether querying for the moment where submitted work was done did succeed or not. Possible values are:
251
+
252
+
- `WGPUQueueWorkDoneStatus_Success` when the query operation went well.
253
+
- `WGPUQueueWorkDoneStatus_InstanceDropped` when the WebGPU instanced was dropped before previous instructions where executed. This callback is executed nonetheless, but with this special status value.
254
+
- `WGPUQueueWorkDoneStatus_Error` when something went wrong in the process (it's probably a bad sign about the overall course of your program).
255
+
````
256
+
257
+
Inspired by what we did when requesting the adapter and device, we can create a callback that takes a boolean as first user pointer and turn it on whenever the callback is invoked:
221
258
222
259
```{lit} C++, Wait for completion
223
-
// TODO
260
+
// Our callback invoked when GPU instructions have been executed
261
+
auto onQueuedWorkDone = [](
262
+
WGPUQueueWorkDoneStatus status,
263
+
void* userdata1,
264
+
void* /* userdata2 */
265
+
) {
266
+
// Display a warning when status is not success
267
+
if (status != WGPUQueueWorkDoneStatus_Success) {
268
+
std::cout << "Warning: wgpuQueueOnSubmittedWorkDone failed, this is suspicious!" << std::endl;
269
+
}
270
+
271
+
// Interpret userdata1 as a pointer to a boolean (and turn it into a
std::cout << "All queued instructions have been executed!" << std::endl;
293
+
```
294
+
295
+
To wait for the callback to effectively get invoked and thus `workDone` to become `true`, we **reuse the same loop as before**, that calls `wgpuInstanceProcessEvents` interleaved with a small sleep:
296
+
297
+
```{lit} C++, Wait for workDone to be true
298
+
// Hand the execution to the WebGPU instance until onQueuedWorkDone gets invoked
Again, we will see in the [next chapter](playing-with-buffers.md) a more fine-grained method to wait for asynchronous operations, using the `WGPUFuture` handle returned by `wgpuQueueOnSubmittedWorkDone`, but for this case using `wgpuInstanceProcessEvents` works well.
312
+
```
313
+
314
+
You should finally see something like this in your program's output:
315
+
316
+
```
317
+
Submitting command...
318
+
Command submitted.
319
+
All queued instructions have been executed!
320
+
Device 0000009EC06FF4F0 was lost: reason 3 (A valid external Instance reference no longer exists.)
224
321
```
225
322
323
+
````{tip}
324
+
It is **normal that our device gets lost** at the end of our program. We can **have the reason change a bit** though by making sure the instance realizes that the device got released before deleting it on its turn. To do so, you may call `wgpuInstanceProcessEvents` between `wgpuDeviceRelease` and `wgpuInstanceRelease`:
You should now see something like this in your output:
336
+
337
+
```
338
+
Device 000000D266AFEF50 was lost: reason 2 (Device was destroyed.)
339
+
```
340
+
````
341
+
226
342
Conclusion
227
343
----------
344
+
345
+
We have seen a few important notions in this chapter:
346
+
347
+
- The CPU and GPU live in **different timelines**.
348
+
- Commands are streamed from CPU to GPU through a **command queue**.
349
+
- Queued command buffers must be encoded using a **command encoder**.
350
+
- We can **wait for enqueued commands** to be executed with `wgpuQueueOnSubmittedWorkDone`.
351
+
352
+
This was a bit abstract because we can queue operations but we did not see any yet. In the next chapter we will start with **simple operations on memory buffers**, followed by **our first shader** to compute things on the GPU!
0 commit comments