diff --git a/main.cpp b/main.cpp index fb003c25..0980f05a 100644 --- a/main.cpp +++ b/main.cpp @@ -268,11 +268,12 @@ struct CLIArguments uint32_t iterations = 1; bool cpp = false; bool metal = false; + bool vulkan_semantics = false; }; static void print_help() { - fprintf(stderr, "Usage: spirv-cross [--output ] [SPIR-V file] [--es] [--no-es] [--version ] [--dump-resources] [--help] [--force-temporary] [--cpp] [--metal] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] [--pls-in format input-name] [--pls-out format output-name]\n"); + fprintf(stderr, "Usage: spirv-cross [--output ] [SPIR-V file] [--es] [--no-es] [--version ] [--dump-resources] [--help] [--force-temporary] [--cpp] [--metal] [--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] [--pls-in format input-name] [--pls-out format output-name]\n"); } static vector remap_pls(const vector &pls_variables, const vector &resources, const vector *secondary_resources) @@ -347,6 +348,7 @@ int main(int argc, char *argv[]) cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); }); cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; }); cbs.add("--metal", [&args](CLIParser &) { args.metal = true; }); + cbs.add("--vulkan-semantics",[&args](CLIParser &) { args.vulkan_semantics = true; }); cbs.add("--pls-in", [&args](CLIParser &parser) { auto fmt = pls_format(parser.next_string()); @@ -401,6 +403,7 @@ int main(int argc, char *argv[]) if (args.set_es) opts.es = args.es; opts.force_temporary = args.force_temporary; + opts.vulkan_semantics = args.vulkan_semantics; opts.vertex.fixup_clipspace = args.fixup; compiler->set_options(opts); diff --git a/reference/shaders-vulkan/frag/input-attachment.frag.vk b/reference/shaders-vulkan/frag/input-attachment.frag.vk new file mode 100644 index 00000000..818af9d8 --- /dev/null +++ b/reference/shaders-vulkan/frag/input-attachment.frag.vk @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(input_attachment_index = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, binding = 1) uniform mediump subpassInput uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (subpassLoad(uSubpass0) + subpassLoad(uSubpass1)); +} + diff --git a/reference/shaders-vulkan/frag/push-constant.frag b/reference/shaders-vulkan/frag/push-constant.frag new file mode 100644 index 00000000..eee4ccdb --- /dev/null +++ b/reference/shaders-vulkan/frag/push-constant.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct PushConstants +{ + vec4 value0; + vec4 value1; +}; + +uniform PushConstants push; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = ((vColor + push.value0) + push.value1); +} + diff --git a/reference/shaders-vulkan/frag/push-constant.frag.vk b/reference/shaders-vulkan/frag/push-constant.frag.vk new file mode 100644 index 00000000..748a0286 --- /dev/null +++ b/reference/shaders-vulkan/frag/push-constant.frag.vk @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = ((vColor + push.value0) + push.value1); +} + diff --git a/reference/shaders-vulkan/frag/separate-sampler-texture.frag b/reference/shaders-vulkan/frag/separate-sampler-texture.frag.vk similarity index 100% rename from reference/shaders-vulkan/frag/separate-sampler-texture.frag rename to reference/shaders-vulkan/frag/separate-sampler-texture.frag.vk diff --git a/reference/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag b/reference/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag new file mode 100644 index 00000000..999dc452 --- /dev/null +++ b/reference/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag @@ -0,0 +1,38 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 1) uniform mediump texture2D uTexture; +layout(binding = 0) uniform mediump sampler uSampler; +layout(binding = 4) uniform mediump texture2DArray uTextureArray; +layout(binding = 3) uniform mediump textureCube uTextureCube; +layout(binding = 2) uniform mediump texture3D uTexture3D; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture, samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +void main() +{ + vec2 off = (vec2(1.0) / vec2(textureSize(uTexture, 0))); + vec2 off2 = (vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 1))); + highp vec2 param = ((vTex + off) + off2); + vec4 c0 = sample_func(uSampler, param); + highp vec2 param_1 = ((vTex + off) + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture, param_1); + vec4 c2 = texture(sampler2DArray(uTextureArray, uSampler), vTex3); + vec4 c3 = texture(samplerCube(uTextureCube, uSampler), vTex3); + vec4 c4 = texture(sampler3D(uTexture3D, uSampler), vTex3); + FragColor = ((((c0 + c1) + c2) + c3) + c4); +} + diff --git a/reference/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag.vk b/reference/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag.vk new file mode 100644 index 00000000..999dc452 --- /dev/null +++ b/reference/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag.vk @@ -0,0 +1,38 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 1) uniform mediump texture2D uTexture; +layout(binding = 0) uniform mediump sampler uSampler; +layout(binding = 4) uniform mediump texture2DArray uTextureArray; +layout(binding = 3) uniform mediump textureCube uTextureCube; +layout(binding = 2) uniform mediump texture3D uTexture3D; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture, samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +void main() +{ + vec2 off = (vec2(1.0) / vec2(textureSize(uTexture, 0))); + vec2 off2 = (vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 1))); + highp vec2 param = ((vTex + off) + off2); + vec4 c0 = sample_func(uSampler, param); + highp vec2 param_1 = ((vTex + off) + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture, param_1); + vec4 c2 = texture(sampler2DArray(uTextureArray, uSampler), vTex3); + vec4 c3 = texture(samplerCube(uTextureCube, uSampler), vTex3); + vec4 c4 = texture(sampler3D(uTexture3D, uSampler), vTex3); + FragColor = ((((c0 + c1) + c2) + c3) + c4); +} + diff --git a/reference/shaders-vulkan/vert/vulkan-vertex.vert b/reference/shaders-vulkan/vert/vulkan-vertex.vert new file mode 100644 index 00000000..8de2b111 --- /dev/null +++ b/reference/shaders-vulkan/vert/vulkan-vertex.vert @@ -0,0 +1,9 @@ +#version 310 es + +uniform int SPIRV_Cross_BaseInstance; + +void main() +{ + gl_Position = (vec4(1.0, 2.0, 3.0, 4.0) * float((gl_VertexID + (gl_InstanceID + SPIRV_Cross_BaseInstance)))); +} + diff --git a/reference/shaders-vulkan/vert/vulkan-vertex.vert.vk b/reference/shaders-vulkan/vert/vulkan-vertex.vert.vk new file mode 100644 index 00000000..9ee3cc09 --- /dev/null +++ b/reference/shaders-vulkan/vert/vulkan-vertex.vert.vk @@ -0,0 +1,7 @@ +#version 310 es + +void main() +{ + gl_Position = (vec4(1.0, 2.0, 3.0, 4.0) * float((gl_VertexIndex + gl_InstanceIndex))); +} + diff --git a/shaders-vulkan/frag/push-constant.frag b/shaders-vulkan/frag/push-constant.frag new file mode 100644 index 00000000..6180faba --- /dev/null +++ b/shaders-vulkan/frag/push-constant.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor + push.value0 + push.value1; +} diff --git a/shaders-vulkan/frag/separate-sampler-texture.frag b/shaders-vulkan/frag/separate-sampler-texture.nocompat.frag similarity index 100% rename from shaders-vulkan/frag/separate-sampler-texture.frag rename to shaders-vulkan/frag/separate-sampler-texture.nocompat.frag diff --git a/shaders-vulkan/vert/vulkan-vertex.vert b/shaders-vulkan/vert/vulkan-vertex.vert new file mode 100644 index 00000000..4d0438ac --- /dev/null +++ b/shaders-vulkan/vert/vulkan-vertex.vert @@ -0,0 +1,6 @@ +#version 310 es + +void main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex) * vec4(1.0, 2.0, 3.0, 4.0); +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index cc48d895..a7294eee 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -673,10 +673,20 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) auto flags = dec.decoration_flags; auto typeflags = meta[type.self].decoration.decoration_flags; + if (options.vulkan_semantics && var.storage == StorageClassPushConstant) + attr.push_back("push_constant"); + if (flags & (1ull << DecorationRowMajor)) attr.push_back("row_major"); if (flags & (1ull << DecorationColMajor)) attr.push_back("column_major"); + + if (options.vulkan_semantics) + { + if (flags & (1ull << DecorationInputAttachmentIndex)) + attr.push_back(join("input_attachment_index = ", dec.input_attachment)); + } + if (flags & (1ull << DecorationLocation)) attr.push_back(join("location = ", dec.location)); if ((flags & (1ull << DecorationDescriptorSet)) && dec.set != 0) // set = 0 is the default. @@ -692,8 +702,9 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) // If SPIR-V does not comply with either layout, we cannot really work around it. if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBlock))) attr.push_back("std140"); - - if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBufferBlock))) + else if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBufferBlock))) + attr.push_back(ssbo_is_std430_packing(type) ? "std430" : "std140"); + else if (options.vulkan_semantics && var.storage == StorageClassPushConstant) attr.push_back(ssbo_is_std430_packing(type) ? "std430" : "std140"); // For images, the type itself adds a layout qualifer. @@ -714,6 +725,19 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) } void CompilerGLSL::emit_push_constant_block(const SPIRVariable &var) +{ + if (options.vulkan_semantics) + emit_push_constant_block_vulkan(var); + else + emit_push_constant_block_glsl(var); +} + +void CompilerGLSL::emit_push_constant_block_vulkan(const SPIRVariable &var) +{ + emit_buffer_block(var); +} + +void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var) { // OpenGL has no concept of push constant blocks, implement it as a uniform struct. auto &type = get(var.basetype); @@ -988,7 +1012,7 @@ void CompilerGLSL::emit_resources() { // For gl_InstanceIndex emulation on GLES, the API user needs to // supply this uniform. - if (meta[var.self].decoration.builtin_type == BuiltInInstanceIndex) + if (meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics) { statement("uniform int SPIRV_Cross_BaseInstance;"); emitted = true; @@ -1942,13 +1966,25 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin) case BuiltInPointSize: return "gl_PointSize"; case BuiltInVertexId: + if (options.vulkan_semantics) + throw CompilerError( + "Cannot implement gl_VertexID in Vulkan GLSL. This shader was created with GL semantics."); return "gl_VertexID"; case BuiltInInstanceId: + if (options.vulkan_semantics) + throw CompilerError( + "Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics."); return "gl_InstanceID"; case BuiltInVertexIndex: - return "gl_VertexID"; // gl_VertexID already has the base offset applied. + if (options.vulkan_semantics) + return "gl_VertexIndex"; + else + return "gl_VertexID"; // gl_VertexID already has the base offset applied. case BuiltInInstanceIndex: - return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID. + if (options.vulkan_semantics) + return "gl_InstanceIndex"; + else + return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID. case BuiltInPrimitiveId: return "gl_PrimitiveID"; case BuiltInInvocationId: @@ -3191,9 +3227,16 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) } else if (type.image.dim == DimSubpassData) { - // Implement subpass loads via texture barrier style sampling. - // Fairly ugly, but should essentially work as a fallback for desktop. - imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)"); + if (options.vulkan_semantics) + { + // With Vulkan semantics, use the proper Vulkan GLSL construct. + imgexpr = join("subpassLoad(", to_expression(ops[2]), ")"); + } + else + { + // Implement subpass loads via texture barrier style sampling. + imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)"); + } pure = true; } else @@ -3504,6 +3547,9 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type) break; } + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics) + return res + "subpassInput"; + // If we're emulating subpassInput with samplers, force sampler2D // so we don't have to specify format. if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData) diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index cb8b33c8..7c8657ef 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -60,6 +60,10 @@ public: bool es = false; bool force_temporary = false; + // If true, Vulkan GLSL features are used instead of GL-compatible features. + // Mostly useful for debugging SPIR-V files. + bool vulkan_semantics = false; + enum Precision { DontCare, @@ -208,6 +212,8 @@ protected: void emit_resources(); void emit_buffer_block(const SPIRVariable &type); void emit_push_constant_block(const SPIRVariable &var); + void emit_push_constant_block_vulkan(const SPIRVariable &var); + void emit_push_constant_block_glsl(const SPIRVariable &var); void emit_interface_block(const SPIRVariable &type); void emit_block_chain(SPIRBlock &block); std::string emit_continue_block(uint32_t continue_block); diff --git a/test_shaders.py b/test_shaders.py index 07a5c312..4ce64758 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -72,10 +72,22 @@ def cross_compile(shader, vulkan): os.close(spirv_f) os.close(glsl_f) + if vulkan: + vulkan_glsl_f, vulkan_glsl_path = tempfile.mkstemp(suffix = os.path.basename(shader)) + os.close(vulkan_glsl_f) + subprocess.check_call(['glslangValidator', '-V' if vulkan else '-G', '-o', spirv_path, shader]) + subprocess.check_call(['./spirv-cross', '--output', glsl_path, spirv_path]) - validate_shader(glsl_path, vulkan) - return (spirv_path, glsl_path) + + if not ('nocompat' in glsl_path): + validate_shader(glsl_path, False) + + if vulkan: + subprocess.check_call(['./spirv-cross', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path]) + validate_shader(vulkan_glsl_path, vulkan) + + return (spirv_path, glsl_path, vulkan_glsl_path if vulkan else None) def md5_for_file(path): md5 = hashlib.md5() @@ -115,12 +127,14 @@ def regression_check(shader, glsl, update, keep): def test_shader(stats, shader, update, keep, vulkan): print('Testing shader:', shader) - spirv, glsl = cross_compile(shader, vulkan) + spirv, glsl, vulkan_glsl = cross_compile(shader, vulkan) if stats: cross_stats = get_shader_stats(glsl) regression_check(shader, glsl, update, keep) + if vulkan_glsl: + regression_check(shader + '.vk', vulkan_glsl, update, keep) os.remove(spirv) if stats: