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.

36 KiB

AmberScript

  • DRAFT

This document defines the script input language for the Amber system. The format is based on the Talvos format, VkRunner format, and VkScript proposed format.

Specification

All amber scripts must start with #!amber as the first line. Comments are specified by a # character and continue to the end of the line, except in inlined shader source code, where AmberScript comments are not possible. Keywords are case sensitive. All names are made up of ASCII characters, and delimited by whitespace.

TODO(dneto): What characters are valid in a name?

Number literals

Literal numbers are normally presented in decimal form. They are interpreted as integers or floating point depending on context: a command parameter is predefined as either integral or floating point, or the data type is user-specified (such as for buffer data).

Hex values: Whenever an integer is expected, you may use a hexadecimal number, which is the characters 0x followed by hexadecimal digits.

Requesting features

If specific device features are required you can use the DEVICE_FEATURE command to enable them.

DEVICE_FEATURE vertexPipelineStoresAndAtomics
DEVICE_FEATURE VariablePointerFeatures.variablePointersStorageBuffer

Currently each of the items in VkPhysicalDeviceFeatures are recognized along with:

  • VariablePointerFeatures.variablePointers
  • VariablePointerFeatures.variablePointersStorageBuffer
  • Float16Int8Features.shaderFloat16
  • Float16Int8Features.shaderInt8
  • Storage8BitFeatures.storageBuffer8BitAccess
  • Storage8BitFeatures.uniformAndStorageBuffer8BitAccess
  • Storage8BitFeatures.storagePushConstant8
  • Storage16BitFeatures.storageBuffer16BitAccess
  • Storage16BitFeatures.uniformAndStorageBuffer16BitAccess
  • Storage16BitFeatures.storagePushConstant16
  • Storage16BitFeatures.storageInputOutput16
  • SubgroupSizeControl.subgroupSizeControl
  • SubgroupSizeControl.computeFullSubgroups

Extensions can be enabled with the DEVICE_EXTENSION and INSTANCE_EXTENSION commands.

DEVICE_EXTENSION VK_KHR_get_physical_device_properties2
INSTANCE_EXTENSION VK_KHR_storage_buffer_storage_class

Setting Engine Configuration

In some instances there is extra data we want to provide to an engine for configuration purposes. The SET ENGINE_DATA command allows that for the given set of data types.

Engine Data Variables

  • fence_timeout_ms - value must be a single uint32 in milliseconds.
SET ENGINE_DATA {engine data variable} {value}*

Virtual File Store

Each amber script contains a virtual file system that can store files of textual data. This lets you bundle multiple source files into a single, hermetic amber script file.

Virtual files are declared using the VIRTUAL_FILE command:

VIRTUAL_FILE {path}
 {file-content}
END

Paths must be unique.

Shaders can directly reference these virtual files for their source.
HLSL shaders that #include other .hlsl files will first check the virtual file system, before falling back to the standard file system.

Shaders

Shader programs are declared using the SHADER command.
Shaders can be declared as PASSTHROUGH, with inlined source or using source from a VIRTUAL_FILE.

Pass-through shader:

# Creates a passthrough vertex shader. The shader passes the vec4 at input
# location 0 through to the `gl_Position`.
SHADER vertex {shader_name} PASSTHROUGH

Shader using inlined source:

# Creates a shader of |shader_type| with the given |shader_name|. The shader
# will be of |shader_format|. The shader source then follows and is terminated
# with the |END| tag.
SHADER {shader_type} {shader_name} {shader_format} [ TARGET_ENV {target_env} ]
{shader_source}
END

Shader using source from VIRTUAL_FILE:

# Creates a shader of |shader_type| with the given |shader_name|. The shader
# will be of |shader_format|. The shader will use the virtual file with |path|.
SHADER {shader_type} {shader_name} {shader_format} [ TARGET_ENV {target_env} ] VIRTUAL_FILE {path}

{shader_name} is used to identify the shader to attach to PIPELINEs,

{shader_type} and {shader_format} are described below:

Shader Type

  • vertex
  • fragment
  • geometry
  • tessellation_evaluation
  • tessellation_control
  • compute
  • multi

The compute pipeline can only contain compute shaders. The graphics pipeline can not contain compute shaders, and must contain a vertex shader and a fragment shader.

The provided multi shader can only be used with SPIRV-ASM and SPIRV-HEX and allows for providing multiple shaders in a single module (so the vertex and fragment shaders can be provided together.)

Note, SPIRV-ASM and SPIRV-HEX can also be used with each of the other shader types, but in that case must only provide a single shader type in the module.

Shader Format

  • GLSL  (with glslang)
  • HLSL  (with dxc or glslang if dxc disabled)
  • SPIRV-ASM (with spirv-as; specifying TARGET_ENV is highly recommended in this case, as explained below)
  • SPIRV-HEX (decoded straight to SPIR-V)
  • OPENCL-C (with clspv)

Target environment

Specifying TARGET_ENV is optional and can be used to select a target SPIR-V environment. For example:

  • spv1.0
  • spv1.5
  • vulkan1.0
  • vulkan1.2

