JSON Test Description Specification

Notation

The following section describes the notation in the Scenario Runner.

A class defines a structured type in C terms with typed parameters. The comma separates the typed parameters:

my_class: {
    my_parameter1: its_type,
    my_parameter2: another_type
}

The previous example defines a class called my_class with two parameters, my_parameter1 of type its_type, and my_parameter2 of type another_type.

Parameter types can be regular types, for example, int, float, string, path, enum or boolean. Parameters can also be other user-defined classes. For class types that are defined later in the spec, the class name is prefixed with class. For example:

some_parameter: class some_new_type,
some_new_type: {

}

Arrays of items are wrapped in [] and lists of allowed types are separated with |.

my_array_of_ints: [int],
my_array_of_things: [int | float]

In the previous example, each element in my_array_of_things is a union of int and float types but can also be a union of different classes.

Parameters that are optional have (default=value) postfix decoration on the type. Parameters that are not optional are required to be specified in the JSON file.

Parameters that have a limited set of allowed values are postfixed with = followed by a | separated list of the allowed values. default values must match existing allowed constraints. For example, to define an optional parameter than can only be a value of red, green or blue, we do:

my_parameter:string(default="red")=("red"|"green"|"blue")

Enumerations are similarly specified but without the “”.

my_enum_param:enum = (clockwise | counterclockwise)

Comments in the specification are inlined after //

my_param:int // this param is used by things

Specification

Note

  • You can use absolute paths. If you do not use absolute paths, all paths are relative to the input scenario JSON file parent directory.

  • All parameters defined in the specification are required unless annotated as optional with default=

The root of the JSON file has two blocks.

root: {
    resources: [ class image | class tensor | class buffer | class raw_data |
                 class graph | class shader | class memory_barrier | class buffer_barrier |
                 class buffer_barrier | class tensor_barrier | class image_barrier ],
    commands: [ class dispatch_compute | class dispatch_graph | class dispatch_barrier |
                class mark_boundary ]
}

resources lists all the resources in the test case and each item in the array can be any of the following types:

commands lists all the commands in order of execution or dispatch:

Resources

image

The image resource has the following properties:

image: {
    uid:string, // globally unique identifier for the resource
    format:string, // string name of the VkFormat enum.
    dims:[int], // n-length array of sized for an n-dimension image
    shader_access:enum = (readonly|writeonly|readwrite|image_read) // type of access required by the shader/graph
    mips:int(default=1), // Number of mipmaps. Create an Image with memory allocated for this many level of details. Mipmaps levels are automatically generated.
    src:path(default=""), // optional path to the DDS file to initialize the resource from
    dst:path(default=""), // optional path to the DDS file to write contents to (post execution of commands)
    min_filter:enum = (NEAREST|LINEAR) // sampler setting
    mag_filter:enum = (NEAREST|LINEAR) // sampler setting
    mip_filter:enum = (NEAREST|LINEAR) // sampler setting
    border_address_mode:enum = (CLAMP_EDGE|CLAMP_BORDER|REPEAT|MIRRORED_REPEAT) // sampler setting
    border_color:enum = (FLOAT_TRANSPARENT_BLACK|FLOAT_OPAQUE_BLACK|FLOAT_OPAQUE_WHITE|INT_TRANSPARENT_BLACK|INT_OPAQUE_BLACK|INT_OPAQUE_WHITE|INT_CUSTOM_EXT|FLOAT_CUSTOM_EXT) // sampler setting
    custom_border_color:[int|float], // length 4 array of integer or float values representing an RGBA color value for a custom border.
    memory_group, // optional memory group to share memory object between resources
    tiling:enum = (OPTIMAL|LINEAR), // optional "Tiling" arrangement info of the image resource
}

For a complete list of VkFormat entries, see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkFormat.html however, we will only support those which line up with the DDS format described here: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide.

The following image formats are supported and have corresponding DDS equivalents: VK_FORMAT_R8_SNORM, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8B8_SINT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8_SNORM, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_R8_UNORM, VK_FORMAT_R32_UINT

The following formats have no corresponding DDS equivalents. They may still be used for intermediate images or via memory aliasing: VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R8_BOOL_ARM, VK_FORMAT_R8_UINT, VK_FORMAT_R8_SINT, VK_FORMAT_R16_UINT, VK_FORMAT_R16_SINT, VK_FORMAT_R32_SINT, VK_FORMAT_R64_SINT, VK_FORMAT_R16_SFLOAT

tensor

The tensor resources have the following properties:

