# 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. 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. ### Shaders #### 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) -- future * SPIRV-ASM (with spirv-as) * SPIRV-HEX (decoded straight to spv) * OPENCL-C (with clspv)  --- potentially? -- future ``` # Creates a passthrough vertex shader. The shader passes the vec4 at input # location 0 through to the `gl_Position`. SHADER vertex PASSTHROUGH # Creates a shader of |shader_type| with the given |shader_name|. The shader # will be of |shader_format|. The shader should then be inlined before the # |END| tag. SHADER ... END ``` ### 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 * float * double * vec[2,3,4]\ * mat[2,3,4]x[2,3,4]\    -- useful? TODO(dneto): Support half-precision floating point. Sized arrays and structures are not currently representable. ``` # Filling the buffer with a given set of data. The values must be # of data. The data can be provided as the type or as a hex value. BUFFER DATA_TYPE DATA + END # Defines a buffer which is filled with data as specified by the `initializer`. BUFFER DATA_TYPE SIZE # 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`. BUFFER FORMAT ``` TODO(dsinclair): Does framebuffer need a format attached to it? #### Buffer Initializers ``` # Fill the buffer with a single value. FILL # Fill the buffer with an increasing value from \ increasing by \. # Floating point data uses floating point addition to generate increasting # values. Likewise, integer data uses integer addition to generate increasing # values. SERIES_FROM INC_BY ``` ### 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 ... END ``` ### Pipeline Content The following commands are all specified within the `PIPELINE` command. ``` # Attach the shader provided by |name_of_shader| to the pipeline and set # the entry point to be |name|. The provided shader for ATTACH must _not_ be # a 'multi' shader. ATTACH ENTRY_POINT # Attach the shader provided by |name_of_shader| to the pipeline and set # the entry point to be 'main'. The provided shader for ATTACH must _not_ be # a 'multi' shader. ATTACH # 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 TYPE ENTRY_POINT ``` ``` # Set the SPIRV-Tools optimization passes to use for a given shader. The # default is to run no optimization passes. SHADER_OPTIMIZATION + END ``` ``` # Set the size of the render buffers. |width| and |height| are integers and # default to 250x250. FRAMEBUFFER_SIZE _width_ _height_ ``` ### Pipeline Buffers #### Buffer Types * uniform * storage * sampled * color * depth_stencil TODO(dsinclair): Reserve `input` as a buffer type for input attachments. TODO(dsinclair): Sync the BufferTypes with the list of Vulkan Descriptor types. A `pipeline` can have buffers bound. This includes buffers to contain image attachment content, depth/stencil content, uniform buffers, etc. ``` # Attach |buffer_name| as an output colour attachment at location |idx|. # The provided buffer must be a `FORMAT` buffer. If no colour attachments are # provided a single attachment with format `R8G8B8A8_UINT` will be created. BIND BUFFER AS color LOCATION # 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. BIND BUFFER AS depth_stencil # 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 AS DESCRIPTOR_SET \ BINDING # Bind the buffer of the given |buffer_type| at the given descriptor set # and binding and index at the given value. BIND BUFFER AS DESCRIPTOR_SET \ BINDING IDX # Bind the sampler at the given descriptor set and binding. BIND SAMPLER DESCRIPTOR_SET BINDING ``` ``` # Set |buffer_name| as the vertex data at location |val|. VERTEX_DATA LOCATION # Set |buffer_name| as the index data to use for `INDEXED` draw commands. INDEX_DATA ``` ##### 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`. ``` # 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 # Run the given |pipeline_name| which must be a `graphics` pipeline. The # rectangle at |x|, |y|, |width|x|height| will be rendered. RUN \  DRAW_RECT POS \  SIZE # Run the |pipeline_name| which must be a `graphics` pipeline. The vertex # data must be attached to the pipeline. A start index of 0 will be used # and a count of the number of elements in the vertex buffer. RUN DRAW_ARRAY AS # 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 a count of the number of items from |value| to the end of the vertex # buffer. RUN  DRAW_ARRAY AS START_IDX # 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 a count |count_value| will be used. RUN  DRAW_ARRAY AS START_IDX \ COUNT # 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 0 will be used and the # count will be determined by the size of the index data buffer. RUN DRAW_ARRAY INDEXED AS # 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 will be determined by the size of the index data buffer. RUN DRAW_ARRAY INDEXED AS START_IDX # 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. RUN  DRAW_ARRAY INDEXED AS \ START_IDX COUNT ``` ### Commands ``` # Sets the clear colour to use for |pipeline| which must be a `graphics` # pipeline. The colours are integers from 0 - 255. # TODO(dsinclair): Do we need to allow different types here to handle different # buffer formats? CLEAR_COLOR # Instructs the |pipeline| which must be a `graphics` pipeline to execute the # clear command. CLEAR ``` ### Expectations #### Comparators * EQ * EQ\_FLOAT * NE * NE\_FLOAT * LT * LE * GT * GE * EQ\_RGB * EQ\_RGBA ``` # Checks that |buffer_name| at |x|, |y| has the given |value|s when compared # with the given |comparator|. EXPECT IDX + # Checks that |buffer_name| at |x|, |y| has values within |tolerance| of |value| # when compared with the given |comparator|. The |tolerance| can be specified # as 1-4 float values separated by spaces. EXPECT IDX TOLERANCE {1,4} \ + # 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 IDX \  SIZE \  EQ_RGB ``` ## 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 TYPE vec2 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 image IDX 0 END  # pipeline PIPELINE graphics kGreenPipeline ATTACH kVertexShader ATTACH kFragmentShader ENTRY_POINT green FRAMEBUFFER_SIZE 256 256 BIND BUFFER kImgBuffer AS image IDX 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 TYPE vec2 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 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 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 ``` ### 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