Check the help text of the corresponding tool (e.g. spirv-as, glslangValidator) for the full list. The SPIRV-HEX shader format is not affected by the target environment.

The specified target environment for the shader overrides the default (spv1.0) or the one specified on the command line.

Specifying the target environment when using the SPIRV-ASM shader format is highly recommended, otherwise the SPIR-V version of the final SPIR-V binary shader passed to the graphics device might not be what you expect. Typically, SPIR-V assembly text will contain a comment near the beginning similar to ; Version: 1.0 but this is ignored by the spirv-as assembler. Thus, you should specify the equivalent target environment (e.g. spv1.0) in the SHADER command.

Specifying the target environment for other shader formats depends on whether you want to vary the final SPIR-V shader binary based on the target environment specified on the command line. For example, you could write one AmberScript file that contains a GLSL shader without specifying a target environment. You could then run the AmberScript file several times with different target environments specified on the command line (spv1.0, spv1.1, spv1.2, etc.) to test the different SPIR-V shader variants.

Buffers

An AmberScript buffer represents a set of contiguous bits. This can be used for either image buffers or, what the target API would refer to as a buffer.

Data Types

  • int8
  • int16
  • int32
  • int64
  • uint8
  • uint16
  • uint32
  • uint64
  • float16
  • float
  • double
  • vec[2,3,4]{type}
  • mat[2,3,4]x[2,3,4]{type} (matx)
  • Any of the Image Formats listed below.
  • For any of the non-Image Formats types above appending '[]' will treat the data as an array. e.g. int8[], vec2[]

Sized arrays and structures are not currently representable.

# Filling the buffer with a given initializer. Initializer data must be
# of |type|. Buffers are STD430 by default.
BUFFER {name} DATA_TYPE {type} {STD140 | STD430} {initializer}

# Defines a buffer which is filled with data as specified by the `initializer`.
BUFFER {name} DATA_TYPE {type} {STD140 | STD430} SIZE _size_in_items_ \
    {initializer}

# Deprecated
# Defines a buffer with width and height and filled by data as specified by the
# `initializer`.
BUFFER {name} DATA_TYPE {type} {STD140 | STD430} WIDTH {w} HEIGHT {h} \
  {initializer}

# Defines a buffer which is filled with binary data from a file specified
# by `FILE`.
BUFFER {name} DATA_TYPE {type} {STD140 | STD430} SIZE _size_in_items_ \
    FILE BINARY {file_name}

# Defines a buffer which is filled with text data parsed from a file specified
# by `FILE`.
BUFFER {name} DATA_TYPE {type} {STD140 | STD430} SIZE _size_in_items_ \
    FILE TEXT {file_name}

# Creates a buffer which will store the given `FORMAT` of data. These
# buffers are used as image and depth buffers in the `PIPELINE` commands.
# The buffer will be sized based on the `RENDER_SIZE` of the `PIPELINE`.
# For multisampled images use value greater than one for `SAMPLES`. Allowed
# sample counts are 1, 2, 4, 8, 16, 32, and 64. Note that Amber doesn't
# preserve multisampled images across pipelines.
BUFFER {name} FORMAT {format_string} \
    [ MIP_LEVELS _mip_levels_ (default 1) ] \
    [ SAMPLES _samples_ (default 1) ]

# Load buffer data from a PNG image with file name specified by `FILE`.
# The file path is relative to the script file being run. Format specified
# by `FORMAT` must match the image format.
BUFFER {name} FORMAT {format_string} FILE PNG {file_name.png}

Images

An AmberScript image is a specialized buffer that specifies image-specific attributes.

Dimensionality
  • DIM_1D -- A 1-dimensional image
  • DIM_2D -- A 2-dimensional image
  • DIM_3D -- A 3-dimensional image
# Specify an image buffer with a format. HEIGHT is necessary for DIM_2D and
# DIM_3D. DEPTH is necessary for DIM_3D.
IMAGE {name} FORMAT {format_string} [ MIP_LEVELS _mip_levels_ (default 1) ] \
    [ SAMPLES _samples_ (default 1) ] \
    {dimensionality} \
    WIDTH {w} [ HEIGHT {h} [ DEPTH {d} ] ] \
    {initializer}

# Specify an image buffer with a data type. HEIGHT is necessary for DIM_2D and
# DIM_3D. DEPTH is necessary for DIM_3D.
IMAGE {name} DATA_TYPE {type} {dimensionality} \
    WIDTH {w} [ HEIGHT {h} [ DEPTH {d} ] ] \
    {intializer}

Buffer Initializers

# Filling the buffer with a given set of data. The values must be
# of the correct type. The data can be provided as the type or as a hex
# value.
DATA
_value_+
END

