layers: Add handle NULL checks to codegen

Add support for validating that required handle parameters are not
specified as VK_NULL_HANDLE to the parameter validation layer's code
generator.
 - Add new parameter validation utility functions to validate required
   handles and arrays of handles.
 - Add new parameter validation layer entrypoints for functions that
   previously had no parameters to validate.
 - Add handle validation logic to the parameter validation code generator.

Change-Id: I7a5680954245db4c1b12587f78e30e17c3903d6c
diff --git a/generator.py b/generator.py
index 8040346..0bf6e95 100644
--- a/generator.py
+++ b/generator.py
@@ -2845,6 +2845,7 @@
         self.structNames = []                             # List of Vulkan struct typenames
         self.stypes = []                                  # Values from the VkStructureType enumeration
         self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
+        self.handleTypes = set()                          # Set of handle type names
         self.commands = []                                # List of CommandData records for all Vulkan commands
         self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
         self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
@@ -2852,7 +2853,8 @@
         # Named tuples to store struct and command data
         self.StructType = namedtuple('StructType', ['name', 'value'])
         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
-                                                        'isconst', 'isoptional', 'iscount', 'len', 'extstructs', 'condition', 'cdecl'])
+                                                        'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
+                                                        'condition', 'cdecl'])
         self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
     #
@@ -2915,6 +2917,7 @@
         self.structNames = []
         self.stypes = []
         self.structTypes = dict()
+        self.handleTypes = set()
         self.commands = []
         self.structMembers = []
         self.validatedStructs = dict()
@@ -2960,6 +2963,8 @@
         if (category == 'struct' or category == 'union'):
             self.structNames.append(name)
             self.genStruct(typeinfo, name)
+        elif (category == 'handle'):
+            self.handleTypes.add(name)
     #
     # Struct parameter check generation.
     # This is a special case of the <type> tag where the contents are
@@ -3034,6 +3039,7 @@
                                                 isconst=True if 'const' in cdecl else False,
                                                 isoptional=isoptional,
                                                 iscount=iscount,
+                                                noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
                                                 len=self.getLen(member),
                                                 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
                                                 condition=conditions[name] if conditions and name in conditions else None,
@@ -3094,6 +3100,7 @@
                                                     isconst=True if 'const' in cdecl else False,
                                                     isoptional=self.paramIsOptional(param),
                                                     iscount=iscount,
+                                                    noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
                                                     len=self.getLen(param),
                                                     extstructs=None,
                                                     condition=None,
@@ -3142,6 +3149,20 @@
                 isoptional = opts
         return isoptional
     #
+    # Check if the handle passed in is optional
+    # Uses the same logic as ValidityOutputGenerator.isHandleOptional
+    def isHandleOptional(self, param, lenParam):
+        # Simple, if it's optional, return true
+        if param.isoptional:
+            return True
+        # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
+        if param.noautovalidity:
+            return True
+        # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
+        if lenParam and lenParam.isoptional:
+            return True
+        return False
+    #
     # Retrieve the value of the len tag
     def getLen(self, param):
         result = None
@@ -3201,7 +3222,8 @@
             if '->' in name:
                 # The count is obtained by dereferencing a member of a struct parameter
                 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
-                                             isstaticarray=None, isoptional=False, type=None, len=None, extstructs=None, condition=None, cdecl=None)
+                                             isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
+                                             condition=None, cdecl=None)
             elif 'latexmath' in name:
                 lenName, decoratedName = self.parseLateXMath(name)
                 lenParam = self.getParamByName(params, lenName)
@@ -3211,7 +3233,7 @@
                 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
                 #                             isoptional=param.isoptional, type=param.type, len=param.len,
                 #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs,
-                #                             condition=None, cdecl=param.cdecl)
+                #                             noautovalidity=True, condition=None, cdecl=param.cdecl)
             else:
                 lenParam = self.getParamByName(params, name)
         return lenParam
@@ -3289,6 +3311,22 @@
                 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value))
         return checkExpr
     #
