layers: Add DrawState check for correct dynamicOffsetCount

At vkCmdBindDescriptorSets() time, the dynamicOffsetCount must match the actual count of dynamic descriptors that are being bound. If not, validation flags an error of type DRAWSTATE_INVALID_DYNAMIC_OFFSET_COUNT.
diff --git a/layers/draw_state.cpp b/layers/draw_state.cpp
index 8c10e04..cc6b509 100644
--- a/layers/draw_state.cpp
+++ b/layers/draw_state.cpp
@@ -2151,10 +2151,16 @@
             pNewNode->stageFlags.resize(totalCount);
             uint32_t offset = 0;
             uint32_t j = 0;
+            VkDescriptorType dType;
             for (uint32_t i=0; i<pCreateInfo->bindingCount; i++) {
+                dType = pCreateInfo->pBinding[i].descriptorType;
                 for (j = 0; j < pCreateInfo->pBinding[i].arraySize; j++) {
-                    pNewNode->descriptorTypes[offset + j] = pCreateInfo->pBinding[i].descriptorType;
+                    pNewNode->descriptorTypes[offset + j] = dType;
                     pNewNode->stageFlags[offset + j] = pCreateInfo->pBinding[i].stageFlags;
+                    if ((dType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) ||
+                        (dType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)) {
+                        pNewNode->dynamicDescriptorCount++;
+                    }
                 }
                 offset += j;
             }
@@ -2689,12 +2695,6 @@
     VkBool32 skipCall = VK_FALSE;
     layer_data* dev_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
     GLOBAL_CB_NODE* pCB = getCBNode(dev_data, commandBuffer);
