1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
# GPU-AV Post Process
The goal behind Post Process is to have a very lightweight way to get information from the GPU so that it can be used on the CPU.
In order to be lightweight, Post Process makes assumptions that it can run the GPU without crashing/hanging.
## Descriptor Indexing
For descriptor indexing we use Post Process to do the same validation on the CPU, but one at draw time and one after queue submission time.
Take the following two descriptors in a GLSL shader:
```glsl
layout(set = 0, binding = 0) buffer SSBO_0 { int data; } descriptor_a;
layout(set = 0, binding = 1) buffer SSBO_1 { int data; } descriptor_b[];
```
Normally we will validate `descriptor_a` at draw time as the Vulkan spec requires any static usage to be valid.
Once we start having bindless (or dynamic array) of descriptor, it is important to only check the index accessed. If `descriptor_b` has a million items, but only two are accessed, we use GPU-AV Post Process to detect that.
We create a buffer that hold information about each descriptor and when the GPU is ran, we quickly just mark what has been access. The instrumented GLSL would look like
```glsl
inst_post_process_descriptor_index(0 /*set*/, 1 /*binding*/, index);
descriptor_b[index].data = 0;
```
## Reusing the same code twice
Currently, we have a `DescriptorValidator` class that is created at draw time with Core Checks and also at post-queue submission with GPU-AV.
The goal is that the `ValidateDescriptor()` function can be called the same from both variations. To call `ValidateDescriptor()` we need 2 things:
1. Which descriptor was accessed
2. The `OpVariable` from the SPIR-V that it was accessed in
## Tracking Descriptors and SPIR-V
The `VkDescriptorSet` object contains a buffer with a `PostProcessDescriptorIndexSlot` for each descriptor in it. This "descriptor index slot" holds information that can only be known at GPU execution time (which index, OpVariable, etc... are used). Each time `vkCmdBindDescriptorSets` is called, we create a LUT mapping of 32 (current max limit) addresses to map to each `VkDescriptorSet` bound and its buffer.
The pipelines contain the `OpVariable` SPIR-V information, such as which type of image is used. This is wrapped up in a `BindingVariableMap` pointer. Using this we can map the accessed descriptor on the GPU back to all the state tracking on the CPU.
The following diagram aims to illustrate the flow of data:

When processing on the CPU, we loop through every unique `VkDescriptorSet` to look at the contents of its `PostProcessDescriptorIndexSlot`. It contains both the instrumented `unqiue shader ID` and the `OpVariable` ID. Just like other GPU-AV checks, we can use the `unqiue shader ID` to lookup which `VkShaderModule`/`VkPipeline`/`VkShaderEXT` that reported the error.
|