+    # Generate the sType check string
+    def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
+        checkExpr = []
+        if lenValue:
+            # This is assumed to be an output array with a pointer to a count value
+            if lenValue.ispointer:
+                raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
+            # This is an array with an integer count value
+            else:
+                checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
+                    funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
+        # This is assumed to be an output handle pointer
+        else:
+            raise('Unsupported parameter validation case: Output handles are not NULL checked')
+        return checkExpr
+    #
     # Generate pNext check string
     def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName):
         checkExpr = []
@@ -3431,6 +3469,9 @@
                 # If this is a pointer to a struct with an sType field, verify the type
                 if value.type in self.structTypes:
                     usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
+                # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
+                elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
+                    usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
                 elif value.name == 'pNext':
                     # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
                     if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
@@ -3451,6 +3492,9 @@
                 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
                 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
                 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', []))
+            elif value.type in self.handleTypes:
+                if not self.isHandleOptional(value, None):
+                    usedLines.append('skipCall |= validate_required_handle(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
             elif value.isbool:
                 usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
             elif value.israngedenum:
@@ -3483,7 +3527,9 @@
     def processCmdData(self):
         indent = self.incIndent(None)
         for command in self.commands:
-            lines, unused = self.genFuncBody(command.name, command.params, '', '', None)
+            # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
+            startIndex = 0 if command.name == 'vkCreateInstance' else 1
+            lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
             if lines:
                 cmdDef = self.getCmdDef(command) + '\n'
                 cmdDef += '{\n'
@@ -3491,10 +3537,9 @@
                 # processed by parameter_validation (except for vkCreateInstance, which does not have a
                 # handle as its first parameter)
                 if unused:
-                    startIndex = 0 if command.name == 'vkCreateInstance' else 1
-                    for name in unused[startIndex:]:
+                    for name in unused:
                         cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
-                    if len(unused) > startIndex:
+                    if len(unused) > 0:
                         cmdDef += '\n'
                 cmdDef += indent + 'bool skipCall = false;\n'
                 for line in lines:
diff --git a/layers/parameter_validation.cpp b/layers/parameter_validation.cpp
index c8d2f42..9a995d9 100644
--- a/layers/parameter_validation.cpp
+++ b/layers/parameter_validation.cpp
@@ -1485,12 +1485,6 @@
                 set.insert(pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex);
             }
 
-            if (pCreateInfo->pQueueCreateInfos[i].queueCount == 0) {
-                log_msg(mdd(physicalDevice), VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
-                        "PARAMCHECK", "VkDeviceCreateInfo parameter, uint32_t pQueueCreateInfos[%d]->queueCount, cannot be zero.",
-                        i);
-            }
-
             if (pCreateInfo->pQueueCreateInfos[i].pQueuePriorities != nullptr) {
                 for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; ++j) {
                     if ((pCreateInfo->pQueueCreateInfos[i].pQueuePriorities[j] < 0.f) ||
@@ -1748,6 +1742,18 @@
     return result;
 }
 
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkUnmapMemory(VkDevice device, VkDeviceMemory memory) {
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkUnmapMemory(my_data->report_data, memory);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, device)->UnmapMemory(device, memory);
+    }
+}
+
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
 vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange *pMemoryRanges) {
     VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
@@ -1798,26 +1804,38 @@
     }
 }
 
-VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
-vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory mem, VkDeviceSize memoryOffset) {
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory,
+                                                                  VkDeviceSize memoryOffset) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->BindBufferMemory(device, buffer, mem, memoryOffset);
+    skipCall |= parameter_validation_vkBindBufferMemory(my_data->report_data, buffer, memory, memoryOffset);
 
-    validate_result(my_data->report_data, "vkBindBufferMemory", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->BindBufferMemory(device, buffer, memory, memoryOffset);
+
+        validate_result(my_data->report_data, "vkBindBufferMemory", result);
+    }
 
     return result;
 }
 
-VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
-vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory mem, VkDeviceSize memoryOffset) {
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory,
+                                                                 VkDeviceSize memoryOffset) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->BindImageMemory(device, image, mem, memoryOffset);
+    skipCall |= parameter_validation_vkBindImageMemory(my_data->report_data, image, memory, memoryOffset);
 
-    validate_result(my_data->report_data, "vkBindImageMemory", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->BindImageMemory(device, image, memory, memoryOffset);
+
+        validate_result(my_data->report_data, "vkBindImageMemory", result);
+    }
 
     return result;
 }