tensor: {
    uid:string, // globally unique identifier for the resource
    dims:[int], // n-length array of sized for an n-dimension tensor
    format:string, // string name of the VkFormat enum.
    shader_access:enum = (readonly|writeonly|readwrite) // type of access required by the shader/graph
    src:path(default=""), // optional path to the NumPy file to initialize the resource from
    dst:path(default=""), // optional path to the NumPy file to write contents to (post execution of commands)
    alias_target:AliasTarget, // (Deprecated) optional "AliasTarget" of image resource to share memory with
    memory_group, // optional memory group to share memory object between resources
    tiling:enum = (OPTIMAL|LINEAR), // optional "Tiling" arrangement info of the tensor resource
}

Supported formats for tensors are limited to a subset of the single channel types defined in the VkFormat enum. Currently supported formats are:

bool: VK_FORMAT_R8_BOOL_ARM
uint8: VK_FORMAT_R8_UINT
int8: VK_FORMAT_R8_SINT
uint16: VK_FORMAT_R16_UINT
int16: VK_FORMAT_R16_SINT
uint32: VK_FORMAT_R32_UINT
int32: VK_FORMAT_R32_SINT
int64: VK_FORMAT_R64_SINT
float16: VK_FORMAT_R16_SFLOAT
float32: VK_FORMAT_R32_SFLOAT

To allow for memory aliasing, the following object is needed in each resource: .. code-block:

struct MemoryGroup {
    uid:string(default=""), // unique string defining the shared memory object
}

buffer

The buffer resources map to Storage Buffers in Vulkan®:

buffer: {
    uid:string, // globally unique identifier for the resource
    size:int, // total size of buffer in bytes
    shader_access:enum = (readonly|writeonly|readwrite) // type of access required by the shader/graph
    src:path(default=""), // optional path to the NumPy file to initialize the resource from
    dst:path(default=""), // optional path to the NumPy file to write contents to (post execution of commands)
    memory_group, // optional memory group to share memory object between resources
}

Buffers do not have a format and it is up to the shader to interpret the data in the correct manner.

raw_data

The raw_data represents some data in host memory that is fed to the dispatches via means other than standard resource binding mechanisms for example, push constant or specialization constants.

raw_data: {
    uid:string, // globally unique identifier for the resource
    src:path, // path to the NumPy file to initialize the resource from
}

shader

The shader resource references a SPIR-V™ or GLSL shader file. The runner loads the GLSL and compiles it to SPIR-V™ before handing it to the Vulkan® Runtime.

shader: {
    uid:string, // globally unique identifier for the resource
    src:path, // path to a shader source file
    entry:string(default="main"), // entry point into the shader
    type: enum = (GLSL | SPIR-V), // Type of shader source to expect
    build_options:string(default=""), // Build options to be used when compiling a GLSL shader source
    include_dirs:[string](default=[]), // Shaders include directories
    push_constants_size:int(default=0), // Size in bytes of the push constants used by the shader. Must be a multiple of 4
    specialization_constants: [class specialization_constant](default=), // n-dimension array
}

specialization_constant: {
    id: int,    // id of the specialization constant in the shader
    value: int|float // float or integer value to set the constant to
}

graph

The graph resource is loaded from a VGF file via the SDK parser API.

graph: {
    uid:string, // globally unique identifier for the resource
    src:path, // path to the VGF file to initialize the resource from
    specialization_constants_map: [class specialization_constant_map](default=), // array containing all the specialization constants referenced within a graph.
    shader_substitutions:[class shader_substitutions](default=) // array containing all the shaders to substitute within the graph.
    push_constants_size:int(default=0), // Size in bytes of the push constants used in the graph. Must be a multiple of 4
}

The specialization constant map allows to map specialization constants to multiple shaders within a graph.

specialization_constant_map: {
    specialization_constants:[specialization_constant], // array of specialization constants id-value pair
    shader_target:string // name of the shader node in the graph on which to apply the constants
}

The shader_substitutions parameter is an array of shader_substitution objects. Each shader_substitution describes a placeholder shader node in the graph that will be substituted with an actual shader implementation. The shader substitution occurs during graph parsing and before graph compilation.

shader_substitutions: {
    {
        shader_ref: string, // reference to the shader resource (by UID) to use for the substitution
        target: string, // name of the placeholder shader node in the graph to replace
    }
}

Barriers

The barrier type resources represents memory, image, tensor and buffer barriers in Vulkan® which are inserted by the dispatch_barrier command. You must ensure that implicit barriers are disabled for the target pipeline in the corresponding dispatch command.

