简体   繁体   中英

Instanced GLSL shaders in Vulkan?

This is the (simplest) instancing shader I can come up with, which basically just transforms a bunch of 2D primitives:

#version 400
#extension GL_ARB_draw_instanced : enable
#extension GL_ARB_shading_language_420pack : enable
layout(std140, binding = 0) uniform VConstants {
    vec4 vfuniforms[48];
};
in vec4 pos;
void main() {

    gl_Position = vec4(0.0,0,0.0,1);
    gl_Position.x = dot(pos, vfuniforms[int(float(gl_InstanceID) * 2.0)]);
    gl_Position.y = dot(pos, vfuniforms[int(float(gl_InstanceID) * 2.0 + 1.0)]);

}

If I try to compile this to SPIR-V with the glslangValidator that comes with the Vulkan SDK, I get:

WARNING: 0:2: '#extension' : extension not supported: GL_ARB_draw_instanced
ERROR: 0:14: 'gl_InstanceID' : undeclared identifier
ERROR: 1 compilation errors.  No code generated.

If I remove the #extension GL_ARB_draw_instanced line, I still get the gl_InstanceID error. Is it possible to write instancing GLSL and compile them to SPIR-V? If so, what I am doing incorrectly?

The form of GLSL that the reference compiler uses to generate SPIR-V for Vulkan doesn't use normal OpenGL extensions. It follows the rules of GLSL 4.50, but it amends/overrides them by the dictates of the implicit pseudo-extension GL_KHR_vulkan_glsl . Note that you don't use #extension to initiate it; it is assumed to be active because you're using the Vulkan GLSL-to-SPIR-V compiler.

In particular, this extension removes gl_InstanceID (and gl_VertexID ). Instead, it creates its own variable, gl_InstanceIndex . The reason for this is because OpenGL was... stupid.

See, gl_InstanceID is the instance starting from the first instance in the instanced drawing command . However, when base-instanced rendering was added (the ability to render an arbitrary range of instances), gl_InstanceID was not updated to match. So if you start from a base instance of 2 with 5 instances, the first gl_InstanceID will still be zero (followed by 1, 2, 3, and 4). So the only things the base instance affects are instanced arrays .

Naturally, Vulkan wanted no part of OpenGL's stupidity in this regard, so it uses what most people would expect the instance value to be: the actual instance index the user asked to render. But this required a new variable, so that users wouldn't do what you're attempting: to accidentally use the same variable across both OpenGL and Vulkan without realizing that they have different semantics.

You'll either need two shaders or to check if VULKAN is defined, which is will be for those using the GL_KHR_vulkan_glsl extension. If it is, you use gl_InstanceIndex ; if not, you use gl_InstanceID . Also, your #extension declarations should be scoped as well, since the Vulkan GLSL-to-SPIR-V compiler will assume GLSL 4.50, and it won't necessarily offer up extensions.

The GL_ARB_draw_instanced extension is not necessary, it is supported by core GLSL 1.40 and vulkan. glslang does not seem to use the extension anyway: https://github.com/KhronosGroup/glslang/blob/master/glslang/MachineIndependent/Initialize.cpp#L1707

Instead of gl_InstanceID, I think that gl_InstanceIndex has to be used. The difference is that gl_InstanceID counts from 0, where gl_InstanceIndex counts from the base instance. See

https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/xhtml/vkspec.html#interfaces-builtin-variables

for more information about the builtins.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM