5

Is is possible to have an array of specialization constants such that the glsl code looks similar to the following:

layout(constant_id = 0) const vec2 arr[2] = vec2[] (
    vec2(2.0f, 2.0f),
    vec2(4.0f, 4.0f)
);

or, alternatively:

layout(constant_id = 0) const float arr[4] = float[] (
    2.0f, 2.0f,
    4.0f, 4.0f
);

As far as I have read there is no limit to the number of specialization constants that can be used so it feels strange that it wouldn't be possible but when I attempt the above the SPIR-V compiler notifies me that 'constant_id' can only be applied to a scalar. Currently I am using a uniform buffer to provide the data but I would like to eliminate the backed buffer and the need to bind the buffer before drawing as well as allow the system to optimize the code during pipeline creation if its possible.

Ryoku
  • 397
  • 2
  • 16
  • If these really are unchanging compile-time constants, then worst-case, you can read the SPIR-V, find the array of interest, and just manipulate the values in-situ before passing them off to Vulkan. – Nicol Bolas Mar 02 '21 at 05:07
  • @NicolBolas The data changes infrequently and only when the pipeline needs to be reconstructed anyways. Just to make sure I understand what you are suggesting, you think I should scan the SIR-V in its character array format after it was read in from its file, and replace the array manually. It's a good idea if there is no obvious solution I wasn't even thinking that. – Ryoku Mar 02 '21 at 05:22
  • "*scan the SIR-V in its character array format*" Word array. SPIR-V is [defined as an array of 32-bit words](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_physicallayout_a_physical_layout_of_a_spir_v_module_and_instruction). But yes, that's the general idea. – Nicol Bolas Mar 02 '21 at 05:23

1 Answers1

5

The shading languages (both Vulkan-GLSL and SPIR-V) makes something of a distinction between the definition of a specialization constant within the shader and the interface for specializing those constants. But they go about this process in different ways.

In both languages, the external interface to a specialization constant only works on scalar values. That is, though you can set multiple constants to values, the constants you're setting are each a single scalar.

SPIR-V allows you to declare a specialization constant which is a composite (array/vector/matrix). However, the components of this composite must be either specialization constants or constant values. If those components are scalar specialization constants, you can OpDecorate them with an ID, which the external code will access.

Vulkan (and OpenGL) GLSL go about this slightly differently from raw SPIR-V. In GLSL, a const-qualified value with a constant_id is a specialization constant. These must be scalars.

However, you can also have a const-qualified value that is initialized by values that are either constant expressions or specialization constants. You don't qualify these with a constant_id, but you built them from things that are so qualified:

layout(constant_id = 18) const int scX = 1;
layout(constant_id = 19) const int scZ = 1;
const vec3 scVec = vec3(scX, 1, scZ);  // partially specialized vector

const-qualified values that are initialized from specialization constants are called "partially specialized". When this GLSL is converted into SPIR-V, these are converted into OpSpecConstantComposite values.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Okay I see what your saying. It's unfortunate that glsl doesn't have any extensions yet that can be enabled for it to allow Vulkan specific functionality like this but at least the Vulkan specification allows it in the first place. Since it seems that I will be required to parse the binary data itself do you possibly know of any resources for learning the language or is the specification the best option? – Ryoku Mar 02 '21 at 05:41
  • @Ryoku: The specification for SPIR-V is pretty readable; it's not like parsing text. The language is designed to be easily parseable, with opcodes specifying how many parameters they take as part of the language. This makes iterating through SPIR-V to look for specific definitions or resultIDs pretty simple. And I'm fairly sure there are tools out there that can take care of most of the work for you. – Nicol Bolas Mar 02 '21 at 05:49
  • @Ryoku: I modified my answer with a better method for handling this. – Nicol Bolas Mar 02 '21 at 06:09
  • Okay, I think I understand, I think I'm gonna have to develop my own tool to automate the process so I don't have to manually edit the file after each change to the glsl. If I'm understanding correctly I should use a normal `const vec2 arr[2]` when compiling the SPIR-V. Then the OpName is a null terminated character string found in the debug information section. The ResultID attached to the OpName is what the compiled code actually uses. You can then replace first instance that matches the ResultID (should be an instruction with an `OpConstantComposite opcode` with `OpSpecConstantComposite` – Ryoku Mar 02 '21 at 06:26
  • @Ryoku That's the general idea. – Nicol Bolas Mar 02 '21 at 14:22
  • Should this not be considered a bug in GLSL? – Krupip Mar 03 '21 at 20:36
  • @whn: What makes it a "bug" in the language? GLSL isn't designed to map to SPIR-V's feature-set, so failure to do that isn't a "bug". – Nicol Bolas Mar 03 '21 at 23:44
  • If parsing a binary format is annoying and you prefer text, you could use `spirv-dis` to disassemble the SPIR-V to human-readable assembly, do a string replacement, then do `spirv-as`. – Andrea Mar 05 '21 at 23:30
  • 1
    If I understand correctly, specialization constants in spirv are given id through `OpDecorate (or other decoration instruction) %SpecConstantResultId SpecId xxx` As the specId decoration in spec says, it only works for scalar result_id. This means spirv also doesn't have vector specialization constants (but you can composite multiple scalar into vector). – shangjiaxuan Aug 13 '22 at 17:14