memory_barrier: {
    uid:string, // globally unique identifier for the resource
    src_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the source
    dst_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the destination
    src_stage:[enum(GRAPH|COMPUTE|ALL)], // source pipeline stages
    dst_stage:[enum(GRAPH|COMPUTE|ALL)], // destination pipeline stages
}
buffer_barrier: {
    uid:string, // globally unique identifier for the resource
    buffer_resource:string, // reference to the buffer resource
    size:int // total size of the buffer affected by this barrier in bytes
    src_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the source
    dst_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the destination
    src_stage:[enum(GRAPH|COMPUTE|ALL)], // source pipeline stages
    dst_stage:[enum(GRAPH|COMPUTE|ALL)], // destination pipeline stages
    offset:int(default=0), // the offset in bytes into the backing memory for the buffer affected by this barrier
}
image_barrier: {
    uid:string, // globally unique identifier for the resource
    src_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the source
    dst_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the destination
    old_layout:enum = (IMAGE_LAYOUT_TENSOR_ALIASING|IMAGE_LAYOUT_GENERAL|IMAGE_LAYOUT_UNDEFINED), // the old image layout in an image layout transition
    new_layout:enum = (IMAGE_LAYOUT_TENSOR_ALIASING|IMAGE_LAYOUT_GENERAL|IMAGE_LAYOUT_UNDEFINED), // the new image layout in an image layout transition
    image_resource:string, // reference to the image resource
    subresource_range: class subresource_range // the subresource range within the image affected by this barrier
    src_stage:[enum(GRAPH|COMPUTE|ALL)], // source pipeline stages
    dst_stage:[enum(GRAPH|COMPUTE|ALL)], // destination pipeline stages
}

The subresource_range resource maps the image subresources of an image affected by an image barrier:

subresource_range: {
    base_mip_level:int (default=0), // the first mipmap level accessible to view
    level_count:int (default=1), // number of mipmap levels accessible
    base_array_layer:int (default=0), // the first array layer accessible to view
    layer_count:int (default=1) // the number of array layers accessible
}
tensor_barrier: {
    uid:string, // globally unique identifier for the resource
    tensor_resource:string, // reference to the tensor resource
    src_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the source
    dst_access:enum(ACCESS_MEMORY_WRITE|ACCESS_MEMORY_READ|ACCESS_GRAPH_WRITE|ACCESS_GRAPH_READ|ACCESS_COMPUTE_SHADER_WRITE|ACCESS_COMPUTE_SHADER_READ), // memory access type from the destination
    src_stage:[enum(GRAPH|COMPUTE|ALL)], // source pipeline stages
    dst_stage:[enum(GRAPH|COMPUTE|ALL)], // destination pipeline stages
}

Commands

Commands are executed in order of appearance in the JSON file. Initial implementation will execute in-order with no overlap.

dispatch_compute

The dispatch_compute command dispatches a compute shader to execute.

dispatch_compute: {
    shader_ref: string, // reference to the shader resource
    push_data_ref: string(default=""), // reference to raw_data resource containing the push_constants data
    rangeND: [int], // 3-dimension dispatch range expressed as number of local workgroups per dimension
    bindings: [class binding] // array of bindings mapping a resource reference to a descriptor set and id
    implicit_barrier:boolean(default=true) // inclusion of implicit memory barrier
}

While push_constants can technically support other stages, we focus on a single push_constant buffer for the compute stage only.

binding: {
    set: int, // descriptor set id
    id: int, // descriptor id in the set
    resource_ref: string // named reference to the resource to bind
    lod: int(default=0) // Optional. Level of details index. In case of an Image resource with mipmaps could be used to bind specific level of details.
    descriptor_type:enum(default=VK_DESCRIPTOR_TYPE_AUTO) = (VK_DESCRIPTOR_TYPE_AUTO|VK_DESCRIPTOR_TYPE_STORAGE_IMAGE), // descriptor type for the resource in current dispatch. Needed only when descriptor type cannot be correctly inferred
}

dispatch_graph

The dispatch_graph command dispatches a compiled graph using the proposed ML extensions for Vulkan®.

dispatch_graph: {
    graph_ref: string, // reference to the graph resource
    push_constants: [class push_constant_map](default=), // mappings between push constants data and the target shader node.
    bindings: [class binding] // array of bindings mapping a resource reference to a descriptor set and id. These bindings describe the inputs and outputs to the graph.
    implicit_barrier:boolean(defualt=true) // inclusion of implicit memory barrier
}

push_constant_map: {
    push_data_ref: string, // reference to raw_data resource containing the push_constants data
    shader_target: string, // name of the shader node in the graph to apply the push constants to
}

dispatch_barrier

The dispatch_barrier command dispatches memory, image and buffer barriers.

dispatch_barrier: {
    image_barrier_refs:[string] (default=[]), // array of image barrier uids
    tensor_barrier_refs:[string] (default=[]), // array of tensor barrier uids
    memory_barrier_refs:[string] (default=[]), // array of memory barrier uids
    buffer_barrier_refs:[string] (default=[]) // array of buffer barrier uids
}

mark_boundary