@@ -1985,12 +2003,18 @@
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus(VkDevice device, VkFence fence) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->GetFenceStatus(device, fence);
+    skipCall |= parameter_validation_vkGetFenceStatus(my_data->report_data, fence);
 
-    validate_result(my_data->report_data, "vkGetFenceStatus", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->GetFenceStatus(device, fence);
+
+        validate_result(my_data->report_data, "vkGetFenceStatus", result);
+    }
 
     return result;
 }
@@ -2075,34 +2099,52 @@
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetEventStatus(VkDevice device, VkEvent event) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->GetEventStatus(device, event);
+    skipCall |= parameter_validation_vkGetEventStatus(my_data->report_data, event);
 
-    validate_result(my_data->report_data, "vkGetEventStatus", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->GetEventStatus(device, event);
+
+        validate_result(my_data->report_data, "vkGetEventStatus", result);
+    }
 
     return result;
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkSetEvent(VkDevice device, VkEvent event) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->SetEvent(device, event);
+    skipCall |= parameter_validation_vkSetEvent(my_data->report_data, event);
 
-    validate_result(my_data->report_data, "vkSetEvent", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->SetEvent(device, event);
+
+        validate_result(my_data->report_data, "vkSetEvent", result);
+    }
 
     return result;
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkResetEvent(VkDevice device, VkEvent event) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->ResetEvent(device, event);
+    skipCall |= parameter_validation_vkResetEvent(my_data->report_data, event);
 
-    validate_result(my_data->report_data, "vkSetEvent", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->ResetEvent(device, event);
+
+        validate_result(my_data->report_data, "vkResetEvent", result);
+    }
 
     return result;
 }
@@ -2474,10 +2516,6 @@
                 return false;
             }
         }
-        if (pCreateInfos->renderPass == VK_NULL_HANDLE) {
-            log_msg(mdd(device), VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1, "PARAMCHECK",
-                    "vkCreateGraphicsPipelines parameter, VkRenderPass pCreateInfos->renderPass, is null pointer");
-        }
 
         int i = 0;
         for (size_t j = 0; j < pCreateInfos[i].stageCount; j++) {
@@ -2696,12 +2734,18 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
 vkResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->ResetDescriptorPool(device, descriptorPool, flags);
+    skipCall |= parameter_validation_vkResetDescriptorPool(my_data->report_data, descriptorPool, flags);
 
-    validate_result(my_data->report_data, "vkResetDescriptorPool", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->ResetDescriptorPool(device, descriptorPool, flags);
+
+        validate_result(my_data->report_data, "vkResetDescriptorPool", result);
+    }
 
     return result;
 }
@@ -2873,12 +2917,18 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
 vkResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags) {
+    VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+    bool skipCall = false;
     layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
     assert(my_data != NULL);
 
-    VkResult result = get_dispatch_table(pc_device_table_map, device)->ResetCommandPool(device, commandPool, flags);
+    skipCall |= parameter_validation_vkResetCommandPool(my_data->report_data, commandPool, flags);
 
-    validate_result(my_data->report_data, "vkResetCommandPool", result);
+    if (!skipCall) {
+        result = get_dispatch_table(pc_device_table_map, device)->ResetCommandPool(device, commandPool, flags);
+
+        validate_result(my_data->report_data, "vkResetCommandPool", result);
+    }
 
     return result;
 }
