Add support for primitive culling layout qualifier. (#2220)

* Add support for primitive culling layout qualifier.

* Add error checks for primitive flags and negative test.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 3bed678..e41bb5d 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -1448,7 +1448,7 @@
         builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb);
     }
 
-    if (sourceExtensions.find("GL_EXT_ray_flags_primitive_culling") != sourceExtensions.end()) {
+    if (glslangIntermediate->getLayoutPrimitiveCulling()) {
         builder.addCapability(spv::CapabilityRayTraversalPrimitiveCullingProvisionalKHR);
     }
 
diff --git a/Test/baseResults/spv.ext.RayPrimCull_Errors.rgen.out b/Test/baseResults/spv.ext.RayPrimCull_Errors.rgen.out
new file mode 100644
index 0000000..23d1acf
--- /dev/null
+++ b/Test/baseResults/spv.ext.RayPrimCull_Errors.rgen.out
@@ -0,0 +1,9 @@
+spv.ext.RayPrimCull_Errors.rgen
+ERROR: 0:3: 'primitive culling' : required extension not requested: GL_EXT_ray_flags_primitive_culling
+ERROR: 0:5: 'primitive_culling' : layout qualifier can not have storage qualifiers 
+ERROR: 0:6: 'primitive_culling' : can only be applied as standalone 
+ERROR: 0:7: 'primitive_culling' : can only be applied as standalone 
+ERROR: 4 compilation errors.  No code generated.
+
+
+SPIR-V is not generated for failed compile or link
diff --git a/Test/rayQuery-allOps.comp b/Test/rayQuery-allOps.comp
index 80f2593..3a2a8d0 100644
--- a/Test/rayQuery-allOps.comp
+++ b/Test/rayQuery-allOps.comp
@@ -2,6 +2,7 @@
 #extension GL_EXT_ray_query : enable
 #extension GL_EXT_ray_flags_primitive_culling : enable
 
+layout(primitive_culling);
 struct Ray
 {
     vec3 pos;
diff --git a/Test/spv.ext.RayGenShader.rgen b/Test/spv.ext.RayGenShader.rgen
index c92772e..27d450f 100644
--- a/Test/spv.ext.RayGenShader.rgen
+++ b/Test/spv.ext.RayGenShader.rgen
@@ -11,6 +11,7 @@
 	vec3 origin;
 
 };
+layout(primitive_culling);
 void main()
 {
     uint lx = gl_LaunchIDEXT.x;
diff --git a/Test/spv.ext.RayPrimCull_Errors.rgen b/Test/spv.ext.RayPrimCull_Errors.rgen
new file mode 100644
index 0000000..0fceccf
--- /dev/null
+++ b/Test/spv.ext.RayPrimCull_Errors.rgen
@@ -0,0 +1,10 @@
+#version 460
+#extension GL_EXT_ray_tracing : enable
+layout(primitive_culling);
+#extension GL_EXT_ray_flags_primitive_culling : enable
+layout(primitive_culling) uniform;
+layout(primitive_culling, binding = 2) uniform accelerationStructureEXT as;
+layout(std140, binding = 2, primitive_culling) buffer block { int x; };
+void main()
+{
+}
diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h
index b2c416d..235ea3f 100644
--- a/glslang/Include/Types.h
+++ b/glslang/Include/Types.h
@@ -1235,6 +1235,7 @@
     bool layoutDerivativeGroupQuads;    // true if layout derivative_group_quadsNV set
     bool layoutDerivativeGroupLinear;   // true if layout derivative_group_linearNV set
     int primitives;                     // mesh shader "max_primitives"DerivativeGroupLinear;   // true if layout derivative_group_linearNV set
+    bool layoutPrimitiveCulling;        // true if layout primitive_culling set
     TLayoutDepth getDepth() const { return layoutDepth; }
 #else
     TLayoutDepth getDepth() const { return EldNone; }
@@ -1268,6 +1269,7 @@
         layoutOverrideCoverage      = false;
         layoutDerivativeGroupQuads  = false;
         layoutDerivativeGroupLinear = false;
+        layoutPrimitiveCulling      = false;
         primitives                  = TQualifier::layoutNotSet;
         interlockOrdering = EioNone;
 #endif
@@ -1331,6 +1333,8 @@
             primitives = src.primitives;
         if (src.interlockOrdering != EioNone)
             interlockOrdering = src.interlockOrdering;
+        if (src.layoutPrimitiveCulling)
+            layoutPrimitiveCulling = src.layoutPrimitiveCulling;
 #endif
     }
 };
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 39027d3..a35d41a 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -5173,6 +5173,12 @@
             }
         }
     }
+
+    if (id == "primitive_culling") {
+        requireExtensions(loc, 1, &E_GL_EXT_ray_flags_primitive_culling, "primitive culling");
+        publicType.shaderQualifiers.layoutPrimitiveCulling = true;
+        return;
+    }
 #endif
 
     error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
@@ -6104,6 +6110,8 @@
         error(loc, message, "num_views", "");
     if (shaderQualifiers.interlockOrdering != EioNone)
         error(loc, message, TQualifier::getInterlockOrderingString(shaderQualifiers.interlockOrdering), "");
+    if (shaderQualifiers.layoutPrimitiveCulling)
+        error(loc, "can only be applied as standalone", "primitive_culling", "");
 #endif
 }
 
@@ -8368,6 +8376,16 @@
     {
         checkIoArraysConsistency(loc);
     }
+
+    if (publicType.shaderQualifiers.layoutPrimitiveCulling) {
+        if (publicType.qualifier.storage != EvqTemporary)
+            error(loc, "layout qualifier can not have storage qualifiers", "primitive_culling","", "");
+        else {
+            intermediate.setLayoutPrimitiveCulling();
+        }
+        // Exit early as further checks are not valid
+        return;
+    }
 #endif 
     const TQualifier& qualifier = publicType.qualifier;
 
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 996e347..9f7b997 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -276,7 +276,8 @@
         needToLegalize(false),
         binaryDoubleOutput(false),
         usePhysicalStorageBuffer(false),
-        uniformLocationBase(0)
+        uniformLocationBase(0),
+        layoutPrimitiveCulling(false)
 #endif
     {
         localSize[0] = 1;
@@ -742,6 +743,8 @@
     void setLayoutDerivativeMode(ComputeDerivativeMode mode) { computeDerivativeMode = mode; }
     bool hasLayoutDerivativeModeNone() const { return computeDerivativeMode != LayoutDerivativeNone; }
     ComputeDerivativeMode getLayoutDerivativeModeNone() const { return computeDerivativeMode; }
+    void setLayoutPrimitiveCulling() { layoutPrimitiveCulling = true; }
+    bool getLayoutPrimitiveCulling() const { return layoutPrimitiveCulling; }
     bool setPrimitives(int m)
     {
         if (primitives != TQualifier::layoutNotSet)
@@ -974,6 +977,7 @@
     ComputeDerivativeMode computeDerivativeMode;
     int primitives;
     int numTaskNVBlocks;
+    bool layoutPrimitiveCulling;
 
     // Base shift values
     std::array<unsigned int, EResCount> shiftBinding;
diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp
index 1d061fb..62941a4 100644
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -337,6 +337,7 @@
         "spv.ext.IntersectShader_Errors.rint",
         "spv.ext.MissShader.rmiss",
         "spv.ext.MissShader_Errors.rmiss",
+        "spv.ext.RayPrimCull_Errors.rgen",
         "spv.ext.RayCallable.rcall",
         "spv.ext.RayCallable_Errors.rcall",
         "spv.ext.RayConstants.rgen",