Name ARB_gl_spirv Name Strings GL_ARB_gl_spirv Contributors John Kessenich, Google Daniel Koch, NVIDIA Christophe Riccio, Unity Graham Sellers, AMD Contact Point for Bug Reports https://www.khronos.org/bugzilla/enter_bug.cgi?product=OpenGL Notice Copyright (c) 2015-2016 The Khronos Group Inc. Copyright terms at http://www.khronos.org/registry/speccopyright.html Status Complete. Ratified by the Khronos Board of Promoters on July 22, 2016. Version Last Modified Date: 25-Apr-2018 Revision: 39 Number ARB Extension #190 Dependencies This extension requires version 3.3 or later of The OpenGL Graphics System. (It is not written for OpenGL ES.) This extension is written against the following specifications: - The GL_KHR_vulkan_glsl extension, Version 30, April 12, 2016. - The OpenGL Graphics System, Version 4.5, Core Profile, May 28, 2015. - The OpenGL Shading Language, Version 4.50, Revision 6, April 14, 2016. - SPIR-V Specification, Version 1.00, Revision 5 This extension interacts with ARB_parallel_shader_compile. This extension interacts with ARB_separate_shader_objects. This extension interacts with ARB_program_interface_query. Overview This is version 100 of the GL_ARB_gl_spirv extension. This extension does two things: 1. Allows a SPIR-V module to be specified as containing a programmable shader stage, rather than using GLSL, whatever the source language was used to create the SPIR-V module. 2. Modifies GLSL to be a source language for creating SPIR-V modules for OpenGL consumption. Such GLSL can be used to create such SPIR-V modules, outside of the OpenGL runtime. Enabling GLSL SPIR-V Features ----------------------------- This extension is not enabled with a #extension as other extensions are. It is also not enabled through use of a profile or #version. The intended level of GLSL features comes from the traditional use of #version, profile, and #extension. Instead, use of this extension is an effect of using a GLSL front-end in a mode that has it generate SPIR-V for OpenGL. Such tool use is outside the scope of using the OpenGL API and outside the definition of GLSL and this extension. See the documentation of the compiler to see how to request generation of SPIR-V for OpenGL. When a front-end is used to accept the GLSL features in this extension, it must error check and reject shaders not adhering to this specification, and accept those that do. Implementation-dependent maximums and capabilities are supplied to, or part of, the front-end, so it can do error checking against them. A shader can query the level of GLSL SPIR-V support available, using the predefined #define GL_SPIRV 100 This allows shader code to say, for example, #ifdef GL_SPIRV layout(constant_id = 11) const int count = 4; #if GL_SPIRV > 100 ... #endif #else const int count = 6; #endif SPIR-V Modules -------------- An entire stage is held in a single SPIR-V module. The SPIR-V model is that multiple (e.g., GLSL) compilation units forming a single stage in a high-level language are all compiled and linked together to form a single entry point (and its call tree) in a SPIR-V module. This would be done prior to using the OpenGL API to create a program object containing the stage. The OpenGL API expects the SPIR-V module to have already been validated, and can return an error if it discovers anything invalid in the module. An invalid SPIR-V module is allowed to result in undefined behavior. Specialization Constants ------------------------ SPIR-V specialization constants, which can be set later by the client API, can be declared using "layout(constant_id=...)". For example, to make a specialization constant with a default value of 12: layout(constant_id = 17) const int arraySize = 12; Above, "17" is the ID by which the API or other tools can later refer to this specific specialization constant. The API or an intermediate tool can then change its value to another constant integer before it is fully lowered to executable code. If it is never changed before final lowering, it will retain the value of 12. Specialization constants have const semantics, except they don't fold. Hence, an array can be declared with 'arraySize' from above: vec4 data[arraySize]; // legal, even though arraySize might change Specialization constants can be in expressions: vec4 data2[arraySize + 2]; This will make data2 be sized by 2 more than whatever constant value 'arraySize' has when it is time to lower the shader to executable code. An expression formed with specialization constants also behaves in the shader like a specialization constant, not a like a constant. arraySize + 2 // a specialization constant (with no constant_id) Such expressions can be used in the same places as a constant. The constant_id can only be applied to a scalar *int*, a scalar *float* or a scalar *bool*. Only basic operators and constructors can be applied to a specialization constant and still result in a specialization constant: layout(constant_id = 17) const int arraySize = 12; sin(float(arraySize)); // result is not a specialization constant While SPIR-V specialization constants are only for scalars, a vector can be made by operations on scalars: 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 A built-in variable can have a 'constant_id' attached to it: layout(constant_id = 18) gl_MaxImageUnits; This makes it behave as a specialization constant. It is not a full redeclaration; all other characteristics are left intact from the original built-in declaration. The built-in vector gl_WorkGroupSize can be specialized using special layout local_size_{xyz}_id's applied to the "in" qualifier. For example: layout(local_size_x_id = 18, local_size_z_id = 19) in; This leaves gl_WorkGroupSize.y as a non-specialization constant, with gl_WorkGroupSize being a partially specialized vector. Its x and z components can be later specialized using the ID's 18 and 19. gl_FragColor ------------ The fragment-stage built-in gl_FragColor, which implies a broadcast to all outputs, is not present in SPIR-V. Shaders where writing to gl_FragColor is allowed can still write to it, but it only means to write to an output: - of the same type as gl_FragColor - decorated with location 0 - not decorated as a built-in variable. There is no implicit broadcast. Mapping to SPIR-V ----------------- For informational purposes (non-specification), the following is an expected way for an implementation to map GLSL constructs to SPIR-V constructs: Mapping of Storage Classes: uniform sampler2D...; -> UniformConstant uniform variable (non-block) -> UniformConstant uniform blockN { ... } ...; -> Uniform, with Block decoration in / out variable -> Input/Output, possibly with block (below) in / out block... -> Input/Output, with Block decoration buffer blockN { ... } ...; -> Uniform, with BufferBlock decoration ... uniform atomic_uint ... -> AtomicCounter shared -> Workgroup -> Private Mapping of input/output blocks or variables is the same for all versions of GLSL. To the extent variables or members are available in a version and a stage, its location is as follows: These are mapped to SPIR-V individual variables, with similarly spelled built-in decorations (except as noted): General: in gl_VertexID in gl_InstanceID in gl_InvocationID in gl_PatchVerticesIn (PatchVertices) in gl_PrimitiveIDIn (PrimitiveID) in/out gl_PrimitiveID (in/out based only on storage qualifier) in gl_TessCoord in/out gl_Layer in/out gl_ViewportIndex patch in/out gl_TessLevelOuter (uses Patch decoration) patch in/out gl_TessLevelInner (uses Patch decoration) Compute stage only: in gl_NumWorkGroups in gl_WorkGroupSize in gl_WorkGroupID in gl_LocalInvocationID in gl_GlobalInvocationID in gl_LocalInvocationIndex Fragment stage only: in gl_FragCoord in gl_FrontFacing in gl_ClipDistance in gl_CullDistance in gl_PointCoord in gl_SampleID in gl_SamplePosition in gl_HelperInvocation out gl_FragDepth in gl_SampleMaskIn (SampleMask) out gl_SampleMask (in/out based only on storage qualifier) These are mapped to SPIR-V blocks, as implied by the pseudo code, with the members decorated with similarly spelled built-in decorations: Non-fragment stage: in/out gl_PerVertex { // some subset of these members will be used gl_Position gl_PointSize gl_ClipDistance gl_CullDistance } // name of block is for debug only There is at most one input and one output block per stage in SPIR-V. The subset and order of members will match between stages sharing an interface. Mapping of precise: precise -> NoContraction Mapping of atomic_uint /offset/ layout qualifier offset -> Offset (decoration) Mapping of images imageLoad() -> OpImageRead imageStore() -> OpImageWrite texelFetch() -> OpImageFetch imageAtomicXXX(params, data) -> %ptr = OpImageTexelPointer params OpAtomicXXX %ptr, data XXXQueryXXX(combined) -> %image = OpImage combined OpXXXQueryXXX %image Mapping of layouts std140/std430 -> explicit *Offset*, *ArrayStride*, and *MatrixStride* Decoration on struct members shared/packed -> not allowed -> not shared, but std140 or std430 xfb_offset -> *Offset* Decoration on the object or struct member xfb_buffer -> *XfbBuffer* Decoration on the object xfb_stride -> *XfbStride* Decoration on the object any xfb_* -> the *Xfb* Execution Mode is set captured XFB -> has both *XfbBuffer* and *Offset* non-captured -> lacking *XfbBuffer* or *Offset* max_vertices -> OutputVertices Mapping of barriers barrier() (compute) -> OpControlBarrier(/*Execution*/Workgroup, /*Memory*/Workgroup, /*Semantics*/AcquireRelease | WorkgroupMemory) barrier() (tess control) -> OpControlBarrier(/*Execution*/Workgroup, /*Memory*/Invocation, /*Semantics*/None) memoryBarrier() -> OpMemoryBarrier(/*Memory*/Device, /*Semantics*/AcquireRelease | UniformMemory | WorkgroupMemory | ImageMemory) memoryBarrierBuffer() -> OpMemoryBarrier(/*Memory*/Device, /*Semantics*/AcquireRelease | UniformMemory) memoryBarrierShared() -> OpMemoryBarrier(/*Memory*/Device, /*Semantics*/AcquireRelease | WorkgroupMemory) memoryBarrierImage() -> OpMemoryBarrier(/*Memory*/Device, /*Semantics*/AcquireRelease | ImageMemory) groupMemoryBarrier() -> OpMemoryBarrier(/*Memory*/Workgroup, /*Semantics*/AcquireRelease | UniformMemory | WorkgroupMemory | ImageMemory) Mapping of atomics all atomic builtin functions -> Semantics = None(Relaxed) atomicExchange() -> OpAtomicExchange imageAtomicExchange() -> OpAtomicExchange atomicCompSwap() -> OpAtomicCompareExchange imageAtomicCompSwap() -> OpAtomicCompareExchange NA -> OpAtomicCompareExchangeWeak atomicCounterIncrement -> OpAtomicIIncrement atomicCounterDecrement -> OpAtomicIDecrement atomicCounter -> OpAtomicLoad Mapping of other instructions % -> OpUMod/OpSMod mod() -> OpFMod NA -> OpSRem/OpFRem pack/unpack (conversion) -> pack/unpack in GLSL extended instructions pack/unpack (no conversion) -> OpBitcast Differences Relative to Other Specifications -------------------------------------------- The following summarizes the set of differences to existing specifications. Additional use of existing SPIR-V features, relative to Vulkan: + The *UniformConstant* Storage Class can be used on individual variables at global scope. (That is, uniforms don't have to be in a block, unless they are built-in members that are in block in GLSL version 4.5.) + *AtomicCounter* Storage Class can use the *Offset* decoration + OriginLowerLeft Corresponding features that GLSL keeps, despite GL_KHR_vulkan_glsl removal: . default uniforms (those not inside a uniform block) . atomic-counter bindings have the /offset/ layout qualifier . special rules for locations for input doubles in the vertex shader . origin_lower_left Addition of features to GLSL: + specialization constants, as per GL_KHR_vulkan_glsl + #define GL_SPIRV 100, when compiled for OpenGL and SPIR-V generation + offset can organize members in a different order than declaration order Non-acceptance of SPIR-V features, relative to Vulkan: - VertexIndex and InstanceIndex built-in decorations cannot be used - Push-constant buffers cannot be used - *DescriptorSet* must always be 0, if present - input targets and input attachments - OpTypeSampler Corresponding features not added to GLSL that GL_KHR_vulkan_glsl added: . gl_VertexIndex and gl_InstanceIndex (gl_VertexID and gl_InstanceID have same semantics as in GLSL) . push_constant buffers . descriptor sets . input_attachment_index and accessing input targets . shader-combining of textures and samplers Removal of features from GLSL, as removed by GL_KHR_vulkan_glsl: - subroutines - the already deprecated texturing functions (e.g., texture2D()) - the already deprecated noise functions (e.g., noise1()) - compatibility profile features - gl_DepthRangeParameters and gl_NumSamples - *shared* and *packed* block layouts Addition of features to The OpenGL Graphics System: + a command to associate a SPIR-V module with a program (ShaderBinary) + a command to select a SPIR-V entry point and set specialization constants in a SPIR-V module (SpecializeShaderARB) + a new appendix for SPIR-V validation rules, precision, etc. Changes of system features, relative to Vulkan: . Vulkan uses only one binding point for a resource array, while OpenGL still uses multiple binding points, so binding numbers are counted differently for SPIR-V used in Vulkan and OpenGL . gl_FragColor can be written to, but it won't broadcast, for versions of OpenGL that support gl_FragColor . Vulkan does not allow multi-dimensional arrays of resources like UBOs and SSBOs in its SPIR-V environment spec. SPIR-V supports it and OpenGL already allows this for GLSL shaders. SPIR-V for OpenGL also allows it. Additions to the SPIR-V specification: + *Offset* can also apply to an object, for transform feedback. + *Offset* can also apply to a default uniform, for atomic_uint offset. New Procedures and Functions void SpecializeShaderARB(uint shader, const char* pEntryPoint, uint numSpecializationConstants, const uint* pConstantIndex, const uint* pConstantValue); New Tokens Accepted by the parameter of ShaderBinary: SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 Accepted by the parameter of GetShaderiv: SPIR_V_BINARY_ARB 0x9552 Modifications to Chapter 7 of the OpenGL 4.5 (Core Profile) Specification (Programs and Shaders) Additions to overview of Chapter 7, "Programs and Shaders" Modify the 4th paragraph beginning with "Alternatively, pre-compiled shader binary code..." as follows: "Alternatively, pre-compiled shader binary code can be loaded into a shader object. A SPIR-V module can also be associated with a shader and then _specialized_. An implementation must..." Additions to Section 7.1, "Shader Objects": Add the following to the end of the description of ShaderSource: "If was previously associated with a SPIR-V module (via the ShaderBinary command), that association is broken. Upon successful completion of this command the SPIR_V_BINARY_ARB state of is set to FALSE." Add a new error for the CompileShader command: "An INVALID_OPERATION error is generated if the SPIR_V_BINARY_ARB state of is TRUE." [[ if ARB_parallel_shader_compile is supported: ]] Modify the preamble to the MaxShaderCompilerThreadsARB command to read: "Applications may use the following to hint to the driver the maximum number of background threads it would like to be used in the process of compiling or specializing shaders, or linking programs, ..." Additions to Section 7.2, "Shader Binaries": In the description of ShaderBinary, remove the text stating that GL defines no specific binary formats and replace it with the following: "GL defines an execution environment for shaders created from SPIR-V modules. To load a SPIR-V binary into GL, set to SHADER_BINARY_FORMAT_SPIR_V_ARB. should point to the start of a valid SPIR-V module binary and should contain the length of that binary, in bytes. Upon successful consumption of the SPIR-V module: * each entry of will be associated with that SPIR-V module, * the SPIR_V_BINARY_ARB state of each shader is set to TRUE, * the COMPILE_STATUS of each of these shaders is set to FALSE, * any existing source string (specified by ShaderSource) is removed, and * any information about a previous compile is lost. Shaders associated with SPIR-V modules must be finalized by calling SpecializeShaderARB as described in Section 7.2.1. GL also provides a mechanism to obtain token values for additional binary formats provided by extensions. The number of binary formats..." Replace the error condition for ShaderBinary which states: "An INVALID_OPERATION error is generated if more than one of the handles in shaders refers to the same type of shader object." with the following: "An INVALID_OPERATION error is generated if is not SHADER_BINARY_FORMAT_SPIR_V_ARB and more than one of the handles in shaders refers to the same type of shader object." Insert Subsection 7.2.1, "Shader Specialization": "Shaders associated with SPIR-V modules must be specialized before they can be linked into a program object. It is not necessary to specialize the shader before it is attached to a program object. Once specialized, a shader may not be specialized again without first re-associating the original SPIR-V module with it, through ShaderBinary. Specialization does two things: * Selects the name of the entry point, for that shader's stage, from the SPIR-V module. * Sets the values of all, or a subset of, the specialization constants in the SPIR-V module. To specialize a shader created from a SPIR-V module, call: void SpecializeShaderARB(uint shader, const char* pEntryPoint, uint numSpecializationConstants, const uint* pConstantIndex, const uint* pConstantValue); is the name of a shader object containing unspecialized SPIR-V as created from a successful call to ShaderBinary to which a SPIR-V module was passed. is a pointer to a NUL-terminated UTF-8 string specifying the name of the entry point in the SPIR-V module to use for this shader. is the number of specialization constants whose values to set in this call. is a pointer to an array of unsigned integers, each holding the index of a specialization constant in the SPIR-V module whose value to set. The corresponding entry in is used to set the value of the specialization constant indexed by the entry in . Although this array is of unsigned integer, each entry is bitcast to the appropriate type for the module, and therefore, floating-point constants may be set by including their IEEE-754 bit representation in the array. Specialization constants not referenced by retain their default values as specified in the SPIR-V module. On successful shader specialization, the compile status for is set to TRUE. On failure, the compile status for is set to FALSE and additional information about the cause of the failure may be available in the shader compilation log. Specialization can fail if the SPIR-V module fails to meet the requirements listed in Appendix "The OpenGL SPIR-V Execution Environment". Errors An INVALID_VALUE error is generated if is not the name of either a program or shader object. An INVALID_OPERATION error is generated if is the name of a program object. INVALID_OPERATION is generated if the value of SPIR_V_BINARY_ARB for is not TRUE, or if the shader has already been specialized. INVALID_VALUE is generated if does not match the of any OpEntryPoint declaration in the SPIR-V module associated with . INVALID_OPERATION is generated if the of the OpEntryPoint indicated by does not match the type of . INVALID_VALUE is generated if any element of refers to a specialization constant that does not exist in the shader module contained in ." Additions to Section 7.3, "Program Objects": Modify the first paragraph after the AttachShader command to read: "Shader objects may be attached to program objects before source code or shader binary has been loaded into the shader object, or before the shader object has been compiled or specialized. Multiple shader ..." Modify the first paragraph after the LinkProgram command to read: "Linking can fail for a variety of reasons as specified in the OpenGL Shading language specification for source shaders, or if the requirements in the Appendix "The OpenGL SPIR-V Execution Environment" are not met for SPIR-V shaders, as well as any of the following reasons..." Modify the second bullet point in the list of reasons LinkProgram can fail: "One or more of the shader objects attached to are not compiled or specialized successfully." Add a new bullet point to this list: "All the shader objects attached to do not have the same value for the SPIR_V_BINARY_ARB state." Modifications to Section 7.3.1, "Program Interfaces" Add the following paragraph after the list of rules describing how the name string is generated, before the paragraph that begins "The order of the active resource list...": "For shaders constructed from SPIR-V binaries (that is with a state of SPIR_V_BINARY_ARB equal to TRUE), variables may not have names associated with them as the OpName and OpMemberName debug instructions are optional and may not be present in a SPIR-V module. When the Op*Name instructions are present, it is implementation-dependent if these are reported via the name reflection APIs. If no _name reflection information_ is available, the name string associated with each active variable is the empty string (""). In this case, any queries that operate with a name as input, will return INVALID_INDEX or -1 as appropriate, and any queries that return information about the name of a resource will report a name length of one (for the null character) and return an empty string with a length of zero." Add a new subsection "7.4.spv, SPIR-V Shader Interface Matching" after section 7.4.1 "Shader Interface Matching": "7.4.spv, SPIR-V Shader Interface Matching SPIR-V shaders must also follow the rules in this section, whether they add to or override those given in section 7.4.1 "Shader Interface Matching." Most importantly, SPIR-V variables and structure members do not have names and so no interface matching is done by name strings. All variables forming the input or output interfaces of shader stages must be listed as operands to the *OpEntryPoint* instruction and are declared with the *Input* or *Output* Storage Classes, respectively, in the SPIR-V module. Shader built-in variables meeting the following requirements define the built-in interface block. They must: - be explicitly declared (there are no implicit built-ins), - be decorated with the *BuiltIn* decoration, - be declared in a block whose top-level members are the built-ins. - not have any *Location* or *Component* decorations. Built-ins only participate in interface matching if they are declared in such a block. There must be no more than one built-in interface block per shader per interface. User-defined interface variables must be decorated with a *Location* and can also be decorated with a *Component*. These correspond to the location and component discussed in section 7.4.1 "Shader Interface Matching". Uniform and shader storage block variables must also be decorated with a *Binding*. A user-defined output variable is considered to match an input variable in the subsequent stage only if the two variables are declared with the same *Location* and *Component* decoration and match in type and decoration, except that interpolation decorations are not required to match. Variables or block members declared as structures are considered to match in type if and only if the structure members match in type, decoration, number, and declaration order. Variables or block members declared as arrays are considered to match in type only if both declarations specify the same element type and size. At an interface between two non-fragment shader stages, the built-in interface block must match exactly, as described above. At an interface involving the fragment shader inputs, the presence or absence of any built-in output does not affect the interface matching. At an interface between two shader stages, the user-defined variable interface must match exactly. Additionally, scalar and vector inputs are well-defined if there is a corresponding output satisfying all of the following conditions: - the input and output match exactly in decoration, - the output is a vector with the same basic type and has at least as many components as the input, and - the common component type of the input and output is 32-bit integer or floating-point (64-bit component types are excluded). In this case, the components of the input will be taken from the first components of the output, and any extra components of the output will be ignored." Add a new subsection 7.6.2.spv "SPIR-V Uniform Offsets and Strides" after section 7.6.2.2 "Standard Uniform Block Layout": "7.6.2.spv SPIR-V Uniform Offsets and Strides The SPIR-V decorations *GLSLShared* or *GLSLPacked* must not be used. A variable in the *Uniform* Storage Class decorated as a *Block* must be explicitly laid out using the *Offset*, *ArrayStride*, and *MatrixStride* decorations. If the variable is decorated as a *BufferBlock*, its offsets and strides must not contradict std430 alignment and minimum offset requirements. Otherwise, its offsets and strides must not contradict std140 alignment and minimum offset requirements." Modifications to Section 7.13 "Shader, Program, and Program Pipeline Queries" Add the following to the list of that are accepted by GetShaderiv: "If is SPIR_V_BINARY_ARB, TRUE is returned if the shader has been successfully associated with a SPIR-V binary module by the ShaderBinary command and FALSE is returned otherwise." Modify the description of COMPILE_STATUS to be as follows: "If is COMPILE_STATUS, TRUE is returned if the shader was last compiled or specialized successfully, and FALSE is returned otherwise." [[ if ARB_parallel_shader_compile is supported: ]] Modify the description of COMPLETION_STATUS_ARB to be as follows: "If is COMPLETION_STATUS_ARB, FALSE is returned if the shader is currently being compiled or specialized by a background thread, TRUE otherwise." Modify the Errors list for GetShaderiv and add SPIR_V_BINARY_ARB to the list of that are accepted. In the description of the GetProgramiv command, modify the definitions of the ACTIVE_ATTRIBUTE_MAX_LENGTH, ACTIVE_UNIFORM_MAX_LENGTH, TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, and ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH values to be as follows: "If pname is ACTIVE_ATTRIBUTE_MAX_LENGTH, the length of the longest active attribute name, including a null terminator, is returned. If no active attributes exist, zero is returned. If no name reflection information is available, one is returned." "If pname is ACTIVE_UNIFORM_MAX_LENGTH, the length of the longest active uniform name, including a null terminator, is returned. If no active uniforms exist, zero is returned. If no name reflection information is available, one is returned." "If pname is TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, the length of the longest output variable name specified to be used for transform feedback, including a null terminator, is returned. If no outputs are used for transform feedback, zero is returned. If no name reflection information is available, one is returned." "If pname is ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, the length of the longest active uniform block name, including the null terminator, is returned. If no active uniform blocks exist, zero is returned. If no name reflection information is available, one is returned." Modify the description of GetShaderInfoLog to read as follows: "If is a shader object, GetShaderInfoLog will return either an empty string or information about the last compilation or specialization attempt for that object." Modifications to Section 7.14 "Required State": Add to the list of state per shader object: "A boolean holding the SPIR-V binary status, initially FALSE." Modifications to Chapter 11 of the OpenGL 4.5 (Core Profile) Specification (Programmable Vertex Processing) Modify Section 11.1.1 "Vertex Attributes" Replace the last sentence in the first paragraph ("This binding can be..") with the following: "This binding can be specified in the shader text using the location qualifier, in a SPIR-V shader using the *Location* decoration, by the application before the program is linked, or automatically assigned by the GL when the program is linked." In the second paragraph of the definition of BindAttribLocation, replace occurrences of "shader text" with "shader text or SPIR-V binary". Add to the end of the third paragraph of the definition of BindAttribLocation: "BindAttribLocation has no effect on SPIR-V shaders as the locations must always be fully specified in the SPIR-V shader as described in section 11.1.1.1 (SPIR-V Vertex Input Interface)." Add the following new subsection to the end of section 11.1.1 "Vertex Attributes": "Section 11.1.1.1 SPIR-V Vertex Input interface When a SPIR-V vertex stage is present, the vertex shader variables listed by *OpEntryPoint* with the *Input* Storage Class form the vertex input attribute interface. These inputs must be decorated with a *Location* and can also be decorated with a *Component* decoration. These correspond to the location and component layout discussed in section 11.1.1 "Vertex Attributes." The vertex shader input variables are matched only by the *Location* and *Component* decorations, and must have a corresponding attribute and binding in the pipeline." Modify section 11.1.2.1 "Output Variables" Replace the first sentence of the second paragraph describing transform feedback mode ("The set of variables to record...") with the following: "The set of variables to record can be specified in shader text using xfb_buffer, xfb_offset, or xfb_stride layout qualifiers. For SPIR-V shaders, these are specified by the *XfbBuffer*, *Offset*, and *XfbStride* decorations, respectively. When a SPIR-V entry point is using any of these for transform feedback, it must declare the *Xfb* Execution Mode." Modify the last paragraph of the definition of TransformFeedbackVaryings ("If the shader is used to record...") as follows: "If the shader used to record output variables for transform feedback varyings uses the xfb_buffer, xfb_offset, or xfb_stride layout qualifiers, or its SPIR-V entry point declares the *Xfb* Execution Mode, the values specified by TransformFeedbackVaryings are ignored, and the set of variables captured for transform feedback is instead derived from the specified layout qualifiers or SPIR-V decorations: outputs specifying both an *XfbBuffer* and an *Offset* are captured, while outputs not specifying both of these are not captured. Values are captured each time the shader writes to such a decorated object." Modifications to Chapter 15 of the OpenGL 4.5 (Core Profile) Specification (Programmable Fragment Processing) Modify section 15.2.3 "Shader Outputs" Replace the sentence leading up to the definition of the BindFragDataLocationIndexed command with the following: "The binding of a user-defined output variable to components of a fragment color number can be specified explicitly in the shader text, SPIR-V shader, or using the command ..." Modify the paragraph following the definition of BindFragDataLocation, replacing the second and third sentences with the following: "Output variables declared with location, component, or index layout qualifiers will use the values specified in the shader text. For SPIR-V shaders, these are specified by the *Location*, *Component*, and *Index* decorations. Output variables without such layout or decoration qualifiers will use the bindings specified by BindFragDataLocationIndexed or BindFragDataLocation, if any. BindFragDataLocation* has no effect on SPIR-V shaders as the locations must always be fully specified as described in section 15.2.3.1 (SPIR-V Fragment Output Interface)." Add the following new subsection to the end of 15.2.3 "Shader Outputs" "Section 15.2.3.1 "SPIR-V Fragment Output Interface" When a SPIR-V fragment stage is present, the variables listed by *OpEntryPoint* with the *Output* Storage Class form the fragment output interface. These variables must be decorated with a *Location* They can also be decorated with a *Component* and/or an *Index*. User-defined fragment shader output variables are matched only by their *Location*, *Component*, and *Index* decorations. If two outputs are placed within the same location, they must have the same underlying type (floating-point or integer). No component aliasing of output variables is allowed. That is, there must not be two output variables which have the same location, component, and index, either explicitly declared or implied. Output values written by a fragment shader must be declared with either *OpTypeFloat* or *OpTypeInt*, and a *Width* of 32. Composites of these types are also permitted." Add Appendix A.spv "The OpenGL SPIR-V Execution Environment" to the OpenGL 4.5 (Core Profile) Specification "A.spv.1 (Required Versions and Formats) An implementation of this extension must support the 1.0 version of SPIR-V and the 1.0 version of the SPIR-V Extended Instructions for GLSL. A SPIR-V module passed into ShaderBinary is interpreted as a series of 32-bit words in host endianness, with literal strings packed as described in section 2.2 of the SPIR-V Specification. The first few words of the SPIR-V module must be a magic number and a SPIR-V version number, as described in section 2.3 of the SPIR-V Specification. A.spv.2 (Valid SPIR-V Built-In Variable Decorations) Implementations supporting GL 4.5 must support the following built-in variable decorations. When used with earlier versions of GL, this list may be subset to the functionality in that version. Each built-in below lists the minimum OpenGL version (or extension) that is required to use the built-in variable decoration. Built-in Variable Decoration Minimum GL version (Extension) ---------------------------- ----------------------------- NumWorkgroups GL 4.3 (ARB_compute_shader) WorkgroupSize GL 4.3 (ARB_compute_shader) WorkgroupId GL 4.3 (ARB_compute_shader) LocalInvocationId GL 4.3 (ARB_compute_shader) GlobalInvocationId GL 4.3 (ARB_compute_shader) LocalInvocationIndex GL 4.3 (ARB_compute_shader) VertexId InstanceId Position PointSize ClipDistance CullDistance GL 4.5 (ARB_cull_distance) PrimitiveId InvocationId GL 4.0 (ARB_tessellation_shader) Layer ViewportIndex GL 4.1 (ARB_viewport_array) PatchVertices GL 4.0 (ARB_tessellation_shader) TessLevelOuter GL 4.0 (ARB_tessellation_shader) TessLevelInner GL 4.0 (ARB_tessellation_shader) TessCoord GL 4.0 (ARB_tessellation_shader) FragCoord FrontFacing PointCoord SampleId GL 4.0 (ARB_sample_shading) SamplePosition GL 4.0 (ARB_sample_shading) SampleMask GL 4.0 (ARB_sample_shading) HelperInvocation GL 4.5 (ARB_ES3_1_compatibility) FragDepth A.spv.3 (Valid SPIR-V Capabilities) Implementations supporting OpenGL 4.5 must support the following capability operands declared by OpCapability. When used against earlier versions of GL, this list may be subset to the functionality in that version. Each capability below lists the OpenGL version (or extension) that is required to use the OpCapability. OpCapability Minimum GL version (Extension) ------------------------------ ----------------------------- Matrix Shader Geometry Tessellation GL 4.0 (ARB_tessellation_shader) Float64 GL 4.0 (ARB_gpu_shader_fp64) AtomicStorage GL 4.2 (ARB_shader_atomic_counters) TessellationPointSize GL 4.0 (ARB_tessellation_shader) GeometryPointSize ImageGatherExtended GL 4.0 (ARB_gpu_shader5) StorageImageMultisample GL 4.2 (ARB_shader_image_load_store) UniformBufferArrayDynamicIndexing GL 4.0 (ARB_gpu_shader5) SampledImageArrayDynamicIndexing GL 4.0 (ARB_gpu_shader5) StorageBufferArrayDynamicIndexing GL 4.3 (ARB_shader_storage_buffer_object) StorageImageArrayDynamicIndexing GL 4.2 (ARB_shader_image_load_store) ClipDistance CullDistance GL 4.5 (ARB_cull_distance) ImageCubeArray GL 4.0 (ARB_texture_cube_map_array) SampleRateShading GL 4.0 (ARB_sample_shading) ImageRect SampledRect Sampled1D Image1D SampledCubeArray GL 4.0 (ARB_texture_cube_map_array) SampledBuffer ImageBuffer ImageMSArray StorageImageExtendedFormats GL 4.2 (ARB_shader_image_load_store) ImageQuery DerivativeControl GL 4.5 (ARB_derivative_control) InterpolationFunction GL 4.0 (ARB_gpu_shader5) TransformFeedback GeometryStreams GL 4.0 (ARB_gpu_shader5) StorageImageWriteWithoutFormat GL 4.2 (ARB_shader_image_load_store) MultiViewport GL 4.1 (ARB_viewport_array) Implementations supporting ARB_gpu_shader_int64 must support the following operand declared by OpCapability: Int64 Implementations supporting ARB_sparse_texture2 must support the following operand declared by OpCapability: SparseResidency Implementations supporting ARB_sparse_texture_clamp must support the following operand declared by OpCapability: MinLod Implementations supporting EXT_shader_image_load_formatted must support the following operand declared by OpCapability: StorageImageReadWithoutFormat Implementations supporting NV_shader_atomic_int64 must support the following operand declared by OpCapability: Int64Atomics A.spv.4 (Validation rules) (in addition to what's allowed/disallowed above) Every entry point must have no return value and accept no arguments. The *Logical* addressing model must be selected. *Scope* for execution must be limited to: - Workgroup - Subgroup *Scope* for memory must be limited to: - Device - Workgroup - Invocation *Storage Class* must be limited to: - *UniformConstant* - *Input* - *Uniform* - *Output* - *Workgroup* - *Private* - *Function* - *AtomicCounter* - *Image* Images: - OpTypeImage must declare a scalar 32-bit float or 32-bit integer type for the /Sampled Type/. - OpSampledImage, OpImageQuerySizeLod, and OpImageQueryLevels must only consume an /Image/ operand whose type has its /Sampled/ operand set to 1. - The /Depth/ operand of OpTypeImage is ignored. *AtomicCounter* storage class: - Unless otherwise specified, variables declared in the AtomicCounter storage class must be unsigned 32-bit integers. - When using the Atomic Instructions, the 'Memory Semantics' operand must be *AtomicCounterMemory*, without inclusion of memory-ordering bits (implying *Relaxed* semantics). Decorations: - The Flat, NoPerspective, Sample, and Centroid decorations must not be used on variables with Storage Class other than *Input* or on variables used in the interface of non-fragment shader entry points. - The Patch decoration must not be used on variables in the interface of a vertex, geometry, or fragment shader stage's entry point. - OpTypeRuntimeArray must only be used for the last member of an OpTypeStruct in the Uniform Storage Class and is decorated as BufferBlock. - If the *Xfb* Execution Mode is set, any output variable that is at least partially captured: * must be decorated with an *XfbBuffer*, declaring the capturing buffer * must have at least one captured output variable in the capturing buffer decorated with an *XfbStride* (and all such *XfbStride* values for the capturing buffer must be equal) - If the *Xfb* Execution Mode is set, any captured output: * must be a non-structure decorated with *Offset* or a member of a structure whose type member is decorated with *Offset* (not all members of such a struct need be captured) * must be a numerical type scalar, vector, matrix, or array of these * must have an *Offset* that is a multiple of its first component * must have an *Offset* that is a multiple of 8 if any captured output in the capturing buffer is a 64-bit type, in which case the corresponding *XfbStride* must also be a multiple of 8 * must not overlap (i.e., alias in any capturing buffer) any other captured output * must not have an *Offset* that causes overflow of the *XfbStride* Compute Shaders - For each compute shader entry point, either a LocalSize execution mode or an object decorated with the WorkgroupSize decoration must be specified. Recursion: - For all entry points, the static function-call graph rooted at that entry point must not contain cycles. A.spv.5 (Precision and Operation of SPIR-V Instructions) The following rules apply to both single and double-precision floating point instructions: - Positive and negative infinities and positive and negative zeros are generated as dictated by [IEEE 754], but subject to the precisions allowed in the following table. - Dividing a non-zero by a zero results in the appropriately signed IEEE 754 infinity. - Any denormalized value input into a shader or potentially generated by any instruction in a shader may be flushed to 0. - The rounding mode cannot be set and is undefined. - NaNs are not required to be generated. Instructions that operate on a NaN are not required to return a NaN as the result. - Support for signaling NaNs is optional and exceptions are never raised. The precision of double-precision instructions is at least that of single precision. For single precision (32 bit) instructions, precisions are required to be at least as follows: Instruction Precision ----------- --------- OpFAdd Correctly rounded OpFSub Correctly rounded OpFMul Correctly rounded OpFOrdEqual, OpFUnordEqual Correct result OpFOrdLessThan, OpFUnordLessThan Correct result OpFOrdGreaterThan, OpFUnordGreaterThan Correct result OpFOrdLessThanEqual, OpFUnordLessThanEqual Correct result OpFOrdGreaterThanEqual, OpFUnordGreaterThanEqual Correct result OpFDiv 2.5 ULP for b in the range [2^(-126), 2^(126)] conversions between types Correctly rounded A.spv.6 (Precision of GLSL.std.450 Instructions) Instruction Precision ----------- --------- fma() Inherited from OpFMul followed by OpFAdd. exp(x), exp2(x) (3+2*|x|) ULP log(), log2() 3 ULP outside the range [0.5, 2.0] absolute error < 2^(-21) inside the range [0.5, 2.0] pow(x, y) Inherited from exp2(y * log2(x)) sqrt() Inherited from 1.0 / inversesqrt() inversesqrt() 2 ULP GLSL.std.450 extended instructions specifically defined in terms of the above instructions inherit the above errors. GLSL.std.450 extended instructions not listed above and not defined in terms of the above have undefined precision. These include, for example, the trigonometric functions and determinant. For the OpSRem and OpSMod instructions, if either operand is negative the result is undefined. Changes to The OpenGL Shading Language Specification, Version 4.50 Changes to Chapter 1 of the OpenGL Shading Language 4.50 Specification (Introduction) Add a paragraph: "The OpenGL API will specify the OpenGL commands used to manipulate SPIR-V shaders. Independent offline tool chains will compile GLSL down to the SPIR-V intermediate language. SPIR-V generation is not enabled with a #extension, #version, or a profile. Instead, use of GLSL for SPIR-V is determined by offline tool-chain use. See the documentation of such tools to see how to request generation of SPIR-V for OpenGL." "GLSL -> SPIR-V compilers must be directed as to what SPIR-V *Capabilities* are legal at run-time and give errors for GLSL feature use outside those capabilities. This is also true for implementation-dependent limits that can be error checked by the front-end against constants present in the GLSL source: the front-end can be informed of such limits, and report errors when they are exceeded." Changes to Chapter 3 of the OpenGL Shading Language 4.50 Specification (Basics) After describing the compatibility profile rules, add: "Compatibility-profile features are not available when generating SPIR-V." Add a new paragraph at the end of section "3.3 Preprocessor": "When shaders are compiled for OpenGL SPIR-V, the following predefined macro is available: #define GL_SPIRV 100 Changes to Chapter 4 of the OpenGL Shading Language 4.50 Specification (Variables and Types) Change section 4.3.3 Constant Expressions: Add a new very first sentence to this section: "SPIR-V specialization constants are expressed in GLSL as const, with a layout qualifier identifier of constant_id, as described in section 4.4.x Specialization-Constant Qualifier." Add to this bullet "A constant expression is one of... * a variable declared with the const qualifier and an initializer, where the initializer is a constant expression" to make it say: "A constant expression is one of... * a variable declared with the const qualifier and an initializer, where the initializer is a constant expression; this includes both const declared with a specialization-constant layout qualifier, e.g., 'layout(constant_id = ...)' and those declared without a specialization-constant layout qualifier" Add to "including getting an element of a constant array," that "an array access with a specialization constant as an index does not result in a constant expression" Add to this bullet "A constant expression is one of... * the value returned by a built-in function..." to make it say: "A constant expression is one of... * for non-specialization-constants only: the value returned by a built-in function... (when any function is called with an argument that is a specialization constant, the result is not a constant expression)" Rewrite the last half of the last paragraph to be its own paragraph saying: "Non-specialization constant expressions may be evaluated by the compiler's host platform, and are therefore not required ... [rest of paragraph stays the same]" Add a paragraph "Specialization constant expressions are never evaluated by the front-end, but instead retain the operations needed to evaluate them later on the host." Add to the table in section 4.4 Layout Qualifiers: | Individual Variable | Block | Allowed Interface ------------------------------------------------------------------------ constant_id = | scalar only | | const ------------------------------------------------------------------------ (The other columns remain blank.) Also add to this table: | Qualifier Only | Allowed Interface ------------------------------------------------------- local_size_x_id = | X | in local_size_y_id = | X | in local_size_z_id = | X | in (The other columns remain blank.) Expand this sentence in section 4.4.1 Input Layout Qualifiers "Where integral-constant-expression is defined in section 4.3.3 Constant Expressions as 'integral constant expression'" to include the following: ", with it being a compile-time error for integer-constant-expression to be a specialization constant: The constant used to set a layout identifier X in layout(layout-qualifier-name = X) must evaluate to a front-end constant containing no specialization constants." At the end of the paragraphs describing the *location* rules, add this paragraph: "When generating SPIR-V, all *in* and *out* qualified user-declared (non built-in) variables and blocks (or all their members) must have a shader-specified *location*. Otherwise, a compile-time error is generated." [Note that an earlier existing rule just above this says "If a block has no block-level *location* layout qualifier, it is required that either all or none of its members have a *location* layout qualifier, or a compile- time error results."] Add a new subsection at the end of section 4.4: "4.4.x Specialization-Constant Qualifier "Specialization constants are declared using "layout(constant_id=...)". For example: layout(constant_id = 17) const int arraySize = 12; "The above makes a specialization constant with a default value of 12. 17 is the ID by which the API or other tools can later refer to this specific specialization constant. If it is never changed before final lowering, it will retain the value of 12. It is a compile-time error to use the constant_id qualifier on anything but a scalar bool, int, uint, float, or double. "Built-in constants can be declared to be specialization constants. For example, layout(constant_id = 31) gl_MaxClipDistances; // add specialization id "The declaration uses just the name of the previously declared built-in variable, with a constant_id layout declaration. It is a compile-time error to do this after the constant has been used: Constants are strictly either non-specialization constants or specialization constants, not both. "The built-in constant vector gl_WorkGroupSize can be specialized using the local_size_{xyz}_id qualifiers, to individually give the components an id. For example: layout(local_size_x_id = 18, local_size_z_id = 19) in; "This leaves gl_WorkGroupSize.y as a non-specialization constant, with gl_WorkGroupSize being a partially specialized vector. Its x and z components can be later specialized using the ids 18 and 19. These ids are declared independently from declaring the work-group size: layout(local_size_x = 32, local_size_y = 32) in; // size is (32,32,1) layout(local_size_x_id = 18) in; // constant_id for x layout(local_size_z_id = 19) in; // constant_id for z "Existing rules for declaring local_size_x, local_size_y, and local_size_z are not changed by this extension. For the local-size ids, it is a compile-time error to provide different id values for the same local-size id, or to provide them after any use. Otherwise, order, placement, number of statements, and replication do not cause errors. "Two arrays sized with specialization constants are the same type only if sized with the same symbol, involving no operations. layout(constant_id = 51) const int aSize = 20; const int pad = 2; const int total = aSize + pad; // specialization constant int a[total], b[total]; // a and b have the same type int c[22]; // different type than a or b int d[aSize + pad]; // different type than a, b, or c int e[aSize + 2]; // different type than a, b, c, or d "Types containing arrays sized with a specialization constant cannot be compared, assigned as aggregates, declared with an initializer, or used as an initializer. They can, however, be passed as arguments to functions having formal parameters of the same type. "Arrays inside a block may be sized with a specialization constant, but the block will have a static layout. Changing the specialized size will not re-layout the block. In the absence of explicit offsets, the layout will be based on the default size of the array." Change section 4.4.5 Uniform and Shader Storage Block Layout Qualifiers Add "The 'shared' and 'packed' layout qualifiers are not available when generating SPIR-V." Change "The initial state of compilation is as if the following were declared:" To "The initial state of compilation when generating SPIR-V is as if the following were declared:" layout(std140, column_major) uniform; layout(std430, column_major) buffer; "The initial state of compilation when not generating SPIR-V is as if the following were declared:..." Change "It is a compile-time error to specify an offset that is smaller than the offset of the previous member in the block or that lies within the previous member of the block." To "It is a compile-time error to have any offset, explicit or assigned, that lies within another member of the block. When not generating SPIR-V, it is a compile-time error to specify an offset that is smaller than the offset of the previous member in the block." Changes to Chapter 5 of the OpenGL Shading Language 4.50 Specification (Operators and Expressions) Add a section at the end of section 5 "5.x Specialization Constant Operations" Only some operations discussed in this section may be applied to a specialization constant and still yield a result that is as specialization constant. The operations allowed are listed below. When a specialization constant is operated on with one of these operators and with another constant or specialization constant, the result is implicitly a specialization constant. - int(), uint(), and bool() constructors for type conversions from any of the following types to any of the following types: * int * uint * bool - vector versions of the above conversion constructors - allowed implicit conversions of the above - swizzles (e.g., foo.yx) - The following when applied to integer or unsigned integer types: * unary negative ( - ) * binary operations ( + , - , * , / , % ) * shift ( <<, >> ) * bitwise operations ( & , | , ^ ) - The following when applied to integer or unsigned integer scalar types: * comparison ( == , != , > , >= , < , <= ) - The following when applied to the Boolean scalar type: * not ( ! ) * logical operations ( && , || , ^^ ) * comparison ( == , != ) - The ternary operator ( ? : ) Changes to Chapter 6 of the OpenGL Shading Language 4.50 Specification Add at the beginning of section 6.1.2 Subroutines: "Subroutine functionality is not available when generating SPIR-V." Changes to Chapter 7 of the OpenGL Shading Language 4.50 Specification (Built-In Variables) Add to the beginning of section 7.4 Built-In Uniform State: "Built-in uniform state is not available when generating SPIR-V." Section 8.14 "Noise Functions" Add: "Noise functions are not present when generating SPIR-V." Changes to Chapter 9 of the OpenGL Shading Language 4.50 Specification (Shading Language Grammar for Core Profile) Arrays can no longer require the size to be a compile-time folded constant expression. Change | LEFT_BRACKET constant_expression RIGHT_BRACKET to | LEFT_BRACKET conditional_expression RIGHT_BRACKET and change | array_specifier LEFT_BRACKET constant_expression RIGHT_BRACKET to | array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET Changes to the SPIR-V specification Section 3.20. Decoration *Offset* can also apply to an object, for transform feedback. *Offset* can also apply to an object in AtomicCounter storage for an atomic counter. Dependencies on ARB_parallel_shader_compile If ARB_parallel_shader_compile is supported, the shader specialization may occur on a background thread in the same manner that compiling and linking does. Dependencies on ARB_separate_shader_objects If ARB_separate_shader_objects is not supported, ignore all references to separable program objects. Dependencies on ARB_program_interface_query If ARB_prorgram_interface_query is not supported, ignore references to commands added by this extension, however other commands defined in terms of these functions still operate as specified before the addition of the program interface query extension. New State Add the following to Table 23.30 "Shader Object State" Get Value Type Get Command Initial Value Description Sec ----------------- ----- ------------ ------------- --------------------- --- SPIR_V_BINARY_ARB B GetShaderiv FALSE Shader is associated 7.2 with a SPIR-V module. Usage Example const uint* pSpirVModule; // This points to the SPIR-V code in memory uint spirVLength; // Length of pSpirVModule in bytes. // Create the shader object. GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); // Load the SPIR-V module into the shader object glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, pSpirVModule, spirVLength); // This will now return FALSE GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); // Specialize the shader const GLuint constantIndices[] = { 0, 7, 3 }; const GLuint constantValues[] = { 1, 42, 0x3F800000 }; glSpecializeShaderARB(shader, "main", 3, constantIndices, constantValues); // This should now return TRUE glGetShaderiv(shader, GL_COMPILE_STATUS, &status); // Create a program, attach our shader to it, and link GLuint program = glCreateProgram(); glAttachShader(program, shader); glLinkProgram(program); Issues 1. Do we need separate extensions to expose legacy GLSL and ESSL legacy capabilities in SPIR-V? DISCUSSION: When GLSL was created, multiple extensions were simultaneously introduced to separate the API mechanism (ARB_shader_objects), the shading language (GL_ARB_shading_language_100) and the shader stages (GL_ARB_vertex_shader, GL_ARB_fragment_shader). We can expect that future versions of OpenGL will support SPIR-V as well but with a different set of capabilities. Additionally, it's probably enough for the ARB to only define a SPIR-V capabilities for OpenGL 4.5 but SPIR-V is not only going to be consumed by OpenGL implementation but also by engine tool chains which can't afford to only support the last version of OpenGL. As a result Unity would define its own private extensions at least to clearly define the capability sets our engine supports at time T, e.g.: GL_UNITY_spirv_webgl, GL_UNITY_spirv_base_level, GL_UNITY_spirv_base_compute, GL_UNITY_spirv_max_level, etc to target older versions of OpenGL without being forced to use a specific GLSL version. For example, GL_UNITY_spirv_base_compute has a feature subset between GLSL 420 + GL_ARB_shader_storage_buffer_object + GL_ARB_compute_shader + GL_ARB_shader_image_load_store and GLES 310 would correspond to what we call an OpenGL device level in Unity 5.1. We should except that many engines would have the need of such ISV specific capability sets. This said it's important for the Khronos Group to set future convergence point but OpenGL 4.5 is not realistic today, which is the domain where ISVs must work. Possible Resolution: Two extensions: - GL_ARB_program_binary_spirv to specify the mechanism. - GL_ARB_spir_v_gl_450 to specify the required SPIR-V capabilities for OpenGL 4.5. Key data needed: What will IHVs actually support? RESOLVED: Minimum: 3.3 for desktop 3.1 for ES [ however, ES is not covered by this extension ] Need to add capabilities needed to distinguish between 3.3 and 4.5. See bug 14520 for data on fine-grained capabilities and other relevant details. 2. Do we want to look at SPIR-V as a shading language or a binary format? DISCUSSION: Practically, this issue is about whether we want to load SPIR-V through the shader object API, the program binary API or do we need a new construct. Unfortunately, there is a lot of issues with the two first options and the third might be a little too complex. Support through program binary API: - SPIR-V is the standard binary format - It makes SPIR-V a post LinkProgram construct - No location remapping is possible (glBindAttribLocation, glBindFragDataLocation[Indexed]) - Can't support multiple OpEntryPoint instructions - Can't provide program arguments (alternative to #pragma) - Can't retrieve SPIR-V source. GL_NUM_PROGRAM_BINARY_FORMATS can enumerate multiple format but glGetProgramBinary doesn't allow us to select the format we want to query back. - Currently SPIR-V doesn't support multiple shader stages per module but this would be useful for linked programs. Support through shader object API: - OpenGL API allows to attach multiple shader objects but SPIR-V modules are fully contained: A single module define the entire stage. - SPIR-V modules don't have to support multiple stage per module but we lose the represent linked program and perform offline cross shader stage dead code elimination. - Need a new glCompileShader variant to support multiple entry-points (OpEntryPoint) or compilation arguments (OpCompileFlag) - glShaderSource doesn't allow to specify the shading language. Is looking at the first bytes to check the magic string okay? - Reflection may be striped from SPIR-V modules Support through new API, "modules": // The difference between a module and a shader: // - Can contain multiple entry points // - Could specify the language, support both GLSL and SPIR-V (maybe) // - Can contain multiple shader stages // - Cross shader stage dead code elimination could be perform offline. (How to hint the compiler? OpCompileFlag?) // - Could be faster to compile shader variants? // - Programs created with glLinkProgramModule don't need GL_PROGRAM_SEPARABLE // - No need to specify the shader stage at object creation, it's embedded in the SPIR-V module GLint length = 0; GLbyte* binary = LoadBinary(Path, &length); GLuint ModuleName = 0; glCreateModules(1, &ModuleName); glCompileModule(ModuleName, GL_SHADING_LANGUAGE_SPIR_V, &binary, &length); GLboolean HasReflection = GL_FALSE; glGetModuleiv(ModuleName, GL_MODULE_REFLECTION, &HasReflection); GLuint ProgramName[2]; glCreatePrograms(2, &ProgramName[0]); assert(HasReflection == GL_TRUE); GLchar const * Strings[] = {"gl_Position", "block.Color"}; glTransformFeedbackVaryings(ProgramName[0], 2, Strings, GL_INTERLEAVED_ATTRIBS); glBindAttribLocation(ProgramName[0], 0, "Position"); glBindAttribLocation(ProgramName[0], 1, "Color"); glBindFragDataLocation(ProgramName[0], 0, "Color"); glLinkProgramModule(ProgramName[0], ModuleName, "mainA", "-pragma optimize(on)"); glLinkProgramModule(ProgramName[1], ModuleName, "mainB", "-pragma optimize(on)"); Other notes: - API doesn't require GLSL -> SPIR-V followed by getProgramBinary() * need an ID for specifying you want a SPIR-V binary? (christophe) GetProgramBinary doesn't do conversion, currently we would retrieve a SPIR-V is the program binary was a SPIR-V binary. However, that would prevent query a of native binary cache. I guess we could add a program parameter or specific that if we use GL_PROGRAM_BINARY_RETRIEVABLE_HINT GetProgramBinary return a native program binary cache, otherwise we get a SPIR-V binary back. - LoadProgramBinary() needs a flag to link by SSO or by name? * or does API spec. just say this comes from using SSO mode or...? (christophe) GL_PROGRAM_SEPARABLE doesn't change linking by name or by location. In GLSL 450, when locations are present, we link by location. Otherwise, we link by name. GL_PROGRAM_SEPARABLE protects the shader code against a backward compatibility change regarding gl_PerVertex block. With separated shader stages, the block must be declared in the shader will before it was optional. This behavior was requested by AMD during the separated program ratification process. Neither the program binary API or the shader object API seem to match SPIR-V features. Creating a new module API has potential but maybe we could build something around the shader object. RESOLVED: Use the existing ShaderBinary() and a new entry point, SpecializeShaderARB() to set specialization constants. 3. Allow more than one stage per SPIR-V module? DISCUSSION: SPIR-V supports multiple entry points per module, of any stage, or any number of instances of the same stage. However, the knowledge of which of those might be used together lies outside the module. OpenGL supports both linked programs and separated programs. If we consider strict separated programs with a program per shader stage, we can rely on a SPIR-V module per program. However, linked programs allows combining multiple shader stages into a single program. Furthermore, linked programs are more efficient than separated programs as the implementation can perform cross shader stages optimizations and packing of inputs and outputs with linked programs. Allowing multiple shader stage in a module would profile a semantic for linked programs and would allow performing dead code elimination and input and output location packing offline. RESOLVED: Allow full generality of what SPIR-V supports, and have the new entry points in the API select the subset of a module that it cares about. 4. Do we want to expose multiple entry points? DISCUSSION: SPIR-V supports multiple entry points through the OpEntryPoint instruction. Do we want to expose this? A way to expose it would be to introduce a variant of glLinkProgram which takes the entry point main as an argument. Typically, engines such as UE4 and Unity are rebuilding a big shared shader library over and over again for each shader variation. While orthogonal to GL_ARB_program_instance, this feature would work extremely well with program instance for an application. We could defer but I'll rather have all the SPIR-V interactions mechanisms defined in one extension from start to provide first class citizenship to SPIR-V. RESOLVED: This is mostly a duplicate of Issue 3, and a single resolution for both is needed there in that one place. 5. Do we want to add support for "constantId" in OpenGL? RESOLVED: Have the new entry points in the API support specializing constants, as in Vulkan. 6. Do we want to support multiple modules per program DISCUSSION: A use case could be to compile subroutines into different modules and link a set of subroutines we want to use into a program canvas. In another use case, using multiple entry points we could build multiple code paths sharing the same shader library. SPIR-V for Vulkan currently expects fully linked stages. This could be part of a much needed rework or the subroutines. Okay to defer for now. RESOLVED: Fully linked modules only, no direct "subroutine" support. 7. Do we want to be able to provide compilation arguments? DISCUSSION: SPIR-V supports the OpCompileFlag instructions to store compilation arguments however OpenGL doesn't support compilation arguments. If we add a new variant of glLinkProgram, it could be an opportunity to add a compilation arguments parameter to glLinkProgram. Some use cases: - Compiling the shaders with different optimizations: It's currently supported with #pragma but SPIR-V doesn't contain preprocessor information - Vendors refused to fix GLSL bugs and there are a lot of them. The reason is that fixing these bugs could cause to break shader compilation hence applications. If we want to start a virtue circle, we need to give developers the opportunity to enforce conformant compilation. - Display on or off warnings in the compilation log. - All the cool stuff C++ compilers are using compilation arguments for. Note that the instruction OpCompileFlag was removed from SPIR-V, and a Bug 13418 "Need a way to control target-compiler behavior from the API" was deferred to a future release. RESOLVED: No flags in first release. 8. Do we want to build a reflection API on SPIR-V modules specifics? - Retrieve compilation arguments - Retrieve shader stages included - Retrieve the list of entry points - Retrieve the list of required capabilities Arguably, it's trivial to build a SPIR-V parser and figure this out ourselves. DISCUSSION: If drivers implement SPIR-V support by lowering it to the same representation that GLSL is lowered to, and that representation is the source of reflection information, much reflection would naturally be present. RESOLVED: First start without reflection, then add later if needed. In the meantime, we have to document how the existing API operates with no reflection information. See Issue 22. 9. Separate programs require gl_PerVertex output blocks to be declared. How do we enforce this requirement in SPIR-V modules? DISCUSSION: Force compiler to generate a gl_PerVertex block if the shader code writes vertex-shader output built-ins? RESOLVED: (In the validation rules): SPIR-V should contain the same form of input/output block as expected by recent versions of GLSL. All in/out built-in show up in SPIR-V in blocks, as per Vulkan rules. GLSL can stay the same, front ends need to block non-blocked built-ins. 10. Do we want to support glBindAttribLocation and glBindFragDataLocation[Indexed] with SPIR-V? Also, what about glTransformFeedbackVaryings? DISCUSSION: Locations can be explicitly put into the shader, and then the application uses the right location. Note that SPIR-V 1.0 does not allow using specialization constants for layout(location=X) ... // X must be a literal, not specialized Alternatively, add a SPIR-V instruction that assigns a string name to an input which the API can use to link with. This would initially be an extension to SPIR-V as part of this. RESOLVED: Don't support any of glBindAttribLocation, glBindFragDataLocation[Index], or glTransformFeedbackVaryings. 11. What capabilities do we need for GL 4.5? RESOLVED: There is a Appendix A.spv for this. 12. How is user-linkage done between stages? DISCUSSION: SPIR-V linkage is by location number and BuiltIn decoration. Does OpenGL need linkage by name? Is SSO required to work with SPIR-V? Require OpName can't be stripped out for linkage objects. If we use the program binary API, yes but this is not desirable. See issue 2. RESOLVED: Link the Vulkan way: built-in decorations, location numbers, etc., never by name. No reflection required. This is a lot like SSO. 13. Can we really handle separate textures and samplers? DISCUSSION: AMD: No, can't do this because OpenGL has features that need cross validation that can't be done. RESOLVED: Remove separate textures and samplers. 14. Are there differences in binding counting between Vulkan and OpenGL? DISCUSSION: Yes, OpenGL uses multiple binding points for a resource array while Vulkan uses just one. RESOLVED: This leaves OpenGL as is, but state in the overview what this difference is. 15. What GL version(s) should be required for the ImageQuery OpCapability? DISCUSSION: The GL features it corresponds with are: - textureSize - EXT_gpu_shader4 (GL 3.0) - textureQueryLod - ARB_texture_query_lod (GL 4.0) - textureQueryLevels - ARB_texture_query_levels (GL 4.3) - imageSize - ARB_shader_image_size (GL 4.3) - textureSamples, imageSamples - ARB_shader_texture_image_samples (GL 4.5) The belief is that these are largely software features and while some of them were not added until later API versions, it was not because of hardware reasons. RESOLVED: Require ImageQuery to be supported for all versions of GL, as it is required for all Vulkan implementations. 16. At what point should an error due to an invalid SPIR-V module/capability be reported? ShaderBinary, SpecializeShaderARB, LinkProgram time? ShaderBinary says a "valid SPIR-V module binary" is required, but you can have a valid module that uses capabilities or extensions not supported by an implementation. RESOLVED. ShaderBinary is expected to form an association between the SPIR-V module and likely would not parse the module as would be required to detect unsupported capabilities or other validation failures. In order to avoid requiring the implementation to parse the module multiples times, we allow this analysis to happen at either SpecializeShaderARB or LinkProgram time, similar to how many errors for source shaders are detected at either compile or link time. 17. Should we allow program objects to contain both source shaders and SPIR-V binary shaders, or should this be a link time failure? RESOLVED. No. This would be needlessly complex to specify. Make this a link-time error. This can be determined by examining the SPIR_V_BINARY_ARB state of each shader. They must either: all be TRUE (SPIR-V shader) or all be FALSE (source shaders). If an application really wishes to mix source and SPIR-V binary shaders, this can be done at program object boundaries by using separable program objects (if supported). 18. Do we need a section for "SPIR-V Transform Feedback Interface"? This would discuss any requirements for the Xfb related decorations in SPIR-V. RESOLVED. Yes. 19. How should the program interface query operations behave for program objects created from SPIR-V shaders? DISCUSSION: we previously said we didn't need reflection to work for SPIR-V shaders (at least for the first version), however we are left with specifying how it should "not work". The primary issue is that SPIR-V binaries are not required to have names associated with variables. They can be associated in debug information, but there is no requirement for that to be present, and it should not be relied upon. Options: A) Make it work. If debug names are present they are enumerated correctly with associated variables. If the names aren't present, the compiler or driver gets to make them up. Alternatively, we could augment SPIR-V to have a mode where all interface variables need to have names which must not be stripped. B) Completely disallow it. All/Most such operations return an error. This may result in some implementation-dependent behavior which is impossible to know (although this problem may exist anyhow due to the offline-compilation step). This would likely include not being able to tell how many active resources there are and related problems. C) Allow as much as possible to work "naturally". You can query for the number of active resources, and for details about them. Anything that doesn't query by name will work as expected. Queries for maximum length of names return one. Queries for anything "by name" return INVALID_INDEX (or -1). Querying the name property of a resource returns an empty string. This may allow many queries to work, but it's not clear how useful it would be if you can't actually know which specific variable you are retrieving information on. If everything is specified a-priori by location/binding/offset/index/component in the shader, this may be sufficient. RESOLVED. Pick (c), but also allow debug names to be returned if an implementation wants to. 20. How should we deal with implementation-dependent behavior that must normally be queried after linking? Examples include: - set of active resources - offsets and strides of GLSLShared and GLSLPacked UBO/SSBOs - MATRIX_STRIDE, UNIFORM_ARRAY_STRIDE for UBOs (only relevant for packed/shared?) - UNIFORM_ARRAY_STRIDE for arrays of atomic counter buffers. DISCUSSION: - Option (c) from issue 19 allows determination of active resources (by shader specified layouts, but not name). - GLSLShared and GLSLPacked should not be allowed in this extension as there is no way to sensibly support, short of Option (a) from Issue 19. - For arrays of atomic counters, Option (c) from Issue 19 may be enough to figure this out, but I'm not sure that will work for offline compilation. Do we need to define a standard "layout" (stride) for arrays of atomic counters? RESOLVED: Picked (c) in issue 19, allowing determination of the number and types of active resources. Remove the shared and packed layouts and have the same behavior as in Vulkan. Atomic counter buffers don't have an associated name string already so there should be no issue with figuring out the UNIFORM_ARRAY_STRIDE for them. 21. What do we need to say about various linking rules related to "named uniform blocks" and "named shader storage blocks"? RESOLVED. We don't need to say anything, as they won't have names in SPIR-V shaders, so they aren't really "named". Instead of being identified by name they are identified (and matched) by uniform block index and/or binding. 22. How do the program interface query APIs work when no name reflection information is available? RESOLVED: The following naturally follows from the specification: GetProgramInterfaceiv(.., pname=MAX_NAME_LENGTH, ..) -> params = 1 GetProgramResourceIndex(.., name) -> INVALID_INDEX GetProgramResourceName(.., length, name) -> length=0, name = "" GetProgramResourceiv(.., props=NAME_LENGTH, ..) -> params = 1 GetProgramResourceLocation(.., name) -> -1 GetProgramResourceLocationIndex(.., name) -> -1 GetUniformLocation(.., name) -> -1 GetActiveUniformName(.., length, uniformName) -> length=0, uniformName = "" GetUniformIndices(..,uniformNames, uniformIndices) -> uniformIndices=INVALID_INDEX GetActiveUniform(..,length,.., name) -> length = 0, name = "" GetActiveUniformsiv(..,pname=UNIFORM_NAME_LENGTH,..) -> params = 1 GetUniformBlockIndex(.., uniformBlockName) -> INVALID_INDEX GetActiveUniformBlockName(..,length,uniformBlockName) -> length=0, uniformBlockName="" GetActiveUniformBlockiv(.., pname=UNIFORM_BLOCK_NAME_LENGTH, ..) -> params = 1 GetActiveAttrib(..) -> length = 0, name = "" GetAttribLocation(.., name) -> -1 GetTransformFeedbackVarying(..) -> length = 0, name = "" GetFragDatatLocation(.., name) -> -1 GetFragDataIndex(.., name) -> -1 GetProgramiv(.., pname=ACTIVE_ATTRIBUTE_MAX_LENGTH, ..) -> params = 1 GetProgramiv(.., pname=ACTIVE_UNIFORM_MAX_LENGTH, ..) -> params = 1 GetProgramiv(.., pname=TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, ..) -> params = 1 GetProgramiv(.., pname=ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, ..) -> params = 1 23. How does setting uniforms work if we can't query for locations based on names? RESOLVED. The shaders need to specify explicit locations for all uniform variables. The application can use offline reflection (or a reflection library) to know which variables are at which location, and then use that to specify the values of uniforms by location, as normal (e.g. Uniform*, ProgramUniform*). 24. What about UniformBlockBinding and ShaderStorageBlockBinding? RESOLVED. Because it is not possible to obtain the uniform block index or storage block index from an unnamed block, the binding value remains as specified in the shader by the layout qualifier or decoration. If you are feeling lucky you could just guess and remap used index values [0, #active blocks), but you won't really know what is going where, so just treat it as an immutable binding, similar to the atomic counter buffer binding point. Really. 25. What about subroutine queries based on names? RESOLVED. SPIR-V does not currently support subroutines, so it is not possibly to have any active subroutines from a SPIR-V based shader, and thus there is never anything to report. 26. How do we change the location of samplers and images? RESOLVED. You don't. Well you can using Uniform1i as usual, but you have no way of knowing which location corresponds to which sampler or image variable as GetUniformLocation won't work without a named variable. Best to just treat them as immutable bindings which are specified in the shader source or binary. 27. Appendix A.spv.3 gives a list of required capabilities that correspond to the core GL 4.5 features as well as for *some* of the ARB extensions that require shading language support. What about the rest of the ARB and vendor extensions that require shading language support? RESOLVED: Extensions that can be represented in terms of core SPIR-V 1.0 functionality don't need any specific consideration. Other ARB or vendor extensions that require SPIR-V functionality that can't be expressed in terms of SPIR-V 1.0 functionality will need to have SPIR-V extensions defined to add the required functionality. Further GL extensions can be defined to advertise support for consuming such SPIR-V capabilities once they have been defined. A (partial) list of extensions that are expected to need some form of modification to SPIR-V follows. ARB_shader_viewport_layer_array - need to allow ViewportIndex and Layer to be output by VS and TES. ARB_shader_clock - need clock() builtin function ARB_shader_ballot - use subgroup capability and add other builtins ARB_shader_atomic_counter_ops - need atomic counter builtin functions ARB_post_depth_coverage - need post_depth_coverage layout ARB_fragment_shader_interlock - need new layouts and interlock builtin functions KHR_blend_equation_advanced - need new layouts ARB_shader_group_vote - need new builtin functions ARB_shader_draw_parameters - need new builtin variables ARB_bindless_texture - need new layout qualifiers and probably other semantics ARB_compute_variable_group_size - need new layout qualifiers and builtin variables Revision History Rev. Date Author Changes ---- ----------- ------------ --------------------------------- 39 25-Apr-2018 JohnK add mappings of barriers and atomics 38 10-Apr-2018 dgkoch OpImageQuerySizeLod and OpImageQuerylevels only work with Sampled images (SPIR-V/issues/280). 37 20-Feb-2018 dgkoch Add whitelist for accepted storage classes (SPIR-V/issues/166). Require Binding for uniform and storage buffer blocks (opengl/api/issues/55). 36 15-Nov-2017 dgkoch clarify error for glSpecializeShader and add new error if shader types mismatch (OpenGL-API/issues/16) 35 25-Oct-2017 JohnK remove the already deprecated noise functions 34 29-May-2017 dgkoch Fix typos. RuntimeArrays are only supported on SSBOs. 33 26-May-2017 JohnK Require in/out explicit locations 32 25-Apr-2017 JohnK Bring up-to-date: Out-of-order block offsets gl_NumSamples not supported atomic counter restrictions bug fixes 31 21-Jul-2016 dgkoch Clarify string length on queries 30 13-Jul-2016 JohnK SPIR-V Offset can also apply to an atomic_uint offset. 29 04-Jul-2015 dgkoch Allow handles of the same shader types for ShaderBinary when using SPIRV. 28 09-Jun-2016 dgkoch Move future extensions to Issue 27. 27 08-Jun-2016 dgkoch Assign enums, add a couple missing errors. Editorial fixes. Specify required version and format of SPIRV. Add Issue 27. 26 02-Jun-2016 JohnK Completion of draft. Pack/unpack, TBD, and resolutions. 25 02-Jun-2016 JohnK Include XFB linking/interface rules 24 02-Jun-2016 JohnK Remove use of GLSL shared/packed and SPIR-V GLSLShared/GLSLPacked, bringing in KHR_vulkan_glsl rules for std140 and std430 23 01-Jun-2016 dgkoch Finish API edits. Cleanup editing pass on formatting and issue resolutions. Add issues 22-26 and resolve the rest. 22 26-May-2016 dgkoch Add ARB suffix to SpecializeShader Add SPIR_V_BINARY_ARB shader state (and some but not all related rules) Add a bunch of API edits alluding to SPIR-V shaders. Lots of comments about outstanding API areas that still need to be addressed. Add unresolved issues 16-21. 21 25-May-2016 JohnK Add interface matching rules 20 19-May-2016 dgkoch Remove language about 'default entry point' Recast features in terms of GL version. 19 19-May-2016 dgkoch Add min GLSL version required for built-in variable decorations and OpCapabilities. Fix various dates. 18 13-May-2016 JohnK Bring in the actual correct subset of GL_KHR_vulkan_glsl, rather than refer to it with differences 17 12-May-2016 dgkoch Verify capabilities for GLSL 4.50 Add capabilities for non-core ARB extensions, and extensions that already have SPIR-V correspondence. 16 12-May-2016 dgkoch grammatical fixes, replace non-ascii chars, formatting 15 11-May-2016 JohnK Clear up misc. TBDs throughout 14 11-May-2016 JohnK Flesh out GL_KHR_vulkan_glsl changes 13 11-May-2016 JohnK Move to final organization to flesh out 12 10-May-2016 JohnK Add the Vulkan validation rules that apply and the TBD from the f2f 11 21-Apr-2016 JohnK Editorial update to API description 10 11-Feb-2016 gsellers Add prototype API language. 9 31-Jan-2016 JohnK Issues 13 & 14 8 04-Dec-2015 JohnK Resolve issue 10 7 05-Nov-2015 JohnK Remove gl_FragColor, update issue 10 6 22-Oct-2015 JohnK Resolutions from Houston 5 21-Oct-2015 JohnK Make into a consistent format 4 16-Oct-2015 JohnK Added dependencies with GL_KHR_vulkan_glsl 3 08-Oct-2015 JohnK Added exec. env. detail, updated issues 2 22-Apr-2015 Christophe Added issues 1 26-Mar-2015 JohnK Initial revision