diff --git a/SPIRV/GLSL.ext.NV.h b/SPIRV/GLSL.ext.NV.h index 8e9298ca8..adfcb0f34 100644 --- a/SPIRV/GLSL.ext.NV.h +++ b/SPIRV/GLSL.ext.NV.h @@ -32,11 +32,18 @@ enum Decoration; enum Op; static const int GLSLextNVVersion = 100; -static const int GLSLextNVRevision = 1; +static const int GLSLextNVRevision = 2; //SPV_NV_sample_mask_override_coverage const char* const E_SPV_NV_sample_mask_override_coverage = "SPV_NV_sample_mask_override_coverage"; static const Decoration OverrideCoverageNV = static_cast(5248); + +//SPV_NV_geometry_shader_passthrough +const char* const E_SPV_NV_geometry_shader_passthrough = "SPV_NV_geometry_shader_passthrough"; + +static const Decoration PassthroughNV = static_cast(5250); + +static const Capability GeometryShaderPassthroughNV = static_cast(5251); #endif // #ifndef GLSLextNV_H \ No newline at end of file diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 71af11bc5..4b7204d99 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -4738,6 +4738,11 @@ spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol builder.addExtension(spv::E_SPV_NV_sample_mask_override_coverage); } } + if (symbol->getQualifier().layoutPassthrough) { + addDecoration(id, spv::PassthroughNV); + builder.addCapability(spv::GeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } #endif return id; diff --git a/SPIRV/disassemble.cpp b/SPIRV/disassemble.cpp index 092711884..cbc9a9b6c 100644 --- a/SPIRV/disassemble.cpp +++ b/SPIRV/disassemble.cpp @@ -481,7 +481,8 @@ void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, extInstSet = GLSLextAMDInst; #endif #ifdef NV_EXTENSIONS - } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0) { + }else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 || + strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0) { extInstSet = GLSLextNVInst; #endif } @@ -654,10 +655,13 @@ static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint #ifdef NV_EXTENSIONS static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint) { - if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0) { + if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 || + strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0) { switch (entrypoint) { - case OverrideCoverageNV: return "OverrideCoverageNV"; - default: return "Bad"; + case OverrideCoverageNV: return "OverrideCoverageNV"; + case PassthroughNV: return "PassthroughNV"; + case GeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + default: return "Bad"; } } return "Bad"; diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp index 878dcf594..8203c95c3 100755 --- a/SPIRV/doc.cpp +++ b/SPIRV/doc.cpp @@ -261,6 +261,7 @@ const char* DecorationString(int decoration) #endif #ifdef NV_EXTENSIONS case 5248: return "OverrideCoverageNV"; + case 5250: return "PassthroughNV"; #endif } } @@ -818,6 +819,11 @@ const char* CapabilityString(int info) case 4423: return "SubgroupBallotKHR"; case 4427: return "DrawParameters"; + +#ifdef NV_EXTENSIONS + case 5251: return "GeometryShaderPassthroughNV"; +#endif + } } diff --git a/Test/baseResults/spv.GeometryShaderPassthrough.geom.out b/Test/baseResults/spv.GeometryShaderPassthrough.geom.out new file mode 100644 index 000000000..05aeb9710 --- /dev/null +++ b/Test/baseResults/spv.GeometryShaderPassthrough.geom.out @@ -0,0 +1,46 @@ +spv.GeometryShaderPassthrough.geom +Warning, version 450 is not yet complete; most version-specific features are present, but some are missing. + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 15 + + Capability Geometry + Capability GeometryShaderPassthroughNV + Extension "SPV_NV_geometry_shader_passthrough" + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Geometry 4 "main" 10 14 + ExecutionMode 4 Triangles + ExecutionMode 4 Invocations 1 + ExecutionMode 4 OutputVertices + Source GLSL 450 + SourceExtension "GL_NV_geometry_shader_passthrough" + Name 4 "main" + Name 8 "gl_PerVertex" + MemberName 8(gl_PerVertex) 0 "gl_Position" + Name 10 "" + Name 12 "Inputs" + MemberName 12(Inputs) 0 "texcoord" + MemberName 12(Inputs) 1 "baseColor" + Name 14 "" + MemberDecorate 8(gl_PerVertex) 0 BuiltIn Position + Decorate 8(gl_PerVertex) Block + Decorate 10 PassthroughNV + Decorate 12(Inputs) Block + Decorate 14 PassthroughNV + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8(gl_PerVertex): TypeStruct 7(fvec4) + 9: TypePointer Input 8(gl_PerVertex) + 10: 9(ptr) Variable Input + 11: TypeVector 6(float) 2 + 12(Inputs): TypeStruct 11(fvec2) 7(fvec4) + 13: TypePointer Input 12(Inputs) + 14: 13(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + Return + FunctionEnd diff --git a/Test/spv.GeometryShaderPassthrough.geom b/Test/spv.GeometryShaderPassthrough.geom new file mode 100644 index 000000000..9e6fe4cec --- /dev/null +++ b/Test/spv.GeometryShaderPassthrough.geom @@ -0,0 +1,17 @@ +#version 450 +#extension GL_NV_geometry_shader_passthrough : require + +layout(triangles) in; + +layout(passthrough) in gl_PerVertex { + vec4 gl_Position; +}; + +layout(passthrough) in Inputs { +vec2 texcoord; +vec4 baseColor; +}; + +void main() +{ +} \ No newline at end of file diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index fc562c328..ae40465b9 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -599,6 +599,9 @@ public: layoutFormat = ElfNone; layoutPushConstant = false; +#ifdef NV_EXTENSIONS + layoutPassthrough = false; +#endif } bool hasLayout() const { @@ -652,6 +655,10 @@ public: bool layoutPushConstant; +#ifdef NV_EXTENSIONS + bool layoutPassthrough; +#endif + bool hasUniformLayout() const { return hasMatrix() || @@ -1537,6 +1544,12 @@ public: if (qualifier.layoutPushConstant) p += snprintf(p, end - p, "push_constant "); +#ifdef NV_EXTENSIONS + if (qualifier.layoutPassthrough) + p += snprintf(p, end - p, "passthrough "); +#endif + + p += snprintf(p, end - p, ") "); } } diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 7a8cbfe2c..67bb883cc 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -604,7 +604,11 @@ void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { if (! type.isArray() && ! symbolTable.atBuiltInLevel()) { - if (type.getQualifier().isArrayedIo(language)) + if (type.getQualifier().isArrayedIo(language) +#ifdef NV_EXTENSIONS + && !type.getQualifier().layoutPassthrough +#endif + ) error(loc, "type must be an array:", type.getStorageQualifierString(), identifier.c_str()); } } @@ -3459,6 +3463,20 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT // - remove unused members // - ensure remaining qualifiers/types match TType& type = block->getWritableType(); + +#ifdef NV_EXTENSIONS + // if gl_PerVertex is redeclared for the purpose of passing through "gl_Position" + // for passthrough purpose, the redclared block should have the same qualifers as + // the current one + if (currentBlockQualifier.layoutPassthrough) + { + type.getQualifier().layoutPassthrough = currentBlockQualifier.layoutPassthrough; + type.getQualifier().storage = currentBlockQualifier.storage; + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } +#endif + TTypeList::iterator member = type.getWritableStruct()->begin(); size_t numOriginalMembersFound = 0; while (member != type.getStruct()->end()) { @@ -3928,6 +3946,14 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi publicType.shaderQualifiers.geometry = ElgTriangleStrip; return; } +#ifdef NV_EXTENSIONS + if (id == "passthrough") { + requireExtensions(loc, 1, &E_SPV_NV_geometry_shader_passthrough, "geometry shader passthrough"); + publicType.qualifier.layoutPassthrough = true; + intermediate.setGeoPassthroughEXT(); + return; + } +#endif } else { assert(language == EShLangTessEvaluation); @@ -4328,6 +4354,11 @@ void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifie if (src.layoutPushConstant) dst.layoutPushConstant = true; + +#ifdef NV_EXTENSIONS + if (src.layoutPassthrough) + dst.layoutPassthrough = true; +#endif } } diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 5f3fdb230..afedb4f71 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -197,6 +197,7 @@ void TParseVersions::initializeExtensionBehavior() #ifdef NV_EXTENSIONS extensionBehavior[E_GL_NV_sample_mask_override_coverage] = EBhDisable; + extensionBehavior[E_SPV_NV_geometry_shader_passthrough] = EBhDisable; #endif // AEP @@ -309,6 +310,7 @@ void TParseVersions::getPreamble(std::string& preamble) #ifdef NV_EXTENSIONS "#define GL_NV_sample_mask_override_coverage 1\n" + "#define GL_NV_geometry_shader_passthrough 1\n" #endif ; } diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index 433cee573..fe9764ffe 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -144,6 +144,7 @@ const char* const E_GL_AMD_gpu_shader_half_float = "GL_AMD_gpu_sh #endif #ifdef NV_EXTENSIONS const char* const E_GL_NV_sample_mask_override_coverage = "GL_NV_sample_mask_override_coverage"; +const char* const E_SPV_NV_geometry_shader_passthrough = "GL_NV_geometry_shader_passthrough"; #endif // AEP diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp index 3834fde32..b8ce6d7e8 100644 --- a/glslang/MachineIndependent/linkValidate.cpp +++ b/glslang/MachineIndependent/linkValidate.cpp @@ -469,9 +469,17 @@ void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled) case EShLangGeometry: if (inputPrimitive == ElgNone) error(infoSink, "At least one shader must specify an input layout primitive"); - if (outputPrimitive == ElgNone) + if (outputPrimitive == ElgNone +#ifdef NV_EXTENSIONS + && !getGeoPassthroughEXT() +#endif + ) error(infoSink, "At least one shader must specify an output layout primitive"); - if (vertices == TQualifier::layoutNotSet) + if (vertices == TQualifier::layoutNotSet +#ifdef NV_EXTENSIONS + && !getGeoPassthroughEXT() +#endif + ) error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); break; case EShLangFragment: diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 8921456fc..34875c2be 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -153,6 +153,7 @@ public: flattenUniformArrays(false), #ifdef NV_EXTENSIONS layoutOverrideCoverage(false), + geoPassthroughEXT(false), #endif useUnknownFormat(false) { @@ -393,6 +394,8 @@ public: #ifdef NV_EXTENSIONS void setLayoutOverrideCoverage() { layoutOverrideCoverage = true; } bool getLayoutOverrideCoverage() const { return layoutOverrideCoverage; } + void setGeoPassthroughEXT() { geoPassthroughEXT = true; } + bool getGeoPassthroughEXT() const { return geoPassthroughEXT; } #endif protected: @@ -457,6 +460,7 @@ protected: #ifdef NV_EXTENSIONS bool layoutOverrideCoverage; + bool geoPassthroughEXT; #endif typedef std::list TGraph; diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index 8593c62d3..c946ef7ee 100644 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -216,6 +216,7 @@ INSTANTIATE_TEST_CASE_P( "spv.forwardFun.frag", "spv.functionCall.frag", "spv.functionSemantics.frag", + "spv.GeometryShaderPassthrough.geom", "spv.interpOps.frag", "spv.int64.frag", "spv.layoutNested.vert",