@@ -3123,12 +3173,29 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t count, uint32_t stride) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdDrawIndirect(commandBuffer, buffer, offset, count, stride);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdDrawIndirect(my_data->report_data, buffer, offset, count, stride);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdDrawIndirect(commandBuffer, buffer, offset, count, stride);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t count, uint32_t stride) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdDrawIndexedIndirect(commandBuffer, buffer, offset, count, stride);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdDrawIndexedIndirect(my_data->report_data, buffer, offset, count, stride);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)
+            ->CmdDrawIndexedIndirect(commandBuffer, buffer, offset, count, stride);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
@@ -3137,7 +3204,15 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdDispatchIndirect(commandBuffer, buffer, offset);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdDispatchIndirect(my_data->report_data, buffer, offset);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdDispatchIndirect(commandBuffer, buffer, offset);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer,
@@ -3308,7 +3383,15 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdFillBuffer(commandBuffer, dstBuffer, dstOffset, size, data);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdFillBuffer(my_data->report_data, dstBuffer, dstOffset, size, data);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdFillBuffer(commandBuffer, dstBuffer, dstOffset, size, data);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image,
@@ -3399,12 +3482,28 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdSetEvent(commandBuffer, event, stageMask);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdSetEvent(my_data->report_data, event, stageMask);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdSetEvent(commandBuffer, event, stageMask);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdResetEvent(commandBuffer, event, stageMask);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdResetEvent(my_data->report_data, event, stageMask);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdResetEvent(commandBuffer, event, stageMask);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
@@ -3449,16 +3548,40 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot, VkQueryControlFlags flags) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdBeginQuery(commandBuffer, queryPool, slot, flags);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdBeginQuery(my_data->report_data, queryPool, slot, flags);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdBeginQuery(commandBuffer, queryPool, slot, flags);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdEndQuery(commandBuffer, queryPool, slot);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdEndQuery(my_data->report_data, queryPool, slot);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdEndQuery(commandBuffer, queryPool, slot);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdResetQueryPool(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdResetQueryPool(commandBuffer, queryPool, firstQuery, queryCount);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdResetQueryPool(my_data->report_data, queryPool, firstQuery, queryCount);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdResetQueryPool(commandBuffer, queryPool, firstQuery, queryCount);
+    }
 }
 
 bool PostCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool,
@@ -3469,18 +3592,35 @@
     return true;
 }
 
-VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
-vkCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t slot) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)->CmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, slot);
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage,
+                                                               VkQueryPool queryPool, uint32_t query) {
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
 
-    PostCmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, slot);
+    skipCall |= parameter_validation_vkCmdWriteTimestamp(my_data->report_data, pipelineStage, queryPool, query);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)->CmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, query);
+
+        PostCmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, query);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
 vkCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount,
                           VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) {
-    get_dispatch_table(pc_device_table_map, commandBuffer)
-        ->CmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, stride, flags);
+    bool skipCall = false;
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+    assert(my_data != NULL);
+
+    skipCall |= parameter_validation_vkCmdCopyQueryPoolResults(my_data->report_data, queryPool, firstQuery, queryCount, dstBuffer,
+                                                               dstOffset, stride, flags);
+
+    if (!skipCall) {
+        get_dispatch_table(pc_device_table_map, commandBuffer)
+            ->CmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, stride, flags);
+    }
 }
 
 VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout,
@@ -3566,10 +3706,18 @@
         return (PFN_vkVoidFunction)vkFreeMemory;
     if (!strcmp(funcName, "vkMapMemory"))
         return (PFN_vkVoidFunction)vkMapMemory;
+    if (!strcmp(funcName, "vkUnmapMemory"))
+        return (PFN_vkVoidFunction)vkUnmapMemory;
     if (!strcmp(funcName, "vkFlushMappedMemoryRanges"))
         return (PFN_vkVoidFunction)vkFlushMappedMemoryRanges;
     if (!strcmp(funcName, "vkInvalidateMappedMemoryRanges"))
         return (PFN_vkVoidFunction)vkInvalidateMappedMemoryRanges;
+    if (!strcmp(funcName, "vkGetDeviceMemoryCommitment"))
+        return (PFN_vkVoidFunction)vkGetDeviceMemoryCommitment;
+    if (!strcmp(funcName, "vkBindBufferMemory"))
+        return (PFN_vkVoidFunction)vkBindBufferMemory;
+    if (!strcmp(funcName, "vkBindImageMemory"))
+        return (PFN_vkVoidFunction)vkBindImageMemory;
     if (!strcmp(funcName, "vkCreateFence"))
         return (PFN_vkVoidFunction)vkCreateFence;
     if (!strcmp(funcName, "vkDestroyFence"))
@@ -3754,6 +3902,8 @@
         return (PFN_vkVoidFunction)vkCreateCommandPool;
     if (!strcmp(funcName, "vkDestroyCommandPool"))
         return (PFN_vkVoidFunction)vkDestroyCommandPool;
