You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
8.2 KiB
151 lines
8.2 KiB
# Amber
|
|
|
|
Amber is a multi-API shader test framework. Graphics and compute bugs can be
|
|
captured and communicated through a scripting interface. This removes the need
|
|
to program to the low level interface when reproducing bugs.
|
|
|
|
Amber is broken into multiple layers: the applications, the parsing components
|
|
and the script execution.
|
|
|
|
## Applications
|
|
There are currently two applications, the `[amber](../samples/amber.cc)`
|
|
application and the Amber integration into the
|
|
[Vulkan Conformance Test Suite 'CTS'](https://github.com/KhronosGroup/VK-GL-CTS/tree/master/external/vulkancts/modules/vulkan/amber). These applications are responsible
|
|
for configuring the script execution environment (setting up Vulkan, Dawn or
|
|
another engine), calling into the parsing code to generate a test script and
|
|
then passing that script into the script execution component.
|
|
|
|
We require the application to configure the execution engine. This allows the
|
|
application to handle loading function pointers, doing configuration and other
|
|
setups as required. There are no hardcoded assumptions in Amber as to how you're
|
|
setting up the API to be tested.
|
|
|
|
This engine configuration is done through the `VulkanEngineConfig` in
|
|
`amber/amber_vulkan.h` or the `DawnEngineConfig` in `amber/amber_dawn.h`.
|
|
|
|
The sample application does this configuration through the config_helper
|
|
classes. `samples/config_helper_vulkan.*` and `samples/config_helper_dawn.*`.
|
|
For the CTS, the Vulkan engine is already configured and we just set the
|
|
`VulkanEngineConfig` as needed.
|
|
|
|
Accessing Amber itself is done through the `Amber` class in `amber/amber.h`. We
|
|
require the application to parse and execute a script as two separate steps.
|
|
This separation allows for the shaders to be retrieved after a parse command
|
|
and then pre-compiled and passed into the execution. Passing the compiled
|
|
shaders is optional.
|
|
|
|
### Delegate
|
|
A delegate object can be provided to Amber, for observing internal operations as
|
|
the script is executed. The delegate can be a null pointer, to indicate no
|
|
observations are requested.
|
|
|
|
If the delegate is provided and the `LogGraphicsCalls` method returns
|
|
`true` then the Vulkan API wrappers will call `Log` for each call into Vulkan.
|
|
If the `LogGraphicsCallsTime` also returns `true` then timings for those
|
|
Vulkan calls will also be recorded. The timestamps are retrieved from the
|
|
`GetTimestampNS` callback.
|
|
|
|
### Buffer Extractions
|
|
Amber can be instructed to retrieve the contents of buffers when execution is
|
|
complete. This is done through the `extractions` list in the Amber `Options`
|
|
structure. You must set the `buffer_name` for each of the buffers you want to
|
|
extract. When Amber completes it will fill out the `width`, `height` and set
|
|
of `Value` objects for that buffer.
|
|
|
|
### Execution
|
|
There are two methods to execute a parsed script: `Execute` and
|
|
`ExecuteWithShaderData`. They both accept the `Recipe` and `Options`, the
|
|
`ExecuteWithShaderData` also accepts a map of shader name to data. The data
|
|
is the compiled SPIR-V binary for the shader. This allows you to compile and
|
|
cache the shader if needed.
|
|
|
|
## Parsing component
|
|
Amber can use scripts written in two dialects:
|
|
[AmberScript](amber_script.md), and [VkScript](vk_script.md). The `AmberScript`
|
|
parser will be used if the first 7 characters of the script are
|
|
`#!amber`, otherwise the `VkScript` parser will be used. The parsers both
|
|
generate a `Script` which is our representation of the script file.
|
|
|
|
### AmberScript
|
|
The AmberScript format maps closely to the format stored in the script objects.
|
|
As such, there is a single Parser class for AmberScript which produces all of
|
|
the needed script components.
|
|
|
|
### VkScript
|
|
For VkScript we do a bit of work to make the script match AmberScript. A default
|
|
pipeline is generated and all content in the script is considered part of the
|
|
generated pipeline. We generate names for all of the buffers in the file. The
|
|
framebuffer is named `framebuffer`. The generated depth buffer is named
|
|
`depth_buffer`. For other buffers, we generate a name of `AutoBuf-<num>` where
|
|
the number is the current number of buffers seen counting from 0.
|
|
|
|
The VkScript parser is broken into three major chunks. The `Parser`,
|
|
`SectionParser` and `CommandParser`. The `Parser` is the overall driver for the
|
|
parser. The `SectionParser` breaks the input file into the overall chunks (each
|
|
of the sections separated by the \[blocks]). The `CommandParser` parses the
|
|
`[test]` section specifically. For other sections they're parsed directly in
|
|
the `Parser` object.
|
|
|
|
### Parsed Representation
|
|
The `Script` object owns all of the pipelines, buffers, shaders, and command
|
|
objects. Other objects hold pointers but the script holds the `unique_ptr` for
|
|
these objects.
|
|
|
|
```
|
|
+--------+ +--------------+
|
|
| Script |---------------->| Requirements |
|
|
+--------+ +--------------+
|
|
|
|
|
+------------------+------------+--------+----------------------+
|
|
| | | |
|
|
v v v v
|
|
+---------+ +---------+ +---------+ +---------+
|
|
| +--------+ | +---------+ | +----------+ | +---------+
|
|
+--| +--------+ +--| +--------+ +--| +----------+ +--| +---------+
|
|
+--| Shader | +--| Buffer | +--| Pipeline | +--| Command |
|
|
+--------+ +--------+ +----------+ +---------+
|
|
^ ^ ^ | | ^ | |
|
|
| | | | | +---------------+ |
|
|
Entry point | | +-----------------+ | |
|
|
Optimizations | | Descriptor Set | |
|
|
Type | | Binding | |
|
|
Compiled Shader| | Attachment Location | |
|
|
| | | |
|
|
+------------------------------------------+ |
|
|
| |
|
|
+-----------------------------------------------+
|
|
BufferCommand
|
|
```
|
|
|
|
A `Script` contains shaders, pipelines, buffers, and commands. Pipelines
|
|
contain shaders and buffers. An Amber buffer corresponds to either a buffer
|
|
resource or an image resource in the backend engine's API.
|
|
Script execution assumes that after executing a command, each `Buffer` object
|
|
has a reference to the latest data for that buffer or image copied into
|
|
the buffers memory. This means that a draw command will need to copy buffer data to
|
|
the device, execute the draw, and then copy the device data back into the
|
|
buffer. Amber does not do any extra calls to fill the buffers. Then engine must
|
|
keep that data in sync.
|
|
|
|
The `Pipeline` object holds the context for a given set of tests. This includes
|
|
shaders, colour attachments, depth/stencil attachment, data buffers, etc. The
|
|
colour attachments will have their `Attachment Location` while data buffers
|
|
will have a `Descriptor Set` and `Binding` provided.
|
|
|
|
## Execution
|
|
When the script is executed the pipeline shaders will be compiled, if not
|
|
provided through the shader map. This will fill in the `Compiled Shader` data
|
|
in the each pipeline shader object. The `CreatePipeline` call will then be
|
|
executed for a given pipeline.
|
|
|
|
With the pipelines created the `Command` objects will be executed. Each
|
|
`Command` knows the Amber `Pipeline` associated with it, that `Pipeline` pointer
|
|
is provided in the command execution and can be used to lookup the
|
|
engine-specific representation of a pipeline.
|
|
|
|
When the `Probe` and `ProbeSSBO` commands are encountered they are not passed
|
|
to the engine but sent to the `Verifier`. This will validate that the data
|
|
in the specified buffer matches the test data. As mentioned above, this assumes
|
|
that the Amber `Buffer` is kept up to date with the current device memory as we
|
|
do not attempt to fill in the buffers before executing the `Probe*` calls.
|