```groovy
# Fill the buffer with a single value.
FILL _value_

# Fill the buffer with an increasing value from |start| increasing by |inc|.
# Floating point data uses floating point addition to generate increasing
# values. Likewise, integer data uses integer addition to generate increasing
# values.
SERIES_FROM _start_ INC_BY _inc_

Buffer Copy

# Copies all data, values and memory from |buffer_from| to |buffer_to|.
# Both buffers must be declared, and of the same data type.
# Buffers used as copy destination can be used only as copy destination, and as
# argument to an EXPECT command.
COPY {buffer_from} TO {buffer_to}

Samplers

Samplers are used for sampling buffers that are bound to a pipeline as sampled image or combined image sampler.

Filter types

  • nearest
  • linear

Address modes

  • repeat
  • mirrored_repeat
  • clamp_to_edge
  • clamp_to_border
  • mirrored_clamp_to_edge

Border colors

  • float_transparent_black
  • int_transparent_black
  • float_opaque_black
  • int_opaque_black
  • float_opaque_white
  • int_opaque_white

# Creates a sampler with |name|.
SAMPLER {name} \
    [ MAG_FILTER {filter_type} (default nearest) ] \
    [ MIN_FILTER {filter_type} (default nearest) ] \
    [ ADDRESS_MODE_U {address_mode} (default repeat) ] \
    [ ADDRESS_MODE_V {address_mode} (default repeat) ] \
    [ ADDRESS_MODE_W {address_mode} (default repeat) ] \
    [ BORDER_COLOR {border_color} (default float_transparent_black) ] \
    [ MIN_LOD _val_ (default 0.0) ] \
    [ MAX_LOD _val_ (default 1.0) ] \
    [ NORMALIZED_COORDS | UNNORMALIZED_COORDS (default NORMALIZED_COORDS) ]

Note: unnormalized coordinates will override MIN_LOD and MAX_LOD to 0.0.

OpenCL Literal Samplers

Literal constant samplers defined in the OpenCL program are automatically generated and bound to the pipeline in Amber.

Note: currently the border color is always transparent black.

Note: the addressing mode is used for all coordinates currently. Arrayed images should use clamp_to_edge for the array index.

Pipelines

Pipeline type

  • compute
  • graphics
# The PIPELINE command creates a pipeline. This can be either compute or
# graphics. Shaders are attached to the pipeline at pipeline creation time.
PIPELINE {pipeline_type} {pipeline_name}
...
END

# Create a pipeline and inherit from a previously declared pipeline.
DERIVE_PIPELINE {pipeline_name} FROM {parent_pipeline}
...
END

Pipeline Content

The following commands are all specified within the PIPELINE command.

  # Attach the shader provided by |name_of_shader| to the pipeline with an
  # entry point name of |name|. The provided shader for ATTACH must _not_ be
  # a 'multi' shader.
  ATTACH {name_of_shader} \
      [ ENTRY_POINT {name} (default "main") ]

  # Attach a 'multi' shader to the pipeline of |shader_type| and use the entry
  # point with |name|. The provided shader _must_ be a 'multi' shader.
  ATTACH {name_of_multi_shader} TYPE {shader_type} ENTRY_POINT {name}

  # Attach specialized shader. Specialization can be specified multiple times.
  # Specialization values must be a 32-bit type. Shader type and entry point
  # must be specified prior to specializing the shader.
  ATTACH {name_of_shader} SPECIALIZE _id_ AS uint32 _value_
  ATTACH {name_of_shader} \
      SPECIALIZE _id_ AS uint32 _value_ \
      SPECIALIZE _id_ AS float _value_
  # Set the SPIRV-Tools optimization passes to use for a given shader. The
  # default is to run no optimization passes.
  SHADER_OPTIMIZATION {shader_name}
    {optimization_name}+
  END
  # Set the compile options used to compile the given shader. Options are parsed
  # the same as on the command line. Currently, only supported for OPENCL-C shaders.
  COMPILE_OPTIONS {shader_name}
    {option}+
  END
  # Set the polygon mode used for all drawing with the pipeline.
  # |mode| is fill, line, or point and it defaults to fill.
  POLYGON_MODE {mode}

Compare operations

  • never
  • less
  • equal
  • less_or_equal
  • greater
  • not_equal
  • greater_or_equal
  • always
  # Set depth test settings. All enable options are specified with keywords on and off.
  # BOUNDS and BIAS values are specified with decimal numbers. |compare_op| is selected
  # from the list of compare operations above.
  DEPTH
    TEST {test_enable}
    WRITE {write_enable}
    COMPARE_OP {compare_op}
    CLAMP {clamp_enable}
    BOUNDS min {bound_min} max {bounds_max}
    BIAS constant {bias_constant} clamp {bias_clamp} slope {bias_slope}
  END

Stencil operations

  • keep
  • replace
  • increment_and_clamp
  • decrement_and_clamp
  • invert
  • increment_and_wrap
  • decrement_and_wrap
  # Set stencil test settings. |face| can be front, back, or front_and_back.
  # |test_enable| is either on or off and affects both faces. |fail_op|, |pass_op|,
  # and |depth_fail_op| are selected from the stencil operations table above,
  # and |compare_op| from the compare operations table. |compare_mask|, |write_mask|,
  # and |reference| are 8bit unsigned integer values (range 0..255).
  STENCIL {face}
    TEST {test_enable}
    FAIL_OP {fail_op}
    PASS_OP {pass_op}
    DEPTH_FAIL_OP {depth_fail_op}
    COMPARE_OP {compare_op}
    COMPARE_MASK {compare_mask}
    WRITE_MASK {write_mask}
    REFERENCE {reference}
  END
  # Set the size of the render buffers. |width| and |height| are integers and
  # default to 250x250.
  FRAMEBUFFER_SIZE _width_ _height_
  # Set subgroup size control setting. Require that subgroups must be launched
  # with all invocations active for given shader. Allow SubgroupSize to vary
  # for given shader. Require a specific SubgroupSize the for given shader.
  # |fully_populated_enable| and |varying_size_enable| can be on or off.
  # |subgroup_size| can be set one of the values below:
  #  - a power-of-two integer that _must_ be greater or equal to minSubgroupSize
  #    and be less than or equal to maxSubgroupSize
  # - MIN to set the required subgroup size to the minSubgroupSize
  # - MAX to set the required subgroup size to the maxSubgroupSize
  SUBROUP {name_of_shader}
    FULLY_POPULATED {fully_populated_enable}
    VARYING_SIZE {varying_size_enable}
    REQUIRED_SIZE {subgroup_size}
  END

Pipeline Buffers

Buffer Types

  • uniform
  • storage
  • uniform_dynamic
  • storage_dynamic
  • uniform_texel_buffer
  • storage_texel_buffer

TODO(dsinclair): Sync the BufferTypes with the list of Vulkan Descriptor types.

A pipeline can have buffers or samplers bound. This includes buffers to contain image attachment content, depth/stencil content, uniform buffers, etc.

  # Attach |buffer_name| as an output color attachment at location |idx|.
  # The provided buffer must be a `FORMAT` buffer. If no color attachments are
  # provided a single attachment with format `B8G8R8A8_UNORM` will be created
  # for graphics pipelines. The MIP level will have a base of |level|.
  BIND BUFFER {buffer_name} AS color LOCATION _idx_ \
      [ BASE_MIP_LEVEL _level_ (default 0) ]

  # Attach |buffer_name| as the depth/stencil buffer. The provided buffer must
  # be a `FORMAT` buffer. If no depth/stencil buffer is specified a default
  # buffer of format `D32_SFLOAT_S8_UINT` will be created for graphics
  # pipelines.
  BIND BUFFER {buffer_name} AS depth_stencil

  # Attach |buffer_name| as the push_constant buffer. There can be only one
  # push constant buffer attached to a pipeline.
  BIND BUFFER {buffer_name} AS push_constant

  # Bind OpenCL argument buffer by name. Specifying the buffer type is optional.
  # Amber will set the type as appropriate for the argument buffer. All uses
  # of the buffer must have a consistent |buffer_type| across all pipelines.
  BIND BUFFER {buffer_name} [ AS {buffer_type} (default computed)] \
      KERNEL ARG_NAME _name_

  # Bind OpenCL argument buffer by argument ordinal. Arguments use 0-based
  # numbering. Specifying the buffer type is optional. Amber will set the
  # type as appropriate for the argument buffer. All uses of the buffer
  # must have a consistent |buffer_type| across all pipelines.
  BIND BUFFER {buffer_name} [ AS {buffer_type} (default computed)] \
      KERNEL ARG_NUMBER _number_

  # Bind OpenCL argument sampler by argument name.
  BIND SAMPLER {sampler_name} KERNEL ARG_NAME _name_

  # Bind OpenCL argument sampler by argument ordinal. Arguments use 0-based
  # numbering.
  BIND SAMPLER {sampler_name} KERNEL ARG_NUMBER _number_

All BIND BUFFER and BIND SAMPLER commands below define a descriptor set and binding ID. These commands can be replaced with BIND BUFFER_ARRAY and BIND SAMPLER_ARRAY commands. In these cases multiple buffer or sampler names need to be provided, separated by spaces. This creates a descriptor array of buffers or samplers bound to the same descriptor set and binding ID. An array of offsets should be provided via OFFSET offset1 offset2 ... when using dynamic buffers with BUFFER_ARRAY.

  # Bind the buffer of the given |buffer_type| at the given descriptor set
  # and binding. The buffer will use a start index of 0.
  BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {buffer_type} DESCRIPTOR_SET _id_ \
       BINDING _id_

  # Attach |buffer_name| as a storage image. The MIP level will have a base
  # value of |level|.
  BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS storage_image \
      DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ]

  # Attach |buffer_name| as a sampled image.  The MIP level will have a base
  # value of |level|.
  BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS sampled_image \
      DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ]

  # Attach |buffer_name| as a combined image sampler. A sampler |sampler_name|
  # must also be specified. The MIP level will have a base value of 0.
  BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS combined_image_sampler SAMPLER {sampler_name} \
      DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ]

  # Bind the sampler at the given descriptor set and binding.
  BIND {SAMPLER | SAMPLER_ARRAY} {sampler_name} DESCRIPTOR_SET _id_ BINDING _id_

  # Bind |buffer_name| as dynamic uniform/storage buffer at the given descriptor set
  # and binding. The buffer will use a start index of |offset|.
  BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {uniform_dynamic | storage_dynamic} \
       DESCRIPTOR_SET _id_ BINDING _id_ OFFSET _offset_
  # Set |buffer_name| as the vertex data at location |val|. RATE defines the
  # input rate for vertex attribute reading. OFFSET sets the byte offset for the
  # vertex data within the buffer |buffer_name|, which by default is 0. FORMAT
  # sets the vertex buffer format, which by default is the format of the buffer
  # |buffer_name|. STRIDE sets the byte stride, which by default is the stride
  # of the format (set explicitly via FORMAT or from the format of the buffer
  # |buffer_name|).
  VERTEX_DATA {buffer_name} LOCATION _val_ [ RATE { vertex | instance } (default vertex) ] \
        [ FORMAT {format} ] [ OFFSET {offset} ] [ STRIDE {stride} ]

  # Set |buffer_name| as the index data to use for `INDEXED` draw commands.
  INDEX_DATA {buffer_name}

OpenCL Plain-Old-Data Arguments

OpenCL kernels can have plain-old-data (pod or pod_ubo in the desriptor map) arguments set their data via this command. Amber will generate the appropriate buffers for the pipeline populated with the specified data.

  # Set argument |name| to |data_type| with value |val|.
  SET KERNEL ARG_NAME _name_ AS {data_type} _val_

  # Set argument |number| to |data_type| with value |val|.
  # Arguments use 0-based numbering.
  SET KERNEL ARG_NUMBER _number_ AS {data_type} _val_

Topologies

  • POINT_LIST
  • LINE_LIST
  • LINE_LIST_WITH_ADJACENCY
  • LINE_STRIP
  • LINE_STRIP_WITH_ADJACENCY
  • TRIANGLE_LIST
  • TRIANGLE_LIST_WITH_ADJACENCY
  • TRIANGLE_STRIP
  • TRIANGLE_STRIP_WITH_ADJACENCY
  • TRIANGLE_fan
  • PATCH_LIST

Run a pipeline.

When running a DRAW_ARRAY command, you must attach the vertex data to the PIPELINE with the VERTEX_DATA command.

To run an indexed draw, attach the index data to the PIPELINE with an INDEX_DATA command.

For the commands which take a START_IDX and a COUNT they can be left off the command (although, START_IDX is required if COUNT is provided). The default value for START_IDX is 0. The default value for COUNT is the item count of vertex buffer minus the START_IDX. The same applies to START_INSTANCE (default 0) and INSTANCE_COUNT (default 1).

# Run the given |pipeline_name| which must be a `compute` pipeline. The
# pipeline will be run with the given number of workgroups in the |x|, |y|, |z|
# dimensions. Each of the x, y and z values must be a uint32.
RUN {pipeline_name} _x_ _y_ _z_
# Run the given |pipeline_name| which must be a `graphics` pipeline. The
# rectangle at |x|, |y|, |width|x|height| will be rendered. Ignores VERTEX_DATA
# and INDEX_DATA on the given pipeline.
RUN {pipeline_name} \
  DRAW_RECT POS _x_in_pixels_ _y_in_pixels_ \
  SIZE _width_in_pixels_ _height_in_pixels_
# Run the given |pipeline_name| which must be a `graphics` pipeline. The
# grid at |x|, |y|, |width|x|height|, |columns|x|rows| will be rendered.
# Ignores VERTEX_DATA and INDEX_DATA on the given pipeline.
# For columns, rows of (5, 4) a total of 5*4=20 rectangles will be drawn.
RUN {pipeline_name} \
  DRAW_GRID POS _x_in_pixels_ _y_in_pixels_ \
  SIZE _width_in_pixels_ _height_in_pixels_ \
  CELLS _columns_of_cells_ _rows_of_cells_
# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
# data must be attached to the pipeline.

# A start index of |value| will be used and the count of |count_value| items
# will be processed. The draw is instanced if |inst_count_value| is greater
# than one. In case of instanced draw |inst_value| controls the starting
# instance ID.
RUN {pipeline_name} DRAW_ARRAY AS {topology} \
    [ START_IDX _value_ (default 0) ] \
    [ COUNT _count_value_ (default vertex_buffer size - start_idx) ] \
    [ START_INSTANCE _inst_value_ (default 0) ] \
    [ INSTANCE_COUNT _inst_count_value_ (default 1) ]
# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
# data and  index data must be attached to the pipeline. The vertices will be
# drawn using the given |topology|.
#
# A start index of |value| will be used and the count of |count_value| items
# will be processed. The draw is instanced if |inst_count_value| is greater
# than one. In case of instanced draw |inst_value| controls the starting
# instance ID.
RUN {pipeline_name} DRAW_ARRAY AS {topology} INDEXED \
    [ START_IDX _value_ (default 0) ] \
    [ COUNT _count_value_ (default index_buffer size - start_idx) ] \
    [ START_INSTANCE _inst_value_ (default 0) ] \
    [ INSTANCE_COUNT _inst_count_value_ (default 1) ]

Repeating commands

# It is sometimes useful to run a given draw command multiple times. This can be
# to detect deterministic rendering or other features.
REPEAT {count}
{command}+
END

The commands which can be used inside a REPEAT block are:

  • CLEAR
  • CLEAR_COLOR
  • CLEAR_DEPTH
  • CLEAR_STENCIL
  • COPY
  • EXPECT
  • RUN

Commands

# Sets the clear color to use for |pipeline| which must be a graphics
# pipeline. The colors are integers from 0 - 255.  Defaults to (0, 0, 0, 0)
CLEAR_COLOR {pipeline} _r (0 - 255)_ _g (0 - 255)_ _b (0 - 255)_ _a (0 - 255)_

# Sets the depth clear value to use for |pipeline| which must be a graphics
# pipeline. |value| must be a decimal number.
CLEAR_DEPTH {pipeline} _value_

# Sets the stencil clear value to use for |pipeline| which must be a graphics
# pipeline. |value| must be an integer from 0 - 255.
CLEAR_STENCIL {pipeline} _value_

# Instructs the |pipeline| which must be a graphics pipeline to execute the
# clear command.
CLEAR {pipeline}

Expectations

Comparators

  • EQ
  • NE
  • LT
  • LE
  • GT
  • GE
  • EQ_RGB
  • EQ_RGBA
  • EQ_BUFFER
  • RMSE_BUFFER
  • EQ_HISTOGRAM_EMD_BUFFER
# Checks that |buffer_name| at |x| has the given |value|s when compared
# with the given |comparator|.
EXPECT {buffer_name} IDX _x_ {comparator} _value_+

# Checks that |buffer_name| at |x| has values within |tolerance| of |value|
# The |tolerance| can be specified as 1-4 float values separated by spaces.
# The tolerances may be given as a percentage by placing a '%' symbol after
# the value. If less tolerance values are provided then are needed for a given
# data component the default tolerance will be applied.
EXPECT {buffer_name} IDX _x_ TOLERANCE _tolerance_{1,4} EQ _value_+

# Checks that |buffer_name| at |x|, |y| for |width|x|height| pixels has the
# given |r|, |g|, |b| values. Each r, g, b value is an integer from 0-255.
EXPECT {buffer_name} IDX _x_in_pixels_ _y_in_pixels_ \
  SIZE _width_in_pixels_ _height_in_pixels_ \
  EQ_RGB _r (0 - 255)_ _g (0 - 255)_ _b (0 - 255)_

# Checks that |buffer_name| at |x|, |y| for |width|x|height| pixels has the
# given |r|, |g|, |b|, |a| values. Each r, g, b, a value is an integer
# from 0-255.
EXPECT {buffer_name} IDX _x_in_pixels_ _y_in_pixels_ \
  SIZE _width_in_pixels_ _height_in_pixels_ \
  EQ_RGBA _r (0 - 255)_ _g (0 - 255)_ _b (0 - 255)_ _a (0 - 255)_

# Checks that |buffer_1| contents are equal to those of |buffer_2|
EXPECT {buffer_1} EQ_BUFFER {buffer_2}

# Checks that the Root Mean Square Error when comparing |buffer_1| to
# |buffer_2| is less than or equal to |tolerance|. Note, |tolerance| is a
# unit-less number.
EXPECT {buffer_1} RMSE_BUFFER {buffer_2} TOLERANCE _value_

# Checks that the Earth Mover's Distance when comparing histograms of
# |buffer_1| to |buffer_2| is less than or equal to |tolerance|.
# Note, |tolerance| is a unit-less number.
EXPECT {buffer_1} EQ_HISTOGRAM_EMD_BUFFER {buffer_2} TOLERANCE _value_

Examples

Compute Shader

#!amber
# Simple amber compute shader.

SHADER compute kComputeShader GLSL
#version 450

layout(binding = 3) buffer block {
  vec2 values[];
};

void main() {
  values[gl_WorkGroupID.x + gl_WorkGroupID.y * gl_NumWorkGroups.x] =
                gl_WorkGroupID.xy;
}
END  # shader

BUFFER kComputeBuffer DATA_TYPE vec2<int32> SIZE 524288 FILL 0

PIPELINE compute kComputePipeline
  ATTACH kComputeShader
  BIND BUFFER kComputeBuffer AS storage DESCRIPTOR_SET 0 BINDING 3
END  # pipeline

RUN kComputePipeline 256 256 1

# Four corners
EXPECT kComputeBuffer IDX 0 EQ 0 0
EXPECT kComputeBuffer IDX 2040 EQ 255 0
EXPECT kComputeBuffer IDX 522240 EQ 0 255
EXPECT kComputeBuffer IDX 524280 EQ 255 255

# Center
EXPECT kComputeBuffer IDX 263168 EQ 128 128

Entry Points

#!amber

SHADER vertex kVertexShader PASSTHROUGH

SHADER fragment kFragmentShader SPIRV-ASM
              OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450

; two entrypoints
               OpEntryPoint Fragment %red "red" %color
               OpEntryPoint Fragment %green "green" %color

               OpExecutionMode %red OriginUpperLeft
               OpExecutionMode %green OriginUpperLeft
               OpSource GLSL 430
               OpName %red "red"
               OpDecorate %color Location 0
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
      %color = OpVariable %_ptr_Output_v4float Output
    %float_1 = OpConstant %float 1
    %float_0 = OpConstant %float 0
  %red_color = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
%green_color = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1

; this entrypoint outputs a red color
        %red = OpFunction %void None %3
          %5 = OpLabel
               OpStore %color %red_color
               OpReturn
               OpFunctionEnd

; this entrypoint outputs a green color
      %green = OpFunction %void None %3
          %6 = OpLabel
               OpStore %color %green_color
               OpReturn
               OpFunctionEnd
END  # shader

BUFFER kImgBuffer FORMAT R8G8B8A8_UINT

PIPELINE graphics kRedPipeline
  ATTACH kVertexShader ENTRY_POINT main
  SHADER_OPTIMIZATION kVertexShader
    --eliminate-dead-branches
    --merge-return
    --eliminate-dead-code-aggressive
  END
  ATTACH kFragmentShader ENTRY_POINT red

  FRAMEBUFFER_SIZE 256 256
  BIND BUFFER kImgBuffer AS color LOCATION 0
END  # pipeline

PIPELINE graphics kGreenPipeline
  ATTACH kVertexShader
  ATTACH kFragmentShader ENTRY_POINT green

  FRAMEBUFFER_SIZE 256 256
  BIND BUFFER kImgBuffer AS color LOCATION 0
END  # pipeline

RUN kRedPipeline DRAW_RECT POS 0 0 SIZE 256 256
RUN kGreenPipeline DRAW_RECT POS 128 128 SIZE 256 256

EXPECT kImgBuffer IDX 0 0 SIZE 127 127 EQ_RGB 255 0 0
EXPECT kImgBuffer IDX 128 128 SIZE 128 128 EQ_RGB 0 255 0

Buffers

#!amber

SHADER vertex kVertexShader GLSL
  #version 430

  layout(location = 0) in vec4 position;
  layout(location = 1) in vec4 color_in;
  layout(location = 0) out vec4 color_out;

  void main() {
    gl_Position = position;
    color_out = color_in;
  }
END  # shader

SHADER fragment kFragmentShader GLSL
  #version 430

  layout(location = 0) in vec4 color_in;
  layout(location = 0) out vec4 color_out;

  void main() {
    color_out = color_in;
  }
END  # shader

BUFFER kPosData DATA_TYPE vec2<int32> DATA
# Top-left
-1 -1  
 0 -1  
-1  0
 0  0
# Top-right
 0 -1  
 1 -1  
 0  0
 1  0
# Bottom-left
-1  0
 0  0
-1  1
 0  1
# Bottom-right
 0  0
 1  0
 0  1
 1  1
END

BUFFER kColorData DATA_TYPE uint32 DATA
# red
0xff0000ff
0xff0000ff
0xff0000ff
0xff0000ff

# green
0xff00ff00
0xff00ff00
0xff00ff00
0xff00ff00

# blue
0xffff0000
0xffff0000
0xffff0000
0xffff0000

# purple
0xff800080
0xff800080
0xff800080
0xff800080
END

BUFFER kIndices DATA_TYPE int32 DATA
0  1  2    2  1  3
4  5  6    6  5  7
8  9  10   10 9  11
12 13 14   14 13 15
END

PIPELINE graphics kGraphicsPipeline
  ATTACH kVertexShader
  ATTACH kFragmentShader

  VERTEX_DATA kPosData LOCATION 0
  VERTEX_DATA kColorData LOCATION 1
  INDEX_DATA kIndices
END  # pipeline

CLEAR_COLOR kGraphicsPipeline 255 0 0 255
CLEAR kGraphicsPipeline

RUN kGraphicsPipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 24

OpenCL-C Shaders

SHADER compute my_shader OPENCL-C
kernel void line(const int* in, global int* out, int m, int b) {
  *out = *in * m + b;
}
END

BUFFER in_buf DATA_TYPE int32 DATA 4 END
BUFFER out_buf DATA_TYPE int32 DATA 0 END

PIPELINE compute my_pipeline
  ATTACH my_shader ENTRY_POINT line
  COMPILE_OPTIONS
    -cluster-pod-kernel-args
    -pod-ubo
    -constant-args-ubo
    -max-ubo-size=128
  END
  BIND BUFFER in_buf KERNEL ARG_NAME in
  BIND BUFFER out_buf KERNEL ARG_NAME out
  SET KERNEL ARG_NAME m AS int32 3
  SET KERNEL ARG_NAME b AS int32 1
END

RUN my_pipeline 1 1 1

EXPECT out_buf EQ IDX 0 EQ 13

Image Formats

  • A1R5G5B5_UNORM_PACK16
  • A2B10G10R10_SINT_PACK32
  • A2B10G10R10_SNORM_PACK32
  • A2B10G10R10_SSCALED_PACK32
  • A2B10G10R10_UINT_PACK32
  • A2B10G10R10_UNORM_PACK32
  • A2B10G10R10_USCALED_PACK32
  • A2R10G10B10_SINT_PACK32
  • A2R10G10B10_SNORM_PACK32
  • A2R10G10B10_SSCALED_PACK32
  • A2R10G10B10_UINT_PACK32
  • A2R10G10B10_UNORM_PACK32
  • A2R10G10B10_USCALED_PACK32
  • A8B8G8R8_SINT_PACK32
  • A8B8G8R8_SNORM_PACK32
  • A8B8G8R8_SRGB_PACK32
  • A8B8G8R8_SSCALED_PACK32
  • A8B8G8R8_UINT_PACK32
  • A8B8G8R8_UNORM_PACK32
  • A8B8G8R8_USCALED_PACK32
  • B10G11R11_UFLOAT_PACK32
  • B4G4R4A4_UNORM_PACK16
  • B5G5R5A1_UNORM_PACK16
  • B5G6R5_UNORM_PACK16
  • B8G8R8A8_SINT
  • B8G8R8A8_SNORM
  • B8G8R8A8_SRGB
  • B8G8R8A8_SSCALED
  • B8G8R8A8_UINT
  • B8G8R8A8_UNORM
  • B8G8R8A8_USCALED
  • B8G8R8_SINT
  • B8G8R8_SNORM
  • B8G8R8_SRGB
  • B8G8R8_SSCALED
  • B8G8R8_UINT
  • B8G8R8_UNORM
  • B8G8R8_USCALED
  • D16_UNORM
  • D16_UNORM_S8_UINT
  • D24_UNORM_S8_UINT
  • D32_SFLOAT
  • D32_SFLOAT_S8_UINT
  • R16G16B16A16_SFLOAT
  • R16G16B16A16_SINT
  • R16G16B16A16_SNORM
  • R16G16B16A16_SSCALED
  • R16G16B16A16_UINT
  • R16G16B16A16_UNORM
  • R16G16B16A16_USCALED
  • R16G16B16_SFLOAT
  • R16G16B16_SINT
  • R16G16B16_SNORM
  • R16G16B16_SSCALED
  • R16G16B16_UINT
  • R16G16B16_UNORM
  • R16G16B16_USCALED
  • R16G16_SFLOAT
  • R16G16_SINT
  • R16G16_SNORM
  • R16G16_SSCALED
  • R16G16_UINT
  • R16G16_UNORM
  • R16G16_USCALED
  • R16_SFLOAT
  • R16_SINT
  • R16_SNORM
  • R16_SSCALED
  • R16_UINT
  • R16_UNORM
  • R16_USCALED
  • R32G32B32A32_SFLOAT
  • R32G32B32A32_SINT
  • R32G32B32A32_UINT
  • R32G32B32_SFLOAT
  • R32G32B32_SINT
  • R32G32B32_UINT
  • R32G32_SFLOAT
  • R32G32_SINT
  • R32G32_UINT
  • R32_SFLOAT
  • R32_SINT
  • R32_UINT
  • R4G4B4A4_UNORM_PACK16
  • R4G4_UNORM_PACK8
  • R5G5B5A1_UNORM_PACK16
  • R5G6B5_UNORM_PACK16
  • R64G64B64A64_SFLOAT
  • R64G64B64A64_SINT
  • R64G64B64A64_UINT
  • R64G64B64_SFLOAT
  • R64G64B64_SINT
  • R64G64B64_UINT
  • R64G64_SFLOAT
  • R64G64_SINT
  • R64G64_UINT
  • R64_SFLOAT
  • R64_SINT
  • R64_UINT
  • R8G8B8A8_SINT
  • R8G8B8A8_SNORM
  • R8G8B8A8_SRGB
  • R8G8B8A8_SSCALED
  • R8G8B8A8_UINT
  • R8G8B8A8_UNORM
  • R8G8B8A8_USCALED
  • R8G8B8_SINT
  • R8G8B8_SNORM
  • R8G8B8_SRGB
  • R8G8B8_SSCALED
  • R8G8B8_UINT
  • R8G8B8_UNORM
  • R8G8B8_USCALED
  • R8G8_SINT
  • R8G8_SNORM
  • R8G8_SRGB
  • R8G8_SSCALED
  • R8G8_UINT
  • R8G8_UNORM
  • R8G8_USCALED
  • R8_SINT
  • R8_SNORM
  • R8_SRGB
  • R8_SSCALED
  • R8_UINT
  • R8_UNORM
  • R8_USCALED
  • S8_UINT
  • X8_D24_UNORM_PACK32