layers: Validate maxComputeWorkGroupInvocations

Validate maxComputeWorkGroupInvocations in vkCmdDispatch.

Change-Id: I2ee10bf1d74f1454cea9ba021f4487b25ff4f1b2
diff --git a/layers/shader_validation.cpp b/layers/shader_validation.cpp
index 45f743f..2180f64 100644
--- a/layers/shader_validation.cpp
+++ b/layers/shader_validation.cpp
@@ -2971,3 +2971,67 @@
     }
     return skip;
 }
+
+bool CoreChecks::ValidateComputeWorkGroupInvocations(GLOBAL_CB_NODE *cb_state, uint32_t groupCountX, uint32_t groupCountY,
+                                                     uint32_t groupCountZ) {
+    auto const &state = cb_state->lastBound[VK_PIPELINE_BIND_POINT_COMPUTE];
+    PIPELINE_STATE *pPipe = state.pipeline_state;
+    if (!pPipe) return false;
+    auto pCreateInfo = pPipe->computePipelineCI.ptr();
+    if (!pCreateInfo) return false;
+
+    unordered_map<VkShaderModule, std::unique_ptr<shader_module>>::iterator it = shaderModuleMap.find(pCreateInfo->stage.module);
+    if (it != shaderModuleMap.end()) {
+        uint32_t local_size_x = 0;
+        uint32_t local_size_y = 0;
+        uint32_t local_size_z = 0;
+        if (FindLocalSize(&(*it->second), local_size_x, local_size_y, local_size_z)) {
+            uint32_t limit = phys_dev_props.limits.maxComputeWorkGroupInvocations;
+            uint64_t invocations = local_size_x * local_size_y;
+            // Prevent overflow.
+            bool overflow = false;
+            if (invocations > UINT32_MAX) {
+                overflow = true;
+            }
+            if (!overflow) {
+                invocations *= local_size_z;
+                if (invocations > UINT32_MAX) {
+                    overflow = true;
+                }
+            }
+            if (!overflow) {
+                invocations *= groupCountX;
+                if (invocations > UINT32_MAX) {
+                    overflow = true;
+                }
+            }
+            if (!overflow) {
+                invocations *= groupCountY;
+                if (invocations > UINT32_MAX) {
+                    overflow = true;
+                }
+            }
+            if (!overflow) {
+                invocations *= groupCountZ;
+                if (invocations > UINT32_MAX) {
+                    overflow = true;
+                }
+            }
+            if (overflow) {
+                return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
+                               HandleToUint64(it->first), "UNASSIGNED-features-limits-maxComputeWorkGroupInvocations",
+                               "ShaderMdoule %s invocations (>%" PRIu32
+                               ") exceeds device limit maxComputeWorkGroupInvocations (%" PRIu32 ").",
+                               report_data->FormatHandle(it->first).c_str(), UINT32_MAX, limit);
+            }
+            if (invocations > limit) {
+                return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
+                               HandleToUint64(it->first), "UNASSIGNED-features-limits-maxComputeWorkGroupInvocations",
+                               "ShaderMdoule %s invocations (%" PRIu64
+                               ") exceeds device limit maxComputeWorkGroupInvocations (%" PRIu32 ").",
+                               report_data->FormatHandle(it->first).c_str(), invocations, limit);
+            }
+        }
+    }
+    return false;
+}