diff --git a/reference/opt/shaders/geom/transform-feedback-streams.geom b/reference/opt/shaders/geom/transform-feedback-streams.geom new file mode 100644 index 00000000..4d238b4a --- /dev/null +++ b/reference/opt/shaders/geom/transform-feedback-streams.geom @@ -0,0 +1,26 @@ +#version 450 +layout(points) in; +layout(max_vertices = 2, points) out; + +layout(xfb_buffer = 1, xfb_stride = 20, stream = 1) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(location = 0, xfb_buffer = 2, xfb_stride = 32, xfb_offset = 16, stream = 1) out vec4 vFoo; +layout(xfb_buffer = 3, xfb_stride = 16, stream = 2) out VertOut +{ + layout(location = 1, xfb_offset = 0) vec4 vBar; +} _23; + + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + EmitStreamVertex(1); + _23.vBar = vec4(5.0); + EmitStreamVertex(2); +} + diff --git a/reference/shaders/geom/transform-feedback-streams.geom b/reference/shaders/geom/transform-feedback-streams.geom new file mode 100644 index 00000000..4d238b4a --- /dev/null +++ b/reference/shaders/geom/transform-feedback-streams.geom @@ -0,0 +1,26 @@ +#version 450 +layout(points) in; +layout(max_vertices = 2, points) out; + +layout(xfb_buffer = 1, xfb_stride = 20, stream = 1) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(location = 0, xfb_buffer = 2, xfb_stride = 32, xfb_offset = 16, stream = 1) out vec4 vFoo; +layout(xfb_buffer = 3, xfb_stride = 16, stream = 2) out VertOut +{ + layout(location = 1, xfb_offset = 0) vec4 vBar; +} _23; + + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + EmitStreamVertex(1); + _23.vBar = vec4(5.0); + EmitStreamVertex(2); +} + diff --git a/shaders/geom/transform-feedback-streams.geom b/shaders/geom/transform-feedback-streams.geom new file mode 100644 index 00000000..1e628907 --- /dev/null +++ b/shaders/geom/transform-feedback-streams.geom @@ -0,0 +1,24 @@ +#version 450 +layout(max_vertices = 2, points) out; +layout(points) in; +layout(stream = 1, xfb_stride = 32, xfb_offset = 16, xfb_buffer = 2, location = 0) out vec4 vFoo; + +layout(stream = 1, xfb_buffer = 1, xfb_stride = 20) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(stream = 2, xfb_buffer = 3) out VertOut +{ + layout(xfb_stride = 16, xfb_offset = 0, location = 1) vec4 vBar; +}; + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + EmitStreamVertex(1); + vBar = vec4(5.0); + EmitStreamVertex(2); +} diff --git a/spirv_common.hpp b/spirv_common.hpp index 6ff6fa9a..53df3761 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -1634,6 +1634,7 @@ struct Meta uint32_t offset = 0; uint32_t xfb_buffer = 0; uint32_t xfb_stride = 0; + uint32_t stream = 0; uint32_t array_stride = 0; uint32_t matrix_stride = 0; uint32_t input_attachment = 0; diff --git a/spirv_cross_parsed_ir.cpp b/spirv_cross_parsed_ir.cpp index d7b82fbf..f409d65f 100644 --- a/spirv_cross_parsed_ir.cpp +++ b/spirv_cross_parsed_ir.cpp @@ -388,6 +388,10 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument) dec.xfb_stride = argument; break; + case DecorationStream: + dec.stream = argument; + break; + case DecorationArrayStride: dec.array_stride = argument; break; @@ -467,6 +471,10 @@ void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decor dec.xfb_stride = argument; break; + case DecorationStream: + dec.stream = argument; + break; + case DecorationSpecId: dec.spec_id = argument; break; @@ -584,6 +592,8 @@ uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const return dec.xfb_buffer; case DecorationXfbStride: return dec.xfb_stride; + case DecorationStream: + return dec.stream; case DecorationBinding: return dec.binding; case DecorationDescriptorSet: @@ -656,6 +666,10 @@ void ParsedIR::unset_decoration(ID id, Decoration decoration) dec.xfb_stride = 0; break; + case DecorationStream: + dec.stream = 0; + break; + case DecorationBinding: dec.binding = 0; break; @@ -730,6 +744,8 @@ uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration d return dec.xfb_buffer; case DecorationXfbStride: return dec.xfb_stride; + case DecorationStream: + return dec.stream; case DecorationSpecId: return dec.spec_id; case DecorationIndex: @@ -826,6 +842,10 @@ void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration dec dec.xfb_stride = 0; break; + case DecorationStream: + dec.stream = 0; + break; + case DecorationSpecId: dec.spec_id = 0; break; diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 260b6c6b..c4831b80 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -1590,7 +1590,8 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) uint32_t member_count = uint32_t(type.member_types.size()); bool have_xfb_buffer_stride = false; bool have_any_xfb_offset = false; - uint32_t xfb_stride = 0, xfb_buffer = 0; + bool have_geom_stream = false; + uint32_t xfb_stride = 0, xfb_buffer = 0, geom_stream = 0; if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride)) { @@ -1599,9 +1600,24 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) xfb_stride = get_decoration(var.self, DecorationXfbStride); } + if (flags.get(DecorationStream)) + { + have_geom_stream = true; + geom_stream = get_decoration(var.self, DecorationStream); + } + // Verify that none of the members violate our assumption. for (uint32_t i = 0; i < member_count; i++) { + if (has_member_decoration(type.self, i, DecorationStream)) + { + uint32_t member_geom_stream = get_member_decoration(type.self, i, DecorationStream); + if (have_geom_stream && member_geom_stream != geom_stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = member_geom_stream; + } + // Only members with an Offset decoration participate in XFB. if (!has_member_decoration(type.self, i, DecorationOffset)) continue; @@ -1632,15 +1648,40 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var) attr.push_back(join("xfb_stride = ", xfb_stride)); uses_enhanced_layouts = true; } + + if (have_geom_stream) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", get_decoration(var.self, DecorationStream))); + } } - else if (var.storage == StorageClassOutput && flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride) && - flags.get(DecorationOffset)) + else if (var.storage == StorageClassOutput) { - // XFB for standalone variables, we can emit all decorations. - attr.push_back(join("xfb_buffer = ", get_decoration(var.self, DecorationXfbBuffer))); - attr.push_back(join("xfb_stride = ", get_decoration(var.self, DecorationXfbStride))); - attr.push_back(join("xfb_offset = ", get_decoration(var.self, DecorationOffset))); - uses_enhanced_layouts = true; + if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride) && + flags.get(DecorationOffset)) + { + // XFB for standalone variables, we can emit all decorations. + attr.push_back(join("xfb_buffer = ", get_decoration(var.self, DecorationXfbBuffer))); + attr.push_back(join("xfb_stride = ", get_decoration(var.self, DecorationXfbStride))); + attr.push_back(join("xfb_offset = ", get_decoration(var.self, DecorationOffset))); + uses_enhanced_layouts = true; + } + + if (flags.get(DecorationStream)) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", get_decoration(var.self, DecorationStream))); + } } // Can only declare Component if we can declare location. @@ -2700,8 +2741,9 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo uint32_t clip_distance_size = 0; bool have_xfb_buffer_stride = false; + bool have_geom_stream = false; bool have_any_xfb_offset = false; - uint32_t xfb_stride = 0, xfb_buffer = 0; + uint32_t xfb_stride = 0, xfb_buffer = 0, geom_stream = 0; std::unordered_map builtin_xfb_offsets; ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { @@ -2718,15 +2760,24 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo { builtins.set(m.builtin_type); if (m.builtin_type == BuiltInCullDistance) - cull_distance_size = this->get(type.member_types[index]).array.front(); + cull_distance_size = to_array_size_literal(this->get(type.member_types[index])); else if (m.builtin_type == BuiltInClipDistance) - clip_distance_size = this->get(type.member_types[index]).array.front(); + clip_distance_size = to_array_size_literal(this->get(type.member_types[index])); if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationOffset)) { have_any_xfb_offset = true; builtin_xfb_offsets[m.builtin_type] = m.offset; } + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationStream)) + { + uint32_t stream = m.stream; + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } } index++; } @@ -2744,6 +2795,15 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo xfb_buffer = buffer_index; xfb_stride = stride; } + + if (storage == StorageClassOutput && has_decoration(var.self, DecorationStream)) + { + uint32_t stream = get_decoration(var.self, DecorationStream); + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } } else if (var.storage == storage && !block && is_builtin_variable(var)) { @@ -2753,9 +2813,9 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo { global_builtins.set(m.builtin_type); if (m.builtin_type == BuiltInCullDistance) - cull_distance_size = type.array.front(); + cull_distance_size = to_array_size_literal(type); else if (m.builtin_type == BuiltInClipDistance) - clip_distance_size = type.array.front(); + clip_distance_size = to_array_size_literal(type); if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationXfbStride) && m.decoration_flags.get(DecorationXfbBuffer) && m.decoration_flags.get(DecorationOffset)) @@ -2772,6 +2832,15 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo xfb_buffer = buffer_index; xfb_stride = stride; } + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationStream)) + { + uint32_t stream = get_decoration(var.self, DecorationStream); + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } } } @@ -2801,9 +2870,9 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo if (storage == StorageClassOutput) { + SmallVector attr; if (have_xfb_buffer_stride && have_any_xfb_offset) { - statement("layout(xfb_buffer = ", xfb_buffer, ", xfb_stride = ", xfb_stride, ") out gl_PerVertex"); if (!options.es) { if (options.version < 440 && options.version >= 140) @@ -2815,7 +2884,22 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo } else if (options.es) SPIRV_CROSS_THROW("Need GL_ARB_enhanced_layouts for xfb_stride or xfb_buffer."); + attr.push_back(join("xfb_buffer = ", xfb_buffer, ", xfb_stride = ", xfb_stride)); } + + if (have_geom_stream) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", geom_stream)); + } + + if (!attr.empty()) + statement("layout(", merge(attr), ") out gl_PerVertex"); else statement("out gl_PerVertex"); }