From 416566bab6d0309c77b6e6c8839805799db5ee6e Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Fri, 8 Jul 2016 10:47:03 +0200 Subject: [PATCH 01/11] Throw if word count of SPIR-V instruction is 0. Avoids infinite loop on garbage SPIR-V files. --- spirv_cross.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spirv_cross.cpp b/spirv_cross.cpp index d5fcb07e..071381d6 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -30,6 +30,10 @@ Instruction::Instruction(const vector &spirv, uint32_t &index) { op = spirv[index] & 0xffff; count = (spirv[index] >> 16) & 0xffff; + + if (count == 0) + throw CompilerError("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file."); + offset = index + 1; length = count - 1; From 2c7d2e4d3c76d65e19a63e555575bb4c576b7fd4 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Mon, 11 Jul 2016 12:47:46 +0200 Subject: [PATCH 02/11] Implement OpImageQuerySamples. --- .../frag/sampler-ms-query.desktop.frag | 14 ++++++++++ .../frag/sampler-ms-query.desktop.frag | 17 ++++++++++++ spirv_glsl.cpp | 27 ++++++++++++++++--- test_shaders.py | 6 ++++- 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 shaders/desktop-only/frag/sampler-ms-query.desktop.frag diff --git a/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag b/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 00000000..251603c9 --- /dev/null +++ b/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSampler; +layout(binding = 1) uniform sampler2DMSArray uSamplerArray; +layout(binding = 2, rgba8) uniform readonly writeonly image2DMS uImage; +layout(binding = 3, rgba8) uniform readonly writeonly image2DMSArray uImageArray; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float((((textureSamples(uSampler) + textureSamples(uSamplerArray)) + imageSamples(uImage)) + imageSamples(uImageArray)))); +} + diff --git a/shaders/desktop-only/frag/sampler-ms-query.desktop.frag b/shaders/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 00000000..f707ed5c --- /dev/null +++ b/shaders/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2DMS uSampler; +layout(binding = 1) uniform sampler2DMSArray uSamplerArray; +layout(rgba8, binding = 2) uniform image2DMS uImage; +layout(rgba8, binding = 3) uniform image2DMSArray uImageArray; + +void main() +{ + FragColor = + vec4( + textureSamples(uSampler) + + textureSamples(uSamplerArray) + + imageSamples(uImage) + + imageSamples(uImageArray)); +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 7e0b4cd5..7578081c 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -3498,7 +3498,27 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { uint32_t result_type = ops[0]; uint32_t id = ops[1]; - emit_op(result_type, id, to_expression(ops[2]), true, false); + auto &e = emit_op(result_type, id, to_expression(ops[2]), true, false); + + // When using the image, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + e.loaded_from = var ? var->self : 0; + break; + } + + case OpImageQuerySamples: + { + auto *var = maybe_get_backing_variable(ops[2]); + if (!var) + throw CompilerError( + "Bug. OpImageQuerySamples must have a backing variable so we know if the image is sampled or not."); + + auto &type = get(var->basetype); + bool image = type.image.sampled == 2; + if (image) + UFOP(imageSamples); + else + UFOP(textureSamples); break; } @@ -3603,6 +3623,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) uint32_t id = ops[1]; auto &e = set(id, join(to_expression(ops[2]), ", ", to_expression(ops[3])), result_type, true); + // When using the pointer, we need to know which variable it is actually loaded from. auto *var = maybe_get_backing_variable(ops[2]); e.loaded_from = var ? var->self : 0; break; @@ -3961,12 +3982,12 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type) throw CompilerError("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported."); } + if (type.image.ms) + res += "MS"; if (type.image.arrayed) res += "Array"; if (type.image.depth) res += "Shadow"; - if (type.image.ms) - res += "MS"; return res; } diff --git a/test_shaders.py b/test_shaders.py index 8b523caa..c9d5b95c 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -146,19 +146,23 @@ def regression_check(shader, glsl, update, keep): def shader_is_vulkan(shader): return '.vk.' in shader +def shader_is_desktop(shader): + return '.desktop.' in shader + def shader_is_spirv(shader): return '.asm.' in shader def test_shader(stats, shader, update, keep): joined_path = os.path.join(shader[0], shader[1]) vulkan = shader_is_vulkan(shader[1]) + desktop = shader_is_desktop(shader[1]) spirv = shader_is_spirv(shader[1]) print('Testing shader:', joined_path) spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, spirv) # Only test GLSL stats if we have a shader following GL semantics. - if stats and (not vulkan) and (not spirv): + if stats and (not vulkan) and (not spirv) and (not desktop): cross_stats = get_shader_stats(glsl) regression_check(shader, glsl, update, keep) From 7af13b68d5446afd4ff30e3b97508cfe33a6cc3e Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Mon, 11 Jul 2016 13:25:37 +0200 Subject: [PATCH 03/11] Support ImageRead/Write on multisampled images. --- .../desktop-only/frag/image-ms.desktop.frag | 13 +++++++++ .../desktop-only/frag/image-ms.desktop.frag | 12 ++++++++ spirv_glsl.cpp | 28 +++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 reference/shaders/desktop-only/frag/image-ms.desktop.frag create mode 100644 shaders/desktop-only/frag/image-ms.desktop.frag diff --git a/reference/shaders/desktop-only/frag/image-ms.desktop.frag b/reference/shaders/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 00000000..24644be1 --- /dev/null +++ b/reference/shaders/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(binding = 0, rgba8) uniform image2DMS uImage; +layout(binding = 1, rgba8) uniform image2DMSArray uImageArray; + +void main() +{ + vec4 a = imageLoad(uImage, ivec2(1, 2), 2); + vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4), 3); + imageStore(uImage, ivec2(2, 3), 1, a); + imageStore(uImageArray, ivec3(2, 3, 7), 1, b); +} + diff --git a/shaders/desktop-only/frag/image-ms.desktop.frag b/shaders/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 00000000..d3acc308 --- /dev/null +++ b/shaders/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(rgba8, binding = 0) uniform image2DMS uImage; +layout(rgba8, binding = 1) uniform image2DMSArray uImageArray; + +void main() +{ + vec4 a = imageLoad(uImage, ivec2(1, 2), 2); + vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4), 3); + imageStore(uImage, ivec2(2, 3), 1, a); + imageStore(uImageArray, ivec3(2, 3, 7), 1, b); +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 7578081c..d13ad6f3 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -3597,7 +3597,19 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) else { // Plain image load/store. - imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ")"); + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + throw CompilerError( + "Multisampled image used in OpImageRead, but unexpected operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ", + to_expression(samples), ")"); + } + else + imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ")"); pure = false; } @@ -3645,7 +3657,19 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) } } - statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ");"); + auto &type = expression_type(ops[0]); + if (type.image.ms) + { + uint32_t operands = ops[3]; + if (operands != ImageOperandsSampleMask || length != 5) + throw CompilerError("Multisampled image used in OpImageWrite, but unexpected operand mask was used."); + uint32_t samples = ops[4]; + statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(samples), + ", ", to_expression(ops[2]), ");"); + } + else + statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), + ");"); if (var && variable_storage_is_aliased(*var)) flush_all_aliased_variables(); From 3265e1fc3ff6b8404cb6f575136c136b46a6c1ca Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Mon, 11 Jul 2016 13:36:11 +0200 Subject: [PATCH 04/11] Implement subpassInputMS loading. --- .../vulkan/frag/input-attachment-ms.vk.frag | 14 ++++++++ .../frag/input-attachment-ms.vk.frag.vk | 14 ++++++++ .../vulkan/frag/input-attachment-ms.vk.frag | 11 ++++++ spirv_glsl.cpp | 36 ++++++++++++++++--- 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 reference/shaders/vulkan/frag/input-attachment-ms.vk.frag create mode 100644 reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk create mode 100644 shaders/vulkan/frag/input-attachment-ms.vk.frag diff --git a/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag b/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag new file mode 100644 index 00000000..7d745633 --- /dev/null +++ b/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DMS uSubpass0; +layout(binding = 1) uniform mediump sampler2DMS uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), 1) + texelFetch(uSubpass1, ivec2(gl_FragCoord.xy), 2)); +} + diff --git a/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk b/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk new file mode 100644 index 00000000..99a814c3 --- /dev/null +++ b/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInputMS uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2)); +} + diff --git a/shaders/vulkan/frag/input-attachment-ms.vk.frag b/shaders/vulkan/frag/input-attachment-ms.vk.frag new file mode 100644 index 00000000..206cbe48 --- /dev/null +++ b/shaders/vulkan/frag/input-attachment-ms.vk.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2); +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index d13ad6f3..976cc485 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -3560,6 +3560,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (var && var->remapped_variable) // Remapped input, just read as-is without any op-code { + if (type.image.ms) + throw CompilerError("Trying to remap multisampled image to variable, this is not possible."); + auto itr = find_if(begin(pls_inputs), end(pls_inputs), [var](const PlsRemap &pls) { return pls.id == var->self; }); @@ -3585,12 +3588,37 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (options.vulkan_semantics) { // With Vulkan semantics, use the proper Vulkan GLSL construct. - imgexpr = join("subpassLoad(", to_expression(ops[2]), ")"); + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + throw CompilerError( + "Multisampled image used in OpImageRead, but unexpected operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("subpassLoad(", to_expression(ops[2]), ", ", to_expression(samples), ")"); + } + else + 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)"); + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + throw CompilerError( + "Multisampled image used in OpImageRead, but unexpected operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ", + to_expression(samples), ")"); + } + else + { + // Implement subpass loads via texture barrier style sampling. + imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)"); + } } pure = true; } @@ -3967,7 +3995,7 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type) } if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics) - return res + "subpassInput"; + return res + "subpassInput" + (type.image.ms ? "MS" : ""); // If we're emulating subpassInput with samplers, force sampler2D // so we don't have to specify format. From 606ecce0f157c966873a536529614a142d971e4d Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 12 Jul 2016 09:35:15 +0200 Subject: [PATCH 05/11] Handle all desktop image formats. --- .../desktop-only/comp/image-formats.comp | 47 ++++++++++++ shaders/desktop-only/comp/image-formats.comp | 48 ++++++++++++ spirv_glsl.cpp | 73 ++++++++++++++++++- 3 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 reference/shaders/desktop-only/comp/image-formats.comp create mode 100644 shaders/desktop-only/comp/image-formats.comp diff --git a/reference/shaders/desktop-only/comp/image-formats.comp b/reference/shaders/desktop-only/comp/image-formats.comp new file mode 100644 index 00000000..7a079757 --- /dev/null +++ b/reference/shaders/desktop-only/comp/image-formats.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, rgba32f) uniform readonly writeonly image2D uImg00; +layout(binding = 1, rgba16f) uniform readonly writeonly image2D uImg01; +layout(binding = 2, rg32f) uniform readonly writeonly image2D uImg02; +layout(binding = 3, rg16f) uniform readonly writeonly image2D uImg03; +layout(binding = 4, r11f_g11f_b10f) uniform readonly writeonly image2D uImg04; +layout(binding = 5, r32f) uniform readonly writeonly image2D uImg05; +layout(binding = 6, r16f) uniform readonly writeonly image2D uImg06; +layout(binding = 7, rgba16) uniform readonly writeonly image2D uImg07; +layout(binding = 8, rgb10_a2) uniform readonly writeonly image2D uImg08; +layout(binding = 9, rgba8) uniform readonly writeonly image2D uImg09; +layout(binding = 10, rg16) uniform readonly writeonly image2D uImg10; +layout(binding = 11, rg8) uniform readonly writeonly image2D uImg11; +layout(binding = 12, r16) uniform readonly writeonly image2D uImg12; +layout(binding = 13, r8) uniform readonly writeonly image2D uImg13; +layout(binding = 14, rgba16_snorm) uniform readonly writeonly image2D uImg14; +layout(binding = 15, rgba8_snorm) uniform readonly writeonly image2D uImg15; +layout(binding = 16, rg16_snorm) uniform readonly writeonly image2D uImg16; +layout(binding = 17, rg8_snorm) uniform readonly writeonly image2D uImg17; +layout(binding = 18, r16_snorm) uniform readonly writeonly image2D uImg18; +layout(binding = 19, r8_snorm) uniform readonly writeonly image2D uImg19; +layout(binding = 20, rgba32i) uniform readonly writeonly iimage2D uImage20; +layout(binding = 21, rgba16i) uniform readonly writeonly iimage2D uImage21; +layout(binding = 22, rgba8i) uniform readonly writeonly iimage2D uImage22; +layout(binding = 23, rg32i) uniform readonly writeonly iimage2D uImage23; +layout(binding = 24, rg16i) uniform readonly writeonly iimage2D uImage24; +layout(binding = 25, rg8i) uniform readonly writeonly iimage2D uImage25; +layout(binding = 26, r32i) uniform readonly writeonly iimage2D uImage26; +layout(binding = 27, r16i) uniform readonly writeonly iimage2D uImage27; +layout(binding = 28, r8i) uniform readonly writeonly iimage2D uImage28; +layout(binding = 29, rgba32ui) uniform readonly writeonly uimage2D uImage29; +layout(binding = 30, rgba16ui) uniform readonly writeonly uimage2D uImage30; +layout(binding = 31, rgb10_a2ui) uniform readonly writeonly uimage2D uImage31; +layout(binding = 32, rgba8ui) uniform readonly writeonly uimage2D uImage32; +layout(binding = 33, rg32ui) uniform readonly writeonly uimage2D uImage33; +layout(binding = 34, rg16ui) uniform readonly writeonly uimage2D uImage34; +layout(binding = 35, rg8ui) uniform readonly writeonly uimage2D uImage35; +layout(binding = 36, r32ui) uniform readonly writeonly uimage2D uImage36; +layout(binding = 37, r16ui) uniform readonly writeonly uimage2D uImage37; +layout(binding = 38, r8ui) uniform readonly writeonly uimage2D uImage38; + +void main() +{ +} + diff --git a/shaders/desktop-only/comp/image-formats.comp b/shaders/desktop-only/comp/image-formats.comp new file mode 100644 index 00000000..5a70623c --- /dev/null +++ b/shaders/desktop-only/comp/image-formats.comp @@ -0,0 +1,48 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(rgba32f, binding = 0) uniform image2D uImg00; +layout(rgba16f, binding = 1) uniform image2D uImg01; +layout(rg32f, binding = 2) uniform image2D uImg02; +layout(rg16f, binding = 3) uniform image2D uImg03; +layout(r11f_g11f_b10f, binding = 4) uniform image2D uImg04; +layout(r32f, binding = 5) uniform image2D uImg05; +layout(r16f, binding = 6) uniform image2D uImg06; +layout(rgba16, binding = 7) uniform image2D uImg07; +layout(rgb10_a2, binding = 8) uniform image2D uImg08; +layout(rgba8, binding = 9) uniform image2D uImg09; +layout(rg16, binding = 10) uniform image2D uImg10; +layout(rg8, binding = 11) uniform image2D uImg11; +layout(r16, binding = 12) uniform image2D uImg12; +layout(r8, binding = 13) uniform image2D uImg13; +layout(rgba16_snorm, binding = 14) uniform image2D uImg14; +layout(rgba8_snorm, binding = 15) uniform image2D uImg15; +layout(rg16_snorm, binding = 16) uniform image2D uImg16; +layout(rg8_snorm, binding = 17) uniform image2D uImg17; +layout(r16_snorm, binding = 18) uniform image2D uImg18; +layout(r8_snorm, binding = 19) uniform image2D uImg19; + +layout(rgba32i, binding = 20) uniform iimage2D uImage20; +layout(rgba16i, binding = 21) uniform iimage2D uImage21; +layout(rgba8i, binding = 22) uniform iimage2D uImage22; +layout(rg32i, binding = 23) uniform iimage2D uImage23; +layout(rg16i, binding = 24) uniform iimage2D uImage24; +layout(rg8i, binding = 25) uniform iimage2D uImage25; +layout(r32i, binding = 26) uniform iimage2D uImage26; +layout(r16i, binding = 27) uniform iimage2D uImage27; +layout(r8i, binding = 28) uniform iimage2D uImage28; + +layout(rgba32ui, binding = 29) uniform uimage2D uImage29; +layout(rgba16ui, binding = 30) uniform uimage2D uImage30; +layout(rgb10_a2ui, binding = 31) uniform uimage2D uImage31; +layout(rgba8ui, binding = 32) uniform uimage2D uImage32; +layout(rg32ui, binding = 33) uniform uimage2D uImage33; +layout(rg16ui, binding = 34) uniform uimage2D uImage34; +layout(rg8ui, binding = 35) uniform uimage2D uImage35; +layout(r32ui, binding = 36) uniform uimage2D uImage36; +layout(r16ui, binding = 37) uniform uimage2D uImage37; +layout(r8ui, binding = 38) uniform uimage2D uImage38; + +void main() +{ +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 976cc485..34f878bb 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -460,7 +460,11 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) { - // Only handle GLES 3.1 compliant types for now ... + auto check_desktop = [this] { + if (options.es) + throw CompilerError("Attempting to use image format not supported in ES profile."); + }; + switch (format) { case ImageFormatRgba32f: @@ -504,10 +508,73 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) case ImageFormatRg16ui: return "rg16ui"; + // Desktop-only formats + case ImageFormatR11fG11fB10f: + check_desktop(); + return "r11f_g11f_b10f"; + case ImageFormatR16f: + check_desktop(); + return "r16f"; + case ImageFormatRgb10A2: + check_desktop(); + return "rgb10_a2"; + case ImageFormatR8: + check_desktop(); + return "r8"; + case ImageFormatRg8: + check_desktop(); + return "rg8"; + case ImageFormatR16: + check_desktop(); + return "r16"; + case ImageFormatRg16: + check_desktop(); + return "rg16"; + case ImageFormatRgba16: + check_desktop(); + return "rgba16"; + case ImageFormatR16Snorm: + check_desktop(); + return "r16_snorm"; + case ImageFormatRg16Snorm: + check_desktop(); + return "rg16_snorm"; + case ImageFormatRgba16Snorm: + check_desktop(); + return "rgba16_snorm"; + case ImageFormatR8Snorm: + check_desktop(); + return "r8_snorm"; + case ImageFormatRg8Snorm: + check_desktop(); + return "rg8_snorm"; + + case ImageFormatR8ui: + check_desktop(); + return "r8ui"; + case ImageFormatRg8ui: + check_desktop(); + return "rg8ui"; + case ImageFormatR16ui: + check_desktop(); + return "r16ui"; + case ImageFormatRgb10a2ui: + check_desktop(); + return "rgb10_a2ui"; + + case ImageFormatR8i: + check_desktop(); + return "r8i"; + case ImageFormatRg8i: + check_desktop(); + return "rg8i"; + case ImageFormatR16i: + check_desktop(); + return "r16i"; + + default: case ImageFormatUnknown: return nullptr; - default: - return "UNSUPPORTED"; // TODO: Fill in rest. } } From 2bfe98c35d1e06ea0c6dbb92b949d292d101433f Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 12 Jul 2016 09:37:31 +0200 Subject: [PATCH 06/11] Fix image-format test to use desktop extension. --- .../comp/{image-formats.comp => image-formats.desktop.comp} | 0 .../comp/{image-formats.comp => image-formats.desktop.comp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename reference/shaders/desktop-only/comp/{image-formats.comp => image-formats.desktop.comp} (100%) rename shaders/desktop-only/comp/{image-formats.comp => image-formats.desktop.comp} (100%) diff --git a/reference/shaders/desktop-only/comp/image-formats.comp b/reference/shaders/desktop-only/comp/image-formats.desktop.comp similarity index 100% rename from reference/shaders/desktop-only/comp/image-formats.comp rename to reference/shaders/desktop-only/comp/image-formats.desktop.comp diff --git a/shaders/desktop-only/comp/image-formats.comp b/shaders/desktop-only/comp/image-formats.desktop.comp similarity index 100% rename from shaders/desktop-only/comp/image-formats.comp rename to shaders/desktop-only/comp/image-formats.desktop.comp From 36a0b63f28cf3378dfc04dae5c86e4427d603681 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 12 Jul 2016 14:33:04 +0200 Subject: [PATCH 07/11] Fix various corner cases with expression dependencies. There was a potential problem if variables were invalidated and SPIR-V read expressions which depended on other expression which in turn depended on the invalidated variable. Also fixes issue where variables were considered immutable if they were forwardable. This allowed some incorrect optimizations to slip through. --- .../shaders/asm/comp/bitcast_iadd.asm.comp | 4 +- .../shaders/asm/frag/invalidation.asm.frag | 15 ++ reference/shaders/comp/generate_height.comp | 3 +- reference/shaders/comp/torture-loop.comp | 3 +- reference/shaders/tesc/water_tess.tesc | 3 +- reference/shaders/vert/ground.vert | 3 +- shaders/asm/comp/bitcast_iadd.asm.comp | 2 + shaders/asm/frag/invalidation.asm.frag | 43 ++++++ spirv_common.hpp | 6 +- spirv_cross.cpp | 26 +++- spirv_cross.hpp | 1 + spirv_glsl.cpp | 140 ++++++++++++------ spirv_glsl.hpp | 2 + 13 files changed, 191 insertions(+), 60 deletions(-) create mode 100644 reference/shaders/asm/frag/invalidation.asm.frag create mode 100644 shaders/asm/frag/invalidation.asm.frag diff --git a/reference/shaders/asm/comp/bitcast_iadd.asm.comp b/reference/shaders/asm/comp/bitcast_iadd.asm.comp index acc39eb2..6a3e5af6 100644 --- a/reference/shaders/asm/comp/bitcast_iadd.asm.comp +++ b/reference/shaders/asm/comp/bitcast_iadd.asm.comp @@ -1,13 +1,13 @@ #version 310 es layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; -layout(binding = 0, std430) buffer _3 +layout(binding = 0, std430) restrict buffer _3 { ivec4 _0; uvec4 _1; } _5; -layout(binding = 1, std430) buffer _4 +layout(binding = 1, std430) restrict buffer _4 { uvec4 _0; ivec4 _1; diff --git a/reference/shaders/asm/frag/invalidation.asm.frag b/reference/shaders/asm/frag/invalidation.asm.frag new file mode 100644 index 00000000..b702bf6b --- /dev/null +++ b/reference/shaders/asm/frag/invalidation.asm.frag @@ -0,0 +1,15 @@ +#version 450 + +in float v0; +in float v1; +out float FragColor; + +void main() +{ + float a = v0; + float b = v1; + float _17 = a; + a = v1; + FragColor = ((_17 + b) * b); +} + diff --git a/reference/shaders/comp/generate_height.comp b/reference/shaders/comp/generate_height.comp index 1950f9b2..d65a4b3e 100644 --- a/reference/shaders/comp/generate_height.comp +++ b/reference/shaders/comp/generate_height.comp @@ -28,6 +28,7 @@ uvec2 workaround_mix(uvec2 a, uvec2 b, bvec2 sel) { _137 = a.x; } + uint _147 = _137; if (sel.y) { _148 = b.y; @@ -36,7 +37,7 @@ uvec2 workaround_mix(uvec2 a, uvec2 b, bvec2 sel) { _148 = a.y; } - return uvec2(_137, _148); + return uvec2(_147, _148); } vec2 alias(vec2 i, vec2 N) diff --git a/reference/shaders/comp/torture-loop.comp b/reference/shaders/comp/torture-loop.comp index 11c8cb85..e27632a8 100644 --- a/reference/shaders/comp/torture-loop.comp +++ b/reference/shaders/comp/torture-loop.comp @@ -21,7 +21,8 @@ void main() uint j; for (;;) { - int _40 = k + 1; + int _39 = k; + int _40 = _39 + 1; k = _40; if ((_40 < 10)) { diff --git a/reference/shaders/tesc/water_tess.tesc b/reference/shaders/tesc/water_tess.tesc index ad39f77b..a933068d 100644 --- a/reference/shaders/tesc/water_tess.tesc +++ b/reference/shaders/tesc/water_tess.tesc @@ -26,7 +26,8 @@ bool frustum_cull(vec2 p0) float radius = (0.5 * length((bb_max - bb_min))); vec3 f0 = vec3(dot(_41.uFrustum[0], vec4(center, 1.0)), dot(_41.uFrustum[1], vec4(center, 1.0)), dot(_41.uFrustum[2], vec4(center, 1.0))); vec3 f1 = vec3(dot(_41.uFrustum[3], vec4(center, 1.0)), dot(_41.uFrustum[4], vec4(center, 1.0)), dot(_41.uFrustum[5], vec4(center, 1.0))); - bool _205 = any(lessThanEqual(f0, vec3((-radius)))); + vec3 _199 = f0; + bool _205 = any(lessThanEqual(_199, vec3((-radius)))); bool _215; if ((!_205)) { diff --git a/reference/shaders/vert/ground.vert b/reference/shaders/vert/ground.vert index 5f4d0454..d8c039a1 100644 --- a/reference/shaders/vert/ground.vert +++ b/reference/shaders/vert/ground.vert @@ -69,6 +69,7 @@ vec2 warp_position() { _332 = 0u; } + uint _342 = _332; if ((uPosition.y < 32u)) { _343 = mask.y; @@ -77,7 +78,7 @@ vec2 warp_position() { _343 = 0u; } - rounding = uvec2(_332, _343); + rounding = uvec2(_342, _343); lower_upper_snapped = vec4(((uPosition + rounding).xyxy & (~mask).xxyy)); return mix(lower_upper_snapped.xy, lower_upper_snapped.zw, vec2(fract_lod)); } diff --git a/shaders/asm/comp/bitcast_iadd.asm.comp b/shaders/asm/comp/bitcast_iadd.asm.comp index 62622ce3..3b31ab28 100644 --- a/shaders/asm/comp/bitcast_iadd.asm.comp +++ b/shaders/asm/comp/bitcast_iadd.asm.comp @@ -18,9 +18,11 @@ OpDecorate %input_struct BufferBlock OpDecorate %inputs DescriptorSet 0 OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict OpDecorate %output_struct BufferBlock OpDecorate %outputs DescriptorSet 0 OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict %void = OpTypeVoid %main_func = OpTypeFunction %void diff --git a/shaders/asm/frag/invalidation.asm.frag b/shaders/asm/frag/invalidation.asm.frag new file mode 100644 index 00000000..1c171b6d --- /dev/null +++ b/shaders/asm/frag/invalidation.asm.frag @@ -0,0 +1,43 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %v0 %v1 %FragColor + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %a "a" + OpName %v0 "v0" + OpName %b "b" + OpName %v1 "v1" + OpName %FragColor "FragColor" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %float = OpTypeFloat 32 + %pfloat = OpTypePointer Function %float + %9 = OpTypePointer Input %float + %v0 = OpVariable %9 Input + %v1 = OpVariable %9 Input + %25 = OpTypePointer Output %float + %FragColor = OpVariable %25 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %a = OpVariable %pfloat Function + %b = OpVariable %pfloat Function + %v0_tmp = OpLoad %float %v0 + %v1_tmp = OpLoad %float %v1 + OpStore %a %v0_tmp + OpStore %b %v1_tmp + + %a_tmp = OpLoad %float %a + %b_tmp = OpLoad %float %b + %res = OpFAdd %float %a_tmp %b_tmp + %res1 = OpFMul %float %res %b_tmp + OpStore %a %v1_tmp + OpStore %FragColor %res1 + OpReturn + OpFunctionEnd diff --git a/spirv_common.hpp b/spirv_common.hpp index f7d3f30d..2deb5afd 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -258,13 +258,15 @@ struct SPIRExpression : IVariant // If this expression will never change, we can avoid lots of temporaries // in high level source. + // An expression being immutable can be speculative, + // it is assumed that this is true almost always. bool immutable = false; // If this expression has been used while invalidated. bool used_while_invalidated = false; - // A list of a variables for which this expression was invalidated by. - std::vector invalidated_by; + // A list of expressions which this expression depends on. + std::vector expression_dependencies; }; struct SPIRFunctionPrototype : IVariant diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 071381d6..385ca435 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -60,7 +60,8 @@ bool Compiler::variable_storage_is_aliased(const SPIRVariable &v) bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0; bool image = type.basetype == SPIRType::Image; bool counter = type.basetype == SPIRType::AtomicCounter; - return ssbo || image || counter; + bool restrict = (meta[v.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0; + return !restrict && (ssbo || image || counter); } bool Compiler::block_is_pure(const SPIRBlock &block) @@ -274,10 +275,7 @@ void Compiler::register_write(uint32_t chain) void Compiler::flush_dependees(SPIRVariable &var) { for (auto expr : var.dependees) - { invalid_expressions.insert(expr); - get(expr).invalidated_by.push_back(var.self); - } var.dependees.clear(); } @@ -352,7 +350,7 @@ bool Compiler::is_immutable(uint32_t id) const // Anything we load from the UniformConstant address space is guaranteed to be immutable. bool pointer_to_const = var.storage == StorageClassUniformConstant; - return pointer_to_const || var.phi_variable || var.forwardable || !expression_is_lvalue(id); + return pointer_to_const || var.phi_variable || !expression_is_lvalue(id); } else if (ids[id].get_type() == TypeExpression) return get(id).immutable; @@ -2024,3 +2022,21 @@ uint32_t Compiler::get_subpass_input_remapped_components(uint32_t id) const { return get(id).remapped_components; } + +void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_expression) +{ + auto &e = get(dst); + auto *s = maybe_get(source_expression); + if (!s) + return; + + auto &e_deps = e.expression_dependencies; + auto &s_deps = s->expression_dependencies; + + // If we depend on a expression, we also depend on all sub-dependencies from source. + e_deps.push_back(source_expression); + e_deps.insert(end(e_deps), begin(s_deps), end(s_deps)); + + // Eliminate duplicated dependencies. + e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps)); +} diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 06a2aace..4c7dafc6 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -349,6 +349,7 @@ protected: uint32_t increase_bound_by(uint32_t incr_amount); bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + void inherit_expression_dependencies(uint32_t dst, uint32_t source); private: void parse(); diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 34f878bb..aa07da8a 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -872,7 +872,8 @@ void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var) void CompilerGLSL::emit_buffer_block(const SPIRVariable &var) { auto &type = get(var.basetype); - auto ssbo = meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock); + bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0; + bool restrict = (meta[var.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0; add_resource_name(var.self); @@ -886,7 +887,7 @@ void CompilerGLSL::emit_buffer_block(const SPIRVariable &var) else resource_names.insert(buffer_name); - statement(layout_for_variable(var) + (ssbo ? "buffer " : "uniform ") + buffer_name); + statement(layout_for_variable(var), restrict ? "restrict " : "", ssbo ? "buffer " : "uniform ", buffer_name); begin_scope(); type.member_name_cache.clear(); @@ -1176,33 +1177,44 @@ void CompilerGLSL::emit_resources() statement(""); } +void CompilerGLSL::handle_invalid_expression(uint32_t id) +{ + auto &expr = get(id); + + // This expression has been invalidated in the past. + // Be careful with this expression next pass ... + // Used for OpCompositeInsert forwarding atm. + expr.used_while_invalidated = true; + + // We tried to read an invalidated expression. + // This means we need another pass at compilation, but next time, force temporary variables so that they cannot be invalidated. + forced_temporaries.insert(id); + force_recompile = true; +} + string CompilerGLSL::to_expression(uint32_t id) { auto itr = invalid_expressions.find(id); if (itr != end(invalid_expressions)) + handle_invalid_expression(id); + + if (ids[id].get_type() == TypeExpression) { + // We might have a more complex chain of dependencies. + // A possible scenario is that we + // + // %1 = OpLoad + // %2 = OpDoSomething %1 %1. here %2 will have a dependency on %1. + // %3 = OpDoSomethingAgain %2 %2. Here %3 will lose the link to %1 since we don't propagate the dependencies like that. + // OpStore %1 %foo // Here we can invalidate %1, and hence all expressions which depend on %1. Only %2 will know since it's part of invalid_expressions. + // %4 = OpDoSomethingAnotherTime %3 %3 // If we forward all expressions we will see %1 expression after store, not before. + // + // However, we can propagate up a list of depended expressions when we used %2, so we can check if %2 is invalid when reading %3 after the store, + // and see that we should not forward reads of the original variable. auto &expr = get(id); - - // This expression has been invalidated in the past. - // Be careful with this expression next pass ... - // Used for OpCompositeInsert forwarding atm. - expr.used_while_invalidated = true; - - // We tried to read an invalidated expression. - // This means we need another pass at compilation, but next time, do not try to forward - // the variables which caused invalidation to happen in the first place. - for (auto var : expr.invalidated_by) - { - //fprintf(stderr, "Expression %u was invalidated due to variable %u being invalid at read time!\n", id, var); - get(var).forwardable = false; - } - - if (expr.invalidated_by.empty() && expr.loaded_from) - { - //fprintf(stderr, "Expression %u was invalidated due to variable %u being invalid at read time!\n", id, expr.loaded_from); - get(expr.loaded_from).forwardable = false; - } - force_recompile = true; + for (uint32_t dep : expr.expression_dependencies) + if (invalid_expressions.find(dep) != end(invalid_expressions)) + handle_invalid_expression(dep); } track_expression_read(id); @@ -1441,13 +1453,23 @@ SPIRExpression &CompilerGLSL::emit_op(uint32_t result_type, uint32_t result_id, void CompilerGLSL::emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) { - emit_op(result_type, result_id, join(op, to_expression(op0)), should_forward(op0), true); + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(op, to_expression(op0)), forward, true); + + if (forward && forced_temporaries.find(result_id) == end(forced_temporaries)) + inherit_expression_dependencies(result_id, op0); } void CompilerGLSL::emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op) { - emit_op(result_type, result_id, join(to_expression(op0), " ", op, " ", to_expression(op1)), - should_forward(op0) && should_forward(op1), true); + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, join(to_expression(op0), " ", op, " ", to_expression(op1)), forward, true); + + if (forward && forced_temporaries.find(result_id) == end(forced_temporaries)) + { + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + } } SPIRType CompilerGLSL::binary_op_bitcast_helper(string &cast_op0, string &cast_op1, SPIRType::BaseType &input_type, @@ -1516,14 +1538,23 @@ void CompilerGLSL::emit_binary_op_cast(uint32_t result_type, uint32_t result_id, void CompilerGLSL::emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) { - emit_op(result_type, result_id, join(op, "(", to_expression(op0), ")"), should_forward(op0), false); + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(op, "(", to_expression(op0), ")"), forward, false); + if (forward && forced_temporaries.find(result_id) == end(forced_temporaries)) + inherit_expression_dependencies(result_id, op0); } void CompilerGLSL::emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op) { - emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", ", to_expression(op1), ")"), - should_forward(op0) && should_forward(op1), false); + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", ", to_expression(op1), ")"), forward, false); + + if (forward && forced_temporaries.find(result_id) == end(forced_temporaries)) + { + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + } } void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, @@ -1554,17 +1585,33 @@ void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t resul void CompilerGLSL::emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op) { + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2); emit_op(result_type, result_id, - join(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(op2), ")"), - should_forward(op0) && should_forward(op1) && should_forward(op2), false); + join(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(op2), ")"), forward, false); + + if (forward && forced_temporaries.find(result_id) == end(forced_temporaries)) + { + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); + } } void CompilerGLSL::emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, uint32_t op3, const char *op) { + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3); emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(op2), ", ", to_expression(op3), ")"), - should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3), false); + forward, false); + + if (forward && forced_temporaries.find(result_id) == end(forced_temporaries)) + { + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); + inherit_expression_dependencies(result_id, op3); + } } string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype) @@ -2391,7 +2438,11 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32 bool CompilerGLSL::should_forward(uint32_t id) { - return is_immutable(id) && !options.force_temporary; + // Immutable expression can always be forwarded. + // If not immutable, we can speculate about it by forwarding potentially mutable variables. + auto *var = maybe_get(id); + bool forward = var ? var->forwardable : false; + return (is_immutable(id) || forward) && !options.force_temporary; } void CompilerGLSL::track_expression_read(uint32_t id) @@ -2665,18 +2716,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // If we're loading from memory that cannot be changed by the shader, // just forward the expression directly to avoid needless temporaries. - if (should_forward(ptr)) - { - set(id, to_expression(ptr), result_type, true); - register_read(id, ptr, true); - } - else - { - // If the variable can be modified after this OpLoad, we cannot just forward the expression. - // We must read it now and store it in a temporary. - emit_op(result_type, id, to_expression(ptr), false, false); - register_read(id, ptr, false); - } + // If an expression is mutable and forwardable, we speculate that it is immutable. + bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries); + + // Suppress usage tracking since using same expression multiple times does not imply any extra work. + emit_op(result_type, id, to_expression(ptr), forward, false, true); + register_read(id, ptr, forward); break; } @@ -2688,8 +2733,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) flush_variable_declaration(var->self); // If the base is immutable, the access chain pointer must also be. + // If an expression is mutable and forwardable, we speculate that it is immutable. auto e = access_chain(ops[2], &ops[3], length - 3, false); - auto &expr = set(ops[1], move(e), ops[0], is_immutable(ops[2])); + auto &expr = set(ops[1], move(e), ops[0], should_forward(ops[2])); expr.loaded_from = ops[2]; break; } @@ -2709,8 +2755,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // For this case, we don't need to invalidate anything and emit any opcode. if (lhs != rhs) { - register_write(ops[0]); statement(lhs, " = ", rhs, ";"); + register_write(ops[0]); } } break; diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 762440ab..e0045cf2 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -345,6 +345,8 @@ protected: void add_variable(std::unordered_set &variables, uint32_t id); void check_function_call_constraints(const uint32_t *args, uint32_t length); + + void handle_invalid_expression(uint32_t id); }; } From 7d8add33e4cb8071ced5f0b0f933985e5745dd19 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 12 Jul 2016 15:00:10 +0200 Subject: [PATCH 08/11] Avoid conflict with C99 keyword. --- spirv_cross.cpp | 4 ++-- spirv_glsl.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 385ca435..c3661578 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -60,8 +60,8 @@ bool Compiler::variable_storage_is_aliased(const SPIRVariable &v) bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0; bool image = type.basetype == SPIRType::Image; bool counter = type.basetype == SPIRType::AtomicCounter; - bool restrict = (meta[v.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0; - return !restrict && (ssbo || image || counter); + bool is_restrict = (meta[v.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0; + return !is_restrict && (ssbo || image || counter); } bool Compiler::block_is_pure(const SPIRBlock &block) diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index aa07da8a..fd929232 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -873,7 +873,7 @@ void CompilerGLSL::emit_buffer_block(const SPIRVariable &var) { auto &type = get(var.basetype); bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0; - bool restrict = (meta[var.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0; + bool is_restrict = (meta[var.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0; add_resource_name(var.self); @@ -887,7 +887,7 @@ void CompilerGLSL::emit_buffer_block(const SPIRVariable &var) else resource_names.insert(buffer_name); - statement(layout_for_variable(var), restrict ? "restrict " : "", ssbo ? "buffer " : "uniform ", buffer_name); + statement(layout_for_variable(var), is_restrict ? "restrict " : "", ssbo ? "buffer " : "uniform ", buffer_name); begin_scope(); type.member_name_cache.clear(); From 5c24d99ff22a25ef38e9e39985f80cf57a1e7418 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 12 Jul 2016 21:20:18 +0200 Subject: [PATCH 09/11] Add distinction between type_id and base_type_id in resource interface. type_id was not intuitive and did not allow for parsing array sizes of variables. Expose another member, base_type_id which will provide the base type suitable for parsing metadata such as decorations and type_id will now point to the actual type which includes full type information such as arrays and so on. --- main.cpp | 12 ++++++++---- spirv_cross.cpp | 22 +++++++++++----------- spirv_cross.hpp | 14 +++++++++++--- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/main.cpp b/main.cpp index a9c368c0..473ca846 100644 --- a/main.cpp +++ b/main.cpp @@ -196,10 +196,14 @@ static void print_resources(const Compiler &compiler, const char *tag, const vec bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant; bool is_block = (compiler.get_decoration_mask(type.self) & ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) != 0; - uint32_t fallback_id = !is_push_constant && is_block ? res.type_id : res.id; + uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id; - fprintf(stderr, " ID %03u : %s", res.id, - !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str()); + string array; + for (auto arr : type.array) + array = join("[", arr ? convert_to_string(arr) : "", "]") + array; + + fprintf(stderr, " ID %03u : %s%s", res.id, + !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str()); if (mask & (1ull << DecorationLocation)) fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation)); @@ -304,7 +308,7 @@ static void print_push_constant_resources(const Compiler &compiler, const vector fprintf(stderr, "==================\n\n"); for (auto &range : ranges) { - const auto &name = compiler.get_member_name(block.type_id, range.index); + const auto &name = compiler.get_member_name(block.base_type_id, range.index); fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index, !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(), diff --git a/spirv_cross.cpp b/spirv_cross.cpp index c3661578..5b77c851 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -422,56 +422,56 @@ ShaderResources Compiler::get_shader_resources() const if (var.storage == StorageClassInput) { if (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) - res.stage_inputs.push_back({ var.self, type.self, meta[type.self].decoration.alias }); + res.stage_inputs.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias }); else - res.stage_inputs.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.stage_inputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } // Subpass inputs else if (var.storage == StorageClassUniformConstant && type.image.dim == DimSubpassData) { - res.subpass_inputs.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.subpass_inputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } // Outputs else if (var.storage == StorageClassOutput) { if (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) - res.stage_outputs.push_back({ var.self, type.self, meta[type.self].decoration.alias }); + res.stage_outputs.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias }); else - res.stage_outputs.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.stage_outputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } // UBOs else if (type.storage == StorageClassUniform && (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock))) { - res.uniform_buffers.push_back({ var.self, type.self, meta[type.self].decoration.alias }); + res.uniform_buffers.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias }); } // SSBOs else if (type.storage == StorageClassUniform && (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock))) { - res.storage_buffers.push_back({ var.self, type.self, meta[type.self].decoration.alias }); + res.storage_buffers.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias }); } // Push constant blocks else if (type.storage == StorageClassPushConstant) { // There can only be one push constant block, but keep the vector in case this restriction is lifted // in the future. - res.push_constant_buffers.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.push_constant_buffers.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } // Images else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image) { - res.storage_images.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.storage_images.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } // Textures else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::SampledImage) { - res.sampled_images.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.sampled_images.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } // Atomic counters else if (type.storage == StorageClassAtomicCounter) { - res.atomic_counters.push_back({ var.self, type.self, meta[var.self].decoration.alias }); + res.atomic_counters.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } } diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 4c7dafc6..e1df27ee 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -36,15 +36,23 @@ struct Resource // This is the ID of the OpVariable. uint32_t id; - // The type of the declared resource. + // The type ID of the variable which includes arrays and all type modifications. + // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general + // since these modifications typically happen on the base_type_id. uint32_t type_id; + // The base type of the declared resource. + // This type is the base type which ignores pointers and arrays of the type_id. + // This is mostly useful to parse decorations of the underlying type. + // base_type_id can also be obtained with get_type(get_type(type_id).self). + uint32_t base_type_id; + // The declared name (OpName) of the resource. // For Buffer blocks, the name actually reflects the externally // visible Block name. // // This name can be retrieved again by using either - // get_name(id) or get_name(type_id) depending if it's a buffer block or not. + // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not. // // This name can be an empty string in which case get_fallback_name(id) can be // used which obtains a suitable fallback identifier for an ID. @@ -111,7 +119,7 @@ public: void unset_decoration(uint32_t id, spv::Decoration decoration); // Gets the SPIR-V associated with ID. - // Mostly used with Resource::type_id to parse the underlying type of a resource. + // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource. const SPIRType &get_type(uint32_t id) const; // Gets the underlying storage class for an OpVariable. From 1b4f7662d3c742c30acd7d769c2873ee4e2d410a Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 19 Jul 2016 09:22:54 +0200 Subject: [PATCH 10/11] Add support for OpImageQueryLod. --- .../shaders/desktop-only/frag/query-lod.frag | 12 ++++++++++++ shaders/desktop-only/frag/query-lod.frag | 10 ++++++++++ spirv_glsl.cpp | 15 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 reference/shaders/desktop-only/frag/query-lod.frag create mode 100644 shaders/desktop-only/frag/query-lod.frag diff --git a/reference/shaders/desktop-only/frag/query-lod.frag b/reference/shaders/desktop-only/frag/query-lod.frag new file mode 100644 index 00000000..f43543b8 --- /dev/null +++ b/reference/shaders/desktop-only/frag/query-lod.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTexCoord; + +void main() +{ + FragColor = textureQueryLod(uSampler, vTexCoord).xyxy; +} + diff --git a/shaders/desktop-only/frag/query-lod.frag b/shaders/desktop-only/frag/query-lod.frag new file mode 100644 index 00000000..0cb16040 --- /dev/null +++ b/shaders/desktop-only/frag/query-lod.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec2 vTexCoord; +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureQueryLod(uSampler, vTexCoord).xyxy; +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index fd929232..a0f7b7ba 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -3619,6 +3619,21 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; } + case OpImageQueryLod: + { + if (!options.es && options.version < 400) + { + require_extension("GL_ARB_texture_query_lod"); + // For some reason, the ARB spec is all-caps. + BFOP(textureQueryLOD); + } + else if (options.es) + throw CompilerError("textureQueryLod not supported in ES profile."); + else + BFOP(textureQueryLod); + break; + } + case OpImageQuerySamples: { auto *var = maybe_get_backing_variable(ops[2]); From 81d00da573907d49f4c00f89b733aa5628f3b8b2 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 19 Jul 2016 09:28:32 +0200 Subject: [PATCH 11/11] Implement OpImageQueryLevels. --- reference/shaders/desktop-only/frag/query-levels.frag | 11 +++++++++++ shaders/desktop-only/frag/query-levels.frag | 9 +++++++++ spirv_glsl.cpp | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 reference/shaders/desktop-only/frag/query-levels.frag create mode 100644 shaders/desktop-only/frag/query-levels.frag diff --git a/reference/shaders/desktop-only/frag/query-levels.frag b/reference/shaders/desktop-only/frag/query-levels.frag new file mode 100644 index 00000000..4a80cbf8 --- /dev/null +++ b/reference/shaders/desktop-only/frag/query-levels.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(textureQueryLevels(uSampler))); +} + diff --git a/shaders/desktop-only/frag/query-levels.frag b/shaders/desktop-only/frag/query-levels.frag new file mode 100644 index 00000000..3a697761 --- /dev/null +++ b/shaders/desktop-only/frag/query-levels.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(textureQueryLevels(uSampler)); +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index a0f7b7ba..21677c82 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -3634,6 +3634,16 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; } + case OpImageQueryLevels: + { + if (!options.es && options.version < 430) + require_extension("GL_ARB_texture_query_levels"); + if (options.es) + throw CompilerError("textureQueryLevels not supported in ES profile."); + UFOP(textureQueryLevels); + break; + } + case OpImageQuerySamples: { auto *var = maybe_get_backing_variable(ops[2]);