layers: Validate that fence is not in use when submitted.
authorMichael Lentine <mlentine@google.com>
Wed, 3 Feb 2016 04:24:26 +0000 (22:24 -0600)
committerTobin Ehlis <tobine@google.com>
Thu, 4 Feb 2016 18:17:41 +0000 (11:17 -0700)
layers/draw_state.cpp
layers/draw_state.h
layers/vk_validation_layer_details.md

index 06d76f1..5952625 100644 (file)
@@ -3312,6 +3312,7 @@ void decrementResources(layer_data* my_data, uint32_t fenceCount, const VkFence*
         auto fence_data = my_data->fenceMap.find(pFences[i]);
         if (fence_data == my_data->fenceMap.end() || !fence_data->second.needsSignaled) return;
         fence_data->second.needsSignaled = false;
+        fence_data->second.in_use.fetch_sub(1);
         if (fence_data->second.priorFence != VK_NULL_HANDLE) {
             decrementResources(my_data, 1, &fence_data->second.priorFence);
         }
@@ -3336,23 +3337,31 @@ void trackCommandBuffers(layer_data* my_data, VkQueue queue, uint32_t cmdBufferC
     auto queue_data = my_data->queueMap.find(queue);
     if (fence != VK_NULL_HANDLE) {
         VkFence priorFence = VK_NULL_HANDLE;
+        auto fence_data = my_data->fenceMap.find(fence);
+        if (fence_data == my_data->fenceMap.end()) {
+            return;
+        }
         if (queue_data != my_data->queueMap.end()) {
             priorFence = queue_data->second.priorFence;
             queue_data->second.priorFence = fence;
             for (auto cmdBuffer : queue_data->second.untrackedCmdBuffers) {
-                my_data->fenceMap[fence].cmdBuffers.push_back(cmdBuffer);
+                fence_data->second.cmdBuffers.push_back(cmdBuffer);
             }
             queue_data->second.untrackedCmdBuffers.clear();
         }
-        my_data->fenceMap[fence].cmdBuffers.clear();
-        my_data->fenceMap[fence].priorFence = priorFence;
-        my_data->fenceMap[fence].needsSignaled = true;
-        my_data->fenceMap[fence].queue = queue;
+        fence_data->second.cmdBuffers.clear();
+        fence_data->second.priorFence = priorFence;
+        fence_data->second.needsSignaled = true;
+        fence_data->second.queue = queue;
+        fence_data->second.in_use.fetch_add(1);
         for (uint32_t i = 0; i < cmdBufferCount; ++i) {
-            for (auto secondaryCmdBuffer : my_data->commandBufferMap[pCmdBuffers[i]]->secondaryCommandBuffers) {
-                my_data->fenceMap[fence].cmdBuffers.push_back(secondaryCmdBuffer);
+            for (auto secondaryCmdBuffer :
+                 my_data->commandBufferMap[pCmdBuffers[i]]
+                     ->secondaryCommandBuffers) {
+                fence_data->second.cmdBuffers.push_back(
+                    secondaryCmdBuffer);
             }
-            my_data->fenceMap[fence].cmdBuffers.push_back(pCmdBuffers[i]);
+            fence_data->second.cmdBuffers.push_back(pCmdBuffers[i]);
         }
     } else {
         if (queue_data != my_data->queueMap.end()) {
@@ -3457,7 +3466,17 @@ VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(VkQueue queue, uint
             skipCall |= validateCommandBufferState(dev_data, pCB);
             loader_platform_thread_unlock_mutex(&globalLock);
         }
-        trackCommandBuffers(dev_data, queue, submit->commandBufferCount, submit->pCommandBuffers, fence);
+        if (dev_data->fenceMap[fence].in_use.load()) {
+            skipCall |= log_msg(
+                dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
+                VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT,
+                reinterpret_cast<uint64_t>(fence), __LINE__,
+                DRAWSTATE_INVALID_FENCE, "DS",
+                "Fence %#" PRIx64 " is already in use by another submission.",
+                reinterpret_cast<uint64_t>(fence));
+        }
+        trackCommandBuffers(dev_data, queue, submit->commandBufferCount,
+                            submit->pCommandBuffers, fence);
     }
     if (VK_FALSE == skipCall)
         return dev_data->device_dispatch_table->QueueSubmit(queue, submitCount, pSubmits, fence);
@@ -3937,15 +3956,31 @@ VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView(VkDevice device
     return result;
 }
 
-//TODO handle pipeline caches
-VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineCache(
-    VkDevice                                    device,
-    const VkPipelineCacheCreateInfo*            pCreateInfo,
-    const VkAllocationCallbacks*                     pAllocator,
-    VkPipelineCache*                            pPipelineCache)
-{
-    layer_data* dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
-    VkResult result = dev_data->device_dispatch_table->CreatePipelineCache(device, pCreateInfo, pAllocator, pPipelineCache);
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+    vkCreateFence(VkDevice device, const VkFenceCreateInfo* pCreateInfo,
+                  const VkAllocationCallbacks* pAllocator, VkFence* pFence) {
+    layer_data *dev_data =
+        get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+    VkResult result = dev_data->device_dispatch_table->CreateFence(
+        device, pCreateInfo, pAllocator, pFence);
+    if (VK_SUCCESS == result) {
+        loader_platform_thread_lock_mutex(&globalLock);
+        dev_data->fenceMap[*pFence].in_use.store(0);
+        loader_platform_thread_unlock_mutex(&globalLock);
+    }
+    return result;
+}
+
+// TODO handle pipeline caches
+VKAPI_ATTR VkResult VKAPI_CALL
+    vkCreatePipelineCache(VkDevice device,
+                          const VkPipelineCacheCreateInfo *pCreateInfo,
+                          const VkAllocationCallbacks *pAllocator,
+                          VkPipelineCache *pPipelineCache) {
+    layer_data *dev_data =
+        get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+    VkResult result = dev_data->device_dispatch_table->CreatePipelineCache(
+        device, pCreateInfo, pAllocator, pPipelineCache);
     return result;
 }
 
@@ -6743,6 +6778,8 @@ VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkD
         return (PFN_vkVoidFunction) vkCreateImage;
     if (!strcmp(funcName, "vkCreateImageView"))
         return (PFN_vkVoidFunction) vkCreateImageView;
+    if (!strcmp(funcName, "vkCreateFence"))
+        return (PFN_vkVoidFunction) vkCreateFence;
     if (!strcmp(funcName, "CreatePipelineCache"))
         return (PFN_vkVoidFunction) vkCreatePipelineCache;
     if (!strcmp(funcName, "DestroyPipelineCache"))
index f8cf2f1..0726e0f 100755 (executable)
 using std::vector;
 
 // Draw State ERROR codes
-typedef enum _DRAW_STATE_ERROR
-{
-    DRAWSTATE_NONE,                             // Used for INFO & other non-error messages
-    DRAWSTATE_INTERNAL_ERROR,                   // Error with DrawState internal data structures
-    DRAWSTATE_NO_PIPELINE_BOUND,                // Unable to identify a bound pipeline
-    DRAWSTATE_INVALID_POOL,                     // Invalid DS pool
-    DRAWSTATE_INVALID_SET,                      // Invalid DS
-    DRAWSTATE_INVALID_LAYOUT,                   // Invalid DS layout
-    DRAWSTATE_INVALID_IMAGE_LAYOUT,             // Invalid Image layout
-    DRAWSTATE_INVALID_PIPELINE,                 // Invalid Pipeline handle referenced
-    DRAWSTATE_INVALID_PIPELINE_LAYOUT,          // Invalid PipelineLayout
-    DRAWSTATE_INVALID_PIPELINE_CREATE_STATE,    // Attempt to create a pipeline with invalid state
-    DRAWSTATE_INVALID_COMMAND_BUFFER,           // Invalid CommandBuffer referenced
-    DRAWSTATE_INVALID_BARRIER,                  // Invalid Barrier
-    DRAWSTATE_INVALID_BUFFER,                   // Invalid Buffer
-    DRAWSTATE_INVALID_QUERY,                    // Invalid Query
-    DRAWSTATE_VTX_INDEX_OUT_OF_BOUNDS,          // binding in vkCmdBindVertexData() too large for PSO's pVertexBindingDescriptions array
-    DRAWSTATE_VTX_INDEX_ALIGNMENT_ERROR,        // binding offset in vkCmdBindIndexBuffer() out of alignment based on indexType
-    //DRAWSTATE_MISSING_DOT_PROGRAM,              // No "dot" program in order to generate png image
-    DRAWSTATE_OUT_OF_MEMORY,                    // malloc failed
-    DRAWSTATE_INVALID_DESCRIPTOR_SET,           // Descriptor Set handle is unknown
-    DRAWSTATE_DESCRIPTOR_TYPE_MISMATCH,         // Type in layout vs. update are not the same
-    DRAWSTATE_DESCRIPTOR_STAGEFLAGS_MISMATCH,   // StageFlags in layout are not the same throughout a single VkWriteDescriptorSet update
-    DRAWSTATE_DESCRIPTOR_UPDATE_OUT_OF_BOUNDS,  // Descriptors set for update out of bounds for corresponding layout section
-    DRAWSTATE_DESCRIPTOR_POOL_EMPTY,            // Attempt to allocate descriptor from a pool with no more descriptors of that type available
-    DRAWSTATE_CANT_FREE_FROM_NON_FREE_POOL,     // Invalid to call vkFreeDescriptorSets on Sets allocated from a NON_FREE Pool
-    DRAWSTATE_INVALID_UPDATE_INDEX,             // Index of requested update is invalid for specified descriptors set
-    DRAWSTATE_INVALID_UPDATE_STRUCT,            // Struct in DS Update tree is of invalid type
-    DRAWSTATE_NUM_SAMPLES_MISMATCH,             // Number of samples in bound PSO does not match number in FB of current RenderPass
-    DRAWSTATE_NO_END_COMMAND_BUFFER,                // Must call vkEndCommandBuffer() before QueueSubmit on that commandBuffer
-    DRAWSTATE_NO_BEGIN_COMMAND_BUFFER,              // Binding cmds or calling End on CB that never had vkBeginCommandBuffer() called on it
-    DRAWSTATE_COMMAND_BUFFER_SINGLE_SUBMIT_VIOLATION, // Cmd Buffer created with VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT flag is submitted multiple times
-    DRAWSTATE_INVALID_SECONDARY_COMMAND_BUFFER,     // vkCmdExecuteCommands() called with a primary commandBuffer in pCommandBuffers array
-    DRAWSTATE_VIEWPORT_NOT_BOUND,               // Draw submitted with no viewport state bound
-    DRAWSTATE_SCISSOR_NOT_BOUND,                // Draw submitted with no scissor state bound
-    DRAWSTATE_LINE_WIDTH_NOT_BOUND,             // Draw submitted with no line width state bound
-    DRAWSTATE_DEPTH_BIAS_NOT_BOUND,             // Draw submitted with no depth bias state bound
-    DRAWSTATE_BLEND_NOT_BOUND,                  // Draw submitted with no blend state bound when color write enabled
-    DRAWSTATE_DEPTH_BOUNDS_NOT_BOUND,           // Draw submitted with no depth bounds state bound when depth enabled
-    DRAWSTATE_STENCIL_NOT_BOUND,                // Draw submitted with no stencil state bound when stencil enabled
-    DRAWSTATE_INDEX_BUFFER_NOT_BOUND,           // Draw submitted with no depth-stencil state bound when depth write enabled
-    DRAWSTATE_PIPELINE_LAYOUTS_INCOMPATIBLE,    // Draw submitted PSO Pipeline layout that's not compatible with layout from BindDescriptorSets
-    DRAWSTATE_RENDERPASS_INCOMPATIBLE,          // Incompatible renderpasses between secondary cmdBuffer and primary cmdBuffer or framebuffer
-    DRAWSTATE_FRAMEBUFFER_INCOMPATIBLE,         // Incompatible framebuffer between secondary cmdBuffer and active renderPass
-    DRAWSTATE_INVALID_RENDERPASS,               // Use of a NULL or otherwise invalid RenderPass object
-    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_DESCRIPTOR_SET_NOT_BOUND,         // DescriptorSet used by pipeline at draw time is not bound, or has been disturbed (which would have flagged previous warning)
-    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,           // CB state at Begin call is bad. Can be Primary/Secondary CB created with mismatched FB/RP information or CB in RECORDING state
-    DRAWSTATE_INVALID_CB_SIMULTANEOUS_USE,      // CmdBuffer is being used in violation of VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT rules (i.e. simultaneous use w/o that bit set)
-    DRAWSTATE_INVALID_COMMAND_BUFFER_RESET,     // Attempting to call Reset (or Begin on recorded cmdBuffer) that was allocated from Pool w/o VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit set. Can also happen when the command buffer is being reset (or freed) when in use.
-    DRAWSTATE_VIEWPORT_SCISSOR_MISMATCH,        // Count for viewports and scissors mismatch and/or state doesn't match count
-    DRAWSTATE_INVALID_IMAGE_ASPECT,             // Image aspect is invalid for the current operation
-    DRAWSTATE_MISSING_ATTACHMENT_REFERENCE,     // Attachment reference must be present in active subpass
+typedef enum _DRAW_STATE_ERROR {
+    DRAWSTATE_NONE,           // Used for INFO & other non-error messages
+    DRAWSTATE_INTERNAL_ERROR, // Error with DrawState internal data structures
+    DRAWSTATE_NO_PIPELINE_BOUND,       // Unable to identify a bound pipeline
+    DRAWSTATE_INVALID_POOL,            // Invalid DS pool
+    DRAWSTATE_INVALID_SET,             // Invalid DS
+    DRAWSTATE_INVALID_LAYOUT,          // Invalid DS layout
+    DRAWSTATE_INVALID_IMAGE_LAYOUT,    // Invalid Image layout
+    DRAWSTATE_INVALID_PIPELINE,        // Invalid Pipeline handle referenced
+    DRAWSTATE_INVALID_PIPELINE_LAYOUT, // Invalid PipelineLayout
+    DRAWSTATE_INVALID_PIPELINE_CREATE_STATE, // Attempt to create a pipeline
+                                             // with invalid state
+    DRAWSTATE_INVALID_COMMAND_BUFFER,        // Invalid CommandBuffer referenced
+    DRAWSTATE_INVALID_BARRIER,               // Invalid Barrier
+    DRAWSTATE_INVALID_BUFFER,                // Invalid Buffer
+    DRAWSTATE_INVALID_QUERY,                 // Invalid Query
+    DRAWSTATE_INVALID_FENCE,                 // Invalid Fence
+    DRAWSTATE_VTX_INDEX_OUT_OF_BOUNDS,   // binding in vkCmdBindVertexData() too
+                                         // large for PSO's
+                                         // pVertexBindingDescriptions array
+    DRAWSTATE_VTX_INDEX_ALIGNMENT_ERROR, // binding offset in
+                                         // vkCmdBindIndexBuffer() out of
+                                         // alignment based on indexType
+    // DRAWSTATE_MISSING_DOT_PROGRAM,              // No "dot" program in order
+    // to generate png image
+    DRAWSTATE_OUT_OF_MEMORY,            // malloc failed
+    DRAWSTATE_INVALID_DESCRIPTOR_SET,   // Descriptor Set handle is unknown
+    DRAWSTATE_DESCRIPTOR_TYPE_MISMATCH, // Type in layout vs. update are not the
+                                        // same
+    DRAWSTATE_DESCRIPTOR_STAGEFLAGS_MISMATCH,  // StageFlags in layout are not
+                                               // the same throughout a single
+                                               // VkWriteDescriptorSet update
+    DRAWSTATE_DESCRIPTOR_UPDATE_OUT_OF_BOUNDS, // Descriptors set for update out
+                                               // of bounds for corresponding
+                                               // layout section
+    DRAWSTATE_DESCRIPTOR_POOL_EMPTY, // Attempt to allocate descriptor from a
+                                     // pool with no more descriptors of that
+                                     // type available
+    DRAWSTATE_CANT_FREE_FROM_NON_FREE_POOL, // Invalid to call
+                                            // vkFreeDescriptorSets on Sets
+                                            // allocated from a NON_FREE Pool
+    DRAWSTATE_INVALID_UPDATE_INDEX,  // Index of requested update is invalid for
+                                     // specified descriptors set
+    DRAWSTATE_INVALID_UPDATE_STRUCT, // Struct in DS Update tree is of invalid
+                                     // type
+    DRAWSTATE_NUM_SAMPLES_MISMATCH,  // Number of samples in bound PSO does not
+                                     // match number in FB of current RenderPass
+    DRAWSTATE_NO_END_COMMAND_BUFFER, // Must call vkEndCommandBuffer() before
+                                     // QueueSubmit on that commandBuffer
+    DRAWSTATE_NO_BEGIN_COMMAND_BUFFER, // Binding cmds or calling End on CB that
+                                       // never had vkBeginCommandBuffer()
+                                       // called on it
+    DRAWSTATE_COMMAND_BUFFER_SINGLE_SUBMIT_VIOLATION, // Cmd Buffer created with
+                                                      // VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
+                                                      // flag is submitted
+                                                      // multiple times
+    DRAWSTATE_INVALID_SECONDARY_COMMAND_BUFFER, // vkCmdExecuteCommands() called
+                                                // with a primary commandBuffer
+                                                // in pCommandBuffers array
+    DRAWSTATE_VIEWPORT_NOT_BOUND, // Draw submitted with no viewport state bound
+    DRAWSTATE_SCISSOR_NOT_BOUND,  // Draw submitted with no scissor state bound
+    DRAWSTATE_LINE_WIDTH_NOT_BOUND, // Draw submitted with no line width state
+                                    // bound
+    DRAWSTATE_DEPTH_BIAS_NOT_BOUND, // Draw submitted with no depth bias state
+                                    // bound
+    DRAWSTATE_BLEND_NOT_BOUND, // Draw submitted with no blend state bound when
+                               // color write enabled
+    DRAWSTATE_DEPTH_BOUNDS_NOT_BOUND, // Draw submitted with no depth bounds
+                                      // state bound when depth enabled
+    DRAWSTATE_STENCIL_NOT_BOUND, // Draw submitted with no stencil state bound
+                                 // when stencil enabled
+    DRAWSTATE_INDEX_BUFFER_NOT_BOUND, // Draw submitted with no depth-stencil
+                                      // state bound when depth write enabled
+    DRAWSTATE_PIPELINE_LAYOUTS_INCOMPATIBLE, // Draw submitted PSO Pipeline
+                                             // layout that's not compatible
+                                             // with layout from
+                                             // BindDescriptorSets
+    DRAWSTATE_RENDERPASS_INCOMPATIBLE,  // Incompatible renderpasses between
+                                        // secondary cmdBuffer and primary
+                                        // cmdBuffer or framebuffer
+    DRAWSTATE_FRAMEBUFFER_INCOMPATIBLE, // Incompatible framebuffer between
+                                        // secondary cmdBuffer and active
+                                        // renderPass
+    DRAWSTATE_INVALID_RENDERPASS,       // Use of a NULL or otherwise invalid
+                                        // RenderPass object
+    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_DESCRIPTOR_SET_NOT_BOUND,   // DescriptorSet used by pipeline at
+                                          // draw time is not bound, or has been
+                                          // disturbed (which would have flagged
+                                          // previous warning)
+    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, // CB state at Begin call is bad. Can be
+                                      // Primary/Secondary CB created with
+                                      // mismatched FB/RP information or CB in
+                                      // RECORDING state
+    DRAWSTATE_INVALID_CB_SIMULTANEOUS_USE, // CmdBuffer is being used in
+                                           // violation of
+                                           // VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT
+                                           // rules (i.e. simultaneous use w/o
+                                           // that bit set)
+    DRAWSTATE_INVALID_COMMAND_BUFFER_RESET, // Attempting to call Reset (or
+                                            // Begin on recorded cmdBuffer) that
+                                            // was allocated from Pool w/o
+                                            // VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
+                                            // bit set
+    DRAWSTATE_VIEWPORT_SCISSOR_MISMATCH, // Count for viewports and scissors
+                                         // mismatch and/or state doesn't match
+                                         // count
+    DRAWSTATE_INVALID_IMAGE_ASPECT, // Image aspect is invalid for the current
+                                    // operation
+    DRAWSTATE_MISSING_ATTACHMENT_REFERENCE, // Attachment reference must be
+                                            // present in active subpass
     DRAWSTATE_INVALID_EXTENSION,
     DRAWSTATE_SAMPLER_DESCRIPTOR_ERROR,         // A Descriptor of *_SAMPLER type is being updated with an invalid or bad Sampler
     DRAWSTATE_INCONSISTENT_IMMUTABLE_SAMPLER_UPDATE, // Descriptors of *COMBINED_IMAGE_SAMPLER type are being updated where some, but not all, of the updates use immutable samplers
index d4f1269..cce7234 100644 (file)
@@ -71,7 +71,8 @@ The VK_LAYER_LUNARG_draw_state layer tracks state leading into Draw cmds. This i
 | Verify Memory Buffer Not Deleted | Validate Command Buffer not submitted with deleted memory buffer | INVALID_BUFFER | vkQueueSubmit | TBD | None |
 | Verify Memory Buffer Destroy | Validate memory buffers are not destroyed more than once | DOUBLE_DESTROY | vkDestroyBuffer | TBD | None |
 | Verify Object Not In Use | Validate that object being freed or modified is not in use | OBJECT_INUSE | vkDestroyBuffer vkFreeDescriptorSets vkUpdateDescriptorSets | TBD | None |
-| Verify Get Queries| Validate that that queries are properly setup, initialized, synchronized | INVALID_QUERY | vkGetFenceStatus vkQueueWaitIdle vkWaitForFences vkDeviceWaitIdle vkCmdBeginQuery vkCmdEndQuery| TBD | None |
+| Verify Get Queries| Validate that that queries are properly setup, initialized and synchronized | INVALID_QUERY | vkGetFenceStatus vkQueueWaitIdle vkWaitForFences vkDeviceWaitIdle vkCmdBeginQuery vkCmdEndQuery | TBD | None |
+| Verify Fences Not In Use | Validate that that fences are not used in multiple submit calls at the same time | INVALID_FENCE | vkQueueSubmit | TBD | None |
 | Live Semaphore  | When waiting on a semaphore, need to make sure that the semaphore is live and therefore can be signalled, otherwise queue is stalled and cannot make forward progress. | QUEUE_FORWARD_PROGRESS | vkQueueSubmit vkQueueBindSparse vkQueuePresentKHR vkAcquireNextImageKHR | TODO | Create test |
 | Storage Buffer Alignment  | Storage Buffer offsets in BindDescriptorSets must agree with offset alignment device limit | INVALID_STORAGE_BUFFER_OFFSET | vkCmdBindDescriptorSets | TODO | Create test |
 | Uniform Buffer Alignment  | Uniform Buffer offsets in BindDescriptorSets must agree with offset alignment device limit | INVALID_UNIFORM_BUFFER_OFFSET | vkCmdBindDescriptorSets | TODO | Create test |