+    if (!strcmp(funcName, "vkResetCommandPool"))
+        return (PFN_vkVoidFunction)vkResetCommandPool;
     if (!strcmp(funcName, "vkCmdBeginRenderPass"))
         return (PFN_vkVoidFunction)vkCmdBeginRenderPass;
     if (!strcmp(funcName, "vkCmdNextSubpass"))
diff --git a/layers/parameter_validation_utils.h b/layers/parameter_validation_utils.h
index 70e5fcc..83315f6 100644
--- a/layers/parameter_validation_utils.h
+++ b/layers/parameter_validation_utils.h
@@ -161,7 +161,11 @@
 }
 
 /**
- * Validate an Vulkan structure type.
+ * Validate a pointer to a Vulkan structure.
+ *
+ * Verify that a required pointer to a structure is not NULL.  If the pointer is
+ * not NULL, verify that each structure's sType field is set to the correct
+ * VkStructureType value.
  *
  * @param report_data debug_report_data object for routing validation messages.
  * @param apiName Name of API call being validated.
@@ -283,6 +287,83 @@
 }
 
 /**
+* Validate a Vulkan handle.
+*
+* Verify that the specified handle is not VK_NULL_HANDLE.
+*
+* @param report_data debug_report_data object for routing validation messages.
+* @param api_name Name of API call being validated.
+* @param parameter_name Name of struct parameter being validated.
+* @param value Handle to validate.
+* @return Boolean value indicating that the call should be skipped.
+*/
+template <typename T>
+bool validate_required_handle(debug_report_data *report_data, const char *api_name, const char *parameter_name, T value) {
+    bool skip_call = false;
+
+    if (value == VK_NULL_HANDLE) {
+        skip_call |=
+            log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+                    ParameterValidationName, "%s: required parameter %s specified as VK_NULL_HANDLE", api_name, parameter_name);
+    }
+
+    return skip_call;
+}
+
+/**
+* Validate an array of Vulkan handles.
+*
+* Verify that required count and array parameters are not NULL.  If count
+* is not NULL and its value is not optional, verify that it is not 0.
+* If the array contains 1 or more handles, verify that no handle is set to
+* VK_NULL_HANDLE.
+*
+* @note This function is only intended to validate arrays of handles when none
+*       of the handles are allowed to be VK_NULL_HANDLE.  For arrays of handles
+*       that are allowed to contain VK_NULL_HANDLE, use validate_array() instead.
+*
+* @param report_data debug_report_data object for routing validation messages.
+* @param api_name Name of API call being validated.
+* @param count_name Name of count parameter.
+* @param array_name Name of array parameter.
+* @param count Number of elements in the array.
+* @param array Array to validate.
+* @param count_required The 'count' parameter may not be 0 when true.
+* @param array_required The 'array' parameter may not be NULL when true.
+* @return Boolean value indicating that the call should be skipped.
+*/
+template <typename T>
+bool validate_handle_array(debug_report_data *report_data, const char *api_name, const char *count_name, const char *array_name,
+                           uint32_t count, const T *array, bool count_required, bool array_required) {
+    bool skip_call = false;
+
+    if ((count == 0) || (array == NULL)) {
+        // Count parameters not tagged as optional cannot be 0
+        if ((count == 0) && count_required) {
+            skip_call |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+                                 ParameterValidationName, "%s: parameter %s must be greater than 0", api_name, count_name);
+        }
+
+        // Array parameters not tagged as optional cannot be NULL, unless the count is 0
+        if ((array == NULL) && array_required && (count != 0)) {
+            skip_call |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+                                 ParameterValidationName, "%s: required parameter %s specified as NULL", api_name, array_name);
+        }
+    } else {
+        // Verify that no handles in the array are VK_NULL_HANDLE
+        for (uint32_t i = 0; i < count; ++i) {
+            if (array[i] == VK_NULL_HANDLE) {
+                skip_call |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, 1,
+                                     ParameterValidationName, "%s: required parameter %s[%d] specified as VK_NULL_HANDLE", api_name,
+                                     array_name, i);
+            }
+        }
+    }
+
+    return skip_call;
+}
+
+/**
  * Validate string array count and content.
  *
  * Verify that required count and array parameters are not 0 or NULL.  If the