layers: Add check for pointsize built-in

When creating a pipeline using TOPOLOGY_POINT_LIST, the vertex
shader MUST contain the PointSize built-in specifying the size.
Added a validation check for this case.

Change-Id: I5ab6eec2d44bb3c281826233fb511224b0ddf923
diff --git a/layers/shader_validation.cpp b/layers/shader_validation.cpp
index e5baa7a..3100127 100644
--- a/layers/shader_validation.cpp
+++ b/layers/shader_validation.cpp
@@ -1618,6 +1618,74 @@
     return skip;
 }
 
+// Return true if the specified built-in is present in the supplied shader source
+bool FindBuiltIn(shader_module const *src, spv::BuiltIn target_built_in) {
+    for (auto insn : *src) {
+        if (insn.opcode() == spv::OpDecorate) {
+            if (insn.word(2) == spv::DecorationBuiltIn) {
+                if (insn.word(3) == target_built_in) {
+                    return true;
+                }
+            }
+        } else if (insn.opcode() == spv::OpMemberDecorate) {
+            if (insn.word(3) == spv::DecorationBuiltIn) {
+                if (insn.word(4) == target_built_in) {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+bool ValidatePointListShaderState(const layer_data *dev_data, const PIPELINE_STATE *pipeline, shader_module const *shaders[5]) {
+    //    o If you only have a vertex shader : you must write gl_PointSize in the shader when using points
+    //    o If you have a geometry or tessellation shader:
+    //        - If shaderTessellationAndGeometryPointSize feature enabled:
+    //            * you must write gl_PointSize in the last geometry shader stage
+    //        - If shaderTessellationAndGeometryPointSize feature disabled:
+    //            * you must not write gl_PointSize and you get a default of 1.0.
+    bool skip = false;
+    if (pipeline->graphicsPipelineCI.pInputAssemblyState &&
+        pipeline->graphicsPipelineCI.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST) {
+        auto report_data = GetReportData(dev_data);
+        bool pointsize_found = false;
+        std::string shader_name;
+        int vertex_stage = GetShaderStageId(VK_SHADER_STAGE_VERTEX_BIT);
+        int geom_stage = GetShaderStageId(VK_SHADER_STAGE_GEOMETRY_BIT);
+        int tess_eval_stage = GetShaderStageId(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
+
+        if (shaders[geom_stage]) {
+            pointsize_found = FindBuiltIn(shaders[geom_stage], spv::BuiltInPointSize);
+            shader_name = "geometry";
+        } else if (shaders[tess_eval_stage]) {
+            pointsize_found = FindBuiltIn(shaders[tess_eval_stage], spv::BuiltInPointSize);
+            shader_name = "tessellation evaluation";
+        } else if (shaders[vertex_stage]) {
+            pointsize_found = FindBuiltIn(shaders[vertex_stage], spv::BuiltInPointSize);
+            shader_name = "vertex";
+        }
+
+        if ((shaders[tess_eval_stage] || shaders[geom_stage]) &&
+            !GetEnabledFeatures(dev_data)->core.shaderTessellationAndGeometryPointSize) {
+            if (pointsize_found) {
+                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
+                                HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_PointSizeBuiltInOverSpecified,
+                                "Pipeline InputAssemblyState topology is set to POINT_LIST and geometry or tessellation "
+                                "shaders specify PointSize which is prohibited when the shaderTessellationAndGeometryPointSize "
+                                "feature is not enabled");
+            }
+        } else if (!pointsize_found) {
+            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
+                            HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_MissingPointSizeBuiltIn,
+                            "Pipeline InputAssemblyState topology is set to POINT_LIST, but PointSize is not specified in the "
+                            "%s shader.",
+                            shader_name.c_str());
+        }
+    }
+    return skip;
+}
+
 // Validate that the shaders used by the given pipeline and store the active_slots
 //  that are actually used by the pipeline into pPipeline->active_slots
 bool ValidateAndCapturePipelineShaderState(layer_data *dev_data, PIPELINE_STATE *pipeline) {
@@ -1677,6 +1745,9 @@
                                                    pCreateInfo->subpass);
     }
 
+    // If pipeline calls for POINT_LIST, verify that PointSize correctly specified (or not) by shaders
+    skip |= ValidatePointListShaderState(dev_data, pipeline, shaders);
+
     return skip;
 }