-    // TODO : Validate dynamic offsets
-    //  If any of the sets being bound include dynamic uniform or storage buffers,
-    //  then pDynamicOffsets must include one element for each array element
-    //  in each dynamic descriptor type binding in each set.
-    //  dynamicOffsetCount is the total number of dynamic offsets provided, and
-    //  must equal the total number of dynamic descriptors in the sets being bound
     if (pCB) {
         if (pCB->state == CB_UPDATE_ACTIVE) {
             if ((VK_PIPELINE_BIND_POINT_COMPUTE == pipelineBindPoint) && (pCB->activeRenderPass)) {
@@ -2704,6 +2704,8 @@
                 skipCall |= log_msg(dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, (VkDbgObjectType) 0, 0, 0, DRAWSTATE_NO_ACTIVE_RENDERPASS, "DS",
                         "Incorrectly binding graphics DescriptorSets without an active RenderPass");
             } else {
+                // Track total count of dynamic descriptor types to make sure we have an offset for each one
+                uint32_t totalDynamicDescriptors = 0;
                 for (uint32_t i=0; i<setCount; i++) {
                     SET_NODE* pSet = getSetNode(dev_data, pDescriptorSets[i]);
                     if (pSet) {
@@ -2714,9 +2716,11 @@
                         loader_platform_thread_unlock_mutex(&globalLock);
                         skipCall |= log_msg(dev_data->report_data, VK_DBG_REPORT_INFO_BIT, VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t) pDescriptorSets[i], 0, DRAWSTATE_NONE, "DS",
                                 "DS %#" PRIxLEAST64 " bound on pipeline %s", (uint64_t) pDescriptorSets[i], string_VkPipelineBindPoint(pipelineBindPoint));
-                        if (!pSet->pUpdateStructs)
+                        if (!pSet->pUpdateStructs) {
                             skipCall |= log_msg(dev_data->report_data, VK_DBG_REPORT_WARN_BIT, VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t) pDescriptorSets[i], 0, DRAWSTATE_DESCRIPTOR_SET_NOT_UPDATED, "DS",
                                     "DS %#" PRIxLEAST64 " bound but it was never updated. You may want to either update it or not bind it.", (uint64_t) pDescriptorSets[i]);
+                        }
+                        totalDynamicDescriptors += pSet->pLayout->dynamicDescriptorCount;
                     } else {
                         skipCall |= log_msg(dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t) pDescriptorSets[i], 0, DRAWSTATE_INVALID_SET, "DS",
                                 "Attempt to bind DS %#" PRIxLEAST64 " that doesn't exist!", (uint64_t) pDescriptorSets[i]);
@@ -2724,6 +2728,11 @@
                 }
                 updateCBTracking(pCB);
                 skipCall |= addCmd(dev_data, pCB, CMD_BINDDESCRIPTORSETS);
+                //  dynamicOffsetCount must equal the total number of dynamic descriptors in the sets being bound
+                if (totalDynamicDescriptors != dynamicOffsetCount) {
+                    skipCall |= log_msg(dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t) commandBuffer, 0, DRAWSTATE_INVALID_DYNAMIC_OFFSET_COUNT, "DS",
+                            "Attempting to bind %u descriptorSets with %u dynamic descriptors, but dynamicOffsetCount is %u. It should exactly match the number of dynamic descriptors.", setCount, totalDynamicDescriptors, dynamicOffsetCount);
+                }
             }
         } else {
             skipCall |= report_error_no_cb_begin(dev_data, commandBuffer, "vkCmdBindDescriptorSets()");
diff --git a/layers/draw_state.h b/layers/draw_state.h
index d5584ae..2dc8df5 100755
--- a/layers/draw_state.h
+++ b/layers/draw_state.h
@@ -73,6 +73,7 @@
     DRAWSTATE_INVALID_RENDERPASS_CMD,           // Invalid cmd submitted while a RenderPass is active
     DRAWSTATE_NO_ACTIVE_RENDERPASS,             // Rendering cmd submitted without an active RenderPass
     DRAWSTATE_DESCRIPTOR_SET_NOT_UPDATED,       // DescriptorSet bound but it was never updated. This is a warning code.
+    DRAWSTATE_INVALID_DYNAMIC_OFFSET_COUNT,     // DescriptorSets bound with different number of dynamic descriptors that were included in dynamicOffsetCount
     DRAWSTATE_CLEAR_CMD_BEFORE_DRAW,            // Clear cmd issued before any Draw in CommandBuffer, should use RenderPass Ops instead
     DRAWSTATE_BEGIN_CB_INVALID_STATE,           // Primary/Secondary CB created with mismatched FB/RP information
     DRAWSTATE_VIEWPORT_SCISSOR_MISMATCH,        // Count for viewports and scissors mismatch and/or state doesn't match count
@@ -165,6 +166,7 @@
     VkDescriptorSetLayoutCreateInfo createInfo;
     uint32_t                        startIndex; // 1st index of this layout
     uint32_t                        endIndex; // last index of this layout
+    uint32_t                        dynamicDescriptorCount; // Total count of dynamic descriptors used by this layout
     vector<VkDescriptorType>        descriptorTypes; // Type per descriptor in this layout to verify correct updates
     vector<VkShaderStageFlags>      stageFlags; // stageFlags per descriptor in this layout to verify correct updates
     unordered_set<uint32_t>         bindings;
diff --git a/layers/vk_validation_layer_details.md b/layers/vk_validation_layer_details.md
index e02d556..884700d 100644
--- a/layers/vk_validation_layer_details.md
+++ b/layers/vk_validation_layer_details.md
@@ -47,6 +47,7 @@
 | Correct use of RenderPass | Validates that the following rendering commands are issued inside an active RenderPass: vkCmdDraw, vkCmdDrawIndexed, vkCmdDrawIndirect, vkCmdDrawIndexedIndirect, vkCmdClearAttachments, vkCmdNextSubpass, vkCmdEndRenderPass | NO_ACTIVE_RENDERPASS | vkCmdBindPipeline vkCmdBindDescriptorSets  vkCmdDraw vkCmdDrawIndexed vkCmdDrawIndirect vkCmdDrawIndexedIndirect vkCmdClearAttachments vkCmdNextSubpass vkCmdEndRenderPass | BindPipelineNoRenderPass ClearAttachmentsOutsideRenderPass | NA |
 | Valid RenderPass | Flag error if attempt made to Begin/End/Continue a NULL or otherwise invalid RenderPass object | INVALID_RENDERPASS | vkCmdBeginRenderPass vkCmdEndRenderPass vkBeginCommandBuffer | NullRenderPass | NA |
 | DescriptorSet Updated | Warn user if DescriptorSet bound that was never updated | DESCRIPTOR_SET_NOT_UPDATED | vkCmdBindDescriptorSets | DescriptorSetNotUpdated | NA |
+| Dynamic Offset Count | Error if dynamicOffsetCount at CmdBindDescriptorSets time is not equal to the actual number of dynamic descriptors in all sets being bound. | INVALID_DYNAMIC_OFFSET_COUNT | vkCmdBindDescriptorSets | TODO | Write a test that hits this case |
 | Correct Clear Use | Warn user if CmdClear for Color or DepthStencil issued to Cmd Buffer prior to a Draw Cmd. RenderPass LOAD_OP_CLEAR is preferred in this case. | CLEAR_CMD_BEFORE_DRAW | vkCmdClearColorImage vkCmdClearDepthStencilImage | ClearCmdNoDraw | NA |
 | Index Buffer Binding | Verify that an index buffer is bound at the point when an indexed draw is attempted. | INDEX_BUFFER_NOT_BOUND | vkCmdDrawIndexed vkCmdDrawIndexedIndirect | TODO | Implement validation test |
 | Viewport and Scissors match | In PSO viewportCount and scissorCount must match. Also for each count that is non-zero, there corresponding data array ptr should be non-NULL. | VIEWPORT_SCISSOR_MISMATCH | vkCreateGraphicsPipelines vkCmdSetViewport vkCmdSetScissor | TODO | Implement validation test |