The mark_boundary command defines the end of a ‘frame’ and explicitly submits JSON commands in the frame. Tools can use this information to identify frames and capture targeted resources specified in the command options. The end of a ‘frame’ implicitly marks the start of the next frame.

mark_boundary: {
    resources:[string], // array of named references to the resources to capture
    frame_id: int, // Id to be assigned to the frame
}

Examples

Example resource descriptors:

"resources": [
    "image": {
        "uid": "InputColorBuffer0",
        "dims": [256, 256],
        "mips": "false",
        "format": "VK_FORMAT_R8G8B8A8_SRGB",
        "src": "./color.dds",
        "dst": "",
        "shader_access": "readonly",
        "border_color": "INT_TRANSPARENT_BLACK"
     },
    "tensor": {
        "uid": "intermediate0",
        "dims" : [256,256],
        "data_type": "VK_FORMAT_R32_SFLOAT",
        "shader_access": "readwrite",
    },
    "tensor": {
        "uid": "weights0",
        "dims" : [10,10],
        "data_type": "VK_FORMAT_R32_SFLOAT",
        "src": "./weights.npy",
        "shader_access": "readonly",
    }
]

Shader modules that are used in dispatch commands can be defined as resources. They can have associated push_constants which are also provided via raw_data resource definitions.

Shader resource example:

"raw_data": {
    "uid": "prep_push_constants",
    "src": "./prep_pc.npy"
},
"shader": {
    "uid": "prep_shader_8x8",
    "src": "preprocess.glsl",
    "type": "GLSL",
    "push_constants_size": 16,
    "specialization_constants": [
        {
            id: 0,
            value: 8
        },
        {
            id: 1,
            value: 8
        }
    ]
    //...
}

You can load complete graphs via the VGF Library Decoder. The complete graphs are specified as VGF files. For some use cases, you should substitute placeholder shader nodes in the graph for specific shaders.

"graph": {
    "uid": "graph_file"
    "src": "./graph_file.vgf",
    "shader_substitutions" : [
        {"shader_ref":"prep_shader", "target": "tfl_custom_pre_node"}
        {"shader_ref":"post_shader", "target": "tfl_custom_post_node"}
    ],
    //...
}

The behavior of the scenario is defined with the Commands section. These commands are processed in order of appearance in the file.

"commands" : [
    "dispatch_compute": {
        "shader_ref": "degamma_shader",
        "push_data_ref": "gamma_consts",
        "bindings": [
            {"set": 0, "id": 0, "resource_ref":"InputColorBuffer0"},
            {"set": 0, "id": 1, "resource_ref":"intermediate0"}
        ],
        "rangeND": [32, 32]
    },
    "dispatch_graph": {
        "graph_ref": "NN_graph",
        "bindings": [
            {"set": 0, "id": 0, "resource_ref":"intermediate0"},
            {"set": 0, "id": 1, "resource_ref":"vectors"},
            {"set": 0, "id": 2, "resource_ref":"depth"},
            {"set": 0, "id": 3, "resource_ref":"upscaled0"}
        ]
    },
    "dispatch_compute": {
        "shader_ref": "post_shader",
        "push_data_ref: "inv_projection",
        "bindings": [
            {"set": 0, "id": 0, "resource_ref":"upscaled0"},
            {"set": 0, "id": 1, "resource_ref":"result0"},
        ],
        "rangeND": [64, 64]
    }
]

You can use the mark_boundary command to signal the completion of a frame and explicitly submit all commands in this frame.

"commands": [
    {
        "dispatch_compute": {
            "bindings": [
                {
                    "id": 0,
                    "set": 0,
                    "resource_ref": "inBufferA"
                },
                {
                    "id": 1,
                    "set": 0,
                    "resource_ref": "inBufferB"
                },
                {
                    "id": 2,
                    "set": 1,
                    "resource_ref": "outBufferAdd"
                }
            ],
            "rangeND": [10],
            "shader_ref": "add_shader"
        }
    },
    {
        "mark_boundary":{
            "resources": [
              "inBufferA",
              "inBufferB"
            ],
            "frame_id" : 0
        }
    }
],
"resources": [
    {
        "shader": {
            "entry": "main",
            "src": "path/to/add_shader.spv",
            "type": "SPIR-V",
            "uid": "add_shader"
        }
    },
    {
        "buffer": {
            "shader_access": "readonly",
            "size": 40,
            "src": "path/to/inBufferA.npy",
            "uid": "inBufferA"
        }
    },
    {
        "buffer": {
            "shader_access": "readonly",
            "size": 40,
            "src": "path/to/inBufferB.npy",
            "uid": "inBufferB"
        }
    },
    {
        "buffer": {
            "dst": "path/to/outBufferAdd.npy",
            "shader_access": "readwrite",
            "size": 40,
            "uid": "outBufferAdd"
        }
    }
]