# Shader Validation

Shader validation is run at `vkCreateShaderModule` and `vkCreate*Pipelines` time. It makes sure both the `SPIR-V` is valid
as well as the `VkPipeline` object interface with the shader. Note, this is all done on the CPU and different than
[GPU-Assisted Validation](gpu_validation.md).

## Standalone VUs with spirv-val

There are many [VUID labeled](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#spirvenv-module-validation-standalone) as `VUID-StandaloneSpirv-*` and all the
[Built-In Variables](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#interfaces-builtin-variables)
VUIDs that can be validated on a single shader module and require no runtime information.

All of these validations are passed off to `spirv-val` in [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools/).

## spirv-opt

There are a few special places where `spirv-opt` is run to reduce recreating work already done in `SPIRV-Tools`.
These can be found by searching for `RegisterPass` in the code

Currently these are

- Specialization constants
  - This `spirv-opt` pass is used to inject the constants from the pipeline layout.
  - Some checks require the runtime spec constant values
- Flatten OpGroupDecorations
- Detects if group decorations were used; however, group decorations were [deprecated](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpGroupDecorate) early on in the development of the SPIR-v specification.
- Debug Printf
  - instruments the shaders - see [GPU-Assisted Validation](gpu_validation.md).
- GPU-VA
  - instruments the shaders - see [GPU-Assisted Validation](gpu_validation.md).

## Different sections

The code is currently split up into the following main sections

- `layers/shader_instruction.cpp`
    - This contains information about each SPIR-V instruction.
- `layers/shader_module.cpp`
    - This contains information about the `VkShaderModule` object
- `layers/shader_validation.cpp`
    - This takes the following above and does the actual validation. All errors are produced here.
    - `layers/vulkan/generated/spirv_validation_helper.cpp`
        - This is generated file provides a way to generate checks for things found in the `vk.xml` related to SPIR-V
- `layers/vulkan/generated/spirv_grammar_helper.cpp`
    - This is a general util file that is [generated](generated_code.md) from the SPIR-V grammar

## Types of Shader Validation

All Shader Validation can be broken into 4 types of checks

- Standalone with runtime properties
  - Things like features and limits
- Shader interface
  - Ex. going between a Vertex and Fragment shader
- Interaction with Pipeline creation structs
  - Vertex input, fragment output, etc
- Draw time
  - making sure bound descriptor matches up what is being touched

## Design details

When dealing with shader validation there are a few concepts to understand and not confuse

- `EntryPoints`
  - Tied to a shader stage (fragment, vertex, etc)
  - Knows which variables and instructions are touched in stage
    - There might be things in a `ShaderModule` not related to shader stage validation
- `Shader Module`
  - a `VkShaderModule` object
  - contains all information about the SPIR-V module
  - contains SPIR-V instructions (in an array of `uint32_t` words)
  - knows the relationship between instructions
  - can contain multiple `EntryPoints`
    - [For more details](https://github.com/KhronosGroup/SPIRV-Guide/blob/master/chapters/entry_execution.md#instructions-with-multiple-execution-modes)
- `Pipeline`
  - contains 1 or more shader object
  - decides both which `Shader Module` and `EntryPoint` are used
  - has other state not known if validating just the shader object
- `Pipeline Library` (GPL) (`VK_EXT_graphics_pipeline_library`)
  - part of a pipeline that can be reused
- `ShaderModuleIdentifier` (`VK_EXT_shader_module_identifier`)
  - lets app use a hash instead of having the driver re-create the `ShaderModule`
  - not possible to validate as the VVL don't know what the `ShaderModule` is

When dealing with validation, it is important to know what should be validated, and when.

If validation only cares about... :

- the SPIR-V itself, is mapped to the `Shader Module`
- if two stages interface, needs to be done when all stages are there
  -For `Pipeline Library` it might need to wait until linking
- descriptors variables, use `EntryPoint`
- the stage of a shader module is always known, regardless of even using `ShaderModuleIdentifier`

### Variables

There are 2 types of Variables

- `Resource Interface` variables (mapped to descriptors)
- `Stage Interface` variables (input and output between shader)
  - Can be either a `BuiltIn` or `User Defined` variables

For each `EntryPoint` we walk the functions and find all `Variables` accessed (load, store, atomic).

Infomaration to note:
- It is possible to have multiple `EntryPoints` pointing to the same interface variable.
- 2 different accesses (ex. `OpLoad`) can point to same `Variable`
- 2 `Image operation` can point to 2 different `Variables`

### Image Accesses

Any variable in a shader pointing to an Image is a `Resource Interface` variable.
There are validation checks that need care only if the variable is accessed.
This requires a `OpImage*` instruction to access the variable.

Most Accesses look like

```
OpImage* -> OpLoad -> OpAccessChain (optional) -> OpVariable
```

There are a few exceptions:

An Image Fetch has an `OpImage` prior to the `OpLoad`

```
OpImageFetch -> OpImage -> OpLoad -> OpVariable
```

Image Atomics use `OpImageTexelPointer` instead of `OpLoad`

```
OpAtomicLoad -> OpImageTexelPointer -> OpAccessChain (optional) -> OpVariable
```

The biggest thing to consider is using either a

- `VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER`
- `VK_DESCRIPTOR_TYPE_SAMPLER` and `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE` combo

```
// VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
OpImageSampleExplicitLod -> OpLoad -> OpAccessChain (optional) -> OpVariable -> OpTypePointer -> OpTypeSampledImage

// VK_DESCRIPTOR_TYPE_SAMPLER and VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
OpImageSampleImplicitLod -> OpSampledImage -> OpTypeSampledImage
                                           -> OpLoad -> OpAccessChain (optional) -> OpVariable (image)
                                           -> OpLoad -> OpAccessChain (optional) -> OpVariable (sampler)
```

Both contain a `OpTypeSampledImage`, which is how we know a `VkSampler` is being used with the variable

But it is also possible to have the Image and Samplers mix and match

```
ImageAccess -> Image_0
            -> Sampler_0

ImageAccess -> Image_0
            -> Sampler_1

ImageAccess -> Image_0 (non-sampled access)
```

This is handled by having the `Resource Interface` variable track if it has a `OpTypeSampledImage`, `OpTypeImage` or `OpTypeSampler`

- If it has `OpTypeSampledImage`, there is **no way** for it to be part of a `SAMPLER`/`SAMPLED_IMAGE` combo
- If it has a `OpTypeImage` or `OpTypeSampler`, we need to know if they are **accessed together**
    - This means the the `ValidateDescriptor` logic needs to know every `OpTypeSampler` variable accessed together with a `OpTypeImage` variable
    - There is no case where only a `OpTypeSampler` variable can be used by itself, so no need to track it the other way

### Atomics

There are 2 types of atomic accesses: "Image" and "Non-Image"

Image atomics are described above how they use `OpImageTexelPointer` instead of `OpLoad`

Non-Image atomics will look like

```
OpAtomicLoad -> OpAccessChain (optional) -> OpVariable
```