From 01423238d40332394ef968bc4073548e4037865f Mon Sep 17 00:00:00 2001 From: John Zulauf Date: Thu, 26 Apr 2018 16:30:39 -0600 Subject: layers: PreRecord Free/Destroy calls To avoid Destroy/Create and Free/Alloc race conditions (when a driver returns a destroyed(freed) handle between the PreCall and PostCall phases of a validation intercept routine, PostCallRecord functionality has been moved to PreCall. This affects the following calls FreeMemory DestroyFence DestroySemaphore DestroyEvent DestroyQueryPool DestroyBuffer DestroyBufferView DestroyImage DestroyImageView DestroyPipeline DestroySampler DestroyDescriptorSetLayout DestroyFramebuffer DestroyRenderPass FreeDescriptorSets Did not alter the destroy functions for Instance, Device, or the Debug or layer extensions. Change-Id: I5133982eaa1a0c94283fa922d9c3bcc90b5a6223 --- layers/core_validation.cpp | 163 +++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 74 deletions(-) (limited to 'layers/core_validation.cpp') diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index b2f71b36..fca9c8e9 100644 --- a/layers/core_validation.cpp +++ b/layers/core_validation.cpp @@ -3104,7 +3104,7 @@ static bool PreCallValidateFreeMemory(layer_data *dev_data, VkDeviceMemory mem, return skip; } -static void PostCallRecordFreeMemory(layer_data *dev_data, VkDeviceMemory mem, DEVICE_MEM_INFO *mem_info, VK_OBJECT obj_struct) { +static void PreCallRecordFreeMemory(layer_data *dev_data, VkDeviceMemory mem, DEVICE_MEM_INFO *mem_info, VK_OBJECT obj_struct) { // Clear mem binding for any bound objects for (auto obj : mem_info->obj_bindings) { log_msg(dev_data->report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, get_debug_report_enum[obj.type], obj.handle, @@ -3139,12 +3139,12 @@ VKAPI_ATTR void VKAPI_CALL FreeMemory(VkDevice device, VkDeviceMemory mem, const unique_lock_t lock(global_lock); bool skip = PreCallValidateFreeMemory(dev_data, mem, &mem_info, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.FreeMemory(device, mem, pAllocator); - lock.lock(); if (mem != VK_NULL_HANDLE) { - PostCallRecordFreeMemory(dev_data, mem, mem_info, obj_struct); + // Avoid free/alloc race by recording state change before dispatching + PreCallRecordFreeMemory(dev_data, mem, mem_info, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.FreeMemory(device, mem, pAllocator); } } @@ -3456,7 +3456,7 @@ static bool PreCallValidateDestroyFence(layer_data *dev_data, VkFence fence, FEN return skip; } -static void PostCallRecordDestroyFence(layer_data *dev_data, VkFence fence) { dev_data->fenceMap.erase(fence); } +static void PreCallRecordDestroyFence(layer_data *dev_data, VkFence fence) { dev_data->fenceMap.erase(fence); } VKAPI_ATTR void VKAPI_CALL DestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); @@ -3467,10 +3467,10 @@ VKAPI_ATTR void VKAPI_CALL DestroyFence(VkDevice device, VkFence fence, const Vk bool skip = PreCallValidateDestroyFence(dev_data, fence, &fence_node, &obj_struct); if (!skip) { + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyFence(dev_data, fence); lock.unlock(); dev_data->dispatch_table.DestroyFence(device, fence, pAllocator); - lock.lock(); - PostCallRecordDestroyFence(dev_data, fence); } } @@ -3486,7 +3486,7 @@ static bool PreCallValidateDestroySemaphore(layer_data *dev_data, VkSemaphore se return skip; } -static void PostCallRecordDestroySemaphore(layer_data *dev_data, VkSemaphore sema) { dev_data->semaphoreMap.erase(sema); } +static void PreCallRecordDestroySemaphore(layer_data *dev_data, VkSemaphore sema) { dev_data->semaphoreMap.erase(sema); } VKAPI_ATTR void VKAPI_CALL DestroySemaphore(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks *pAllocator) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); @@ -3495,10 +3495,10 @@ VKAPI_ATTR void VKAPI_CALL DestroySemaphore(VkDevice device, VkSemaphore semapho unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroySemaphore(dev_data, semaphore, &sema_node, &obj_struct); if (!skip) { + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroySemaphore(dev_data, semaphore); lock.unlock(); dev_data->dispatch_table.DestroySemaphore(device, semaphore, pAllocator); - lock.lock(); - PostCallRecordDestroySemaphore(dev_data, semaphore); } } @@ -3513,7 +3513,7 @@ static bool PreCallValidateDestroyEvent(layer_data *dev_data, VkEvent event, EVE return skip; } -static void PostCallRecordDestroyEvent(layer_data *dev_data, VkEvent event, EVENT_STATE *event_state, VK_OBJECT obj_struct) { +static void PreCallRecordDestroyEvent(layer_data *dev_data, VkEvent event, EVENT_STATE *event_state, VK_OBJECT obj_struct) { invalidateCommandBuffers(dev_data, event_state->cb_bindings, obj_struct); dev_data->eventMap.erase(event); } @@ -3525,12 +3525,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyEvent(VkDevice device, VkEvent event, const Vk unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyEvent(dev_data, event, &event_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyEvent(device, event, pAllocator); - lock.lock(); if (event != VK_NULL_HANDLE) { - PostCallRecordDestroyEvent(dev_data, event, event_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyEvent(dev_data, event, event_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyEvent(device, event, pAllocator); } } @@ -3546,8 +3546,8 @@ static bool PreCallValidateDestroyQueryPool(layer_data *dev_data, VkQueryPool qu return skip; } -static void PostCallRecordDestroyQueryPool(layer_data *dev_data, VkQueryPool query_pool, QUERY_POOL_NODE *qp_state, - VK_OBJECT obj_struct) { +static void PreCallRecordDestroyQueryPool(layer_data *dev_data, VkQueryPool query_pool, QUERY_POOL_NODE *qp_state, + VK_OBJECT obj_struct) { invalidateCommandBuffers(dev_data, qp_state->cb_bindings, obj_struct); dev_data->queryPoolMap.erase(query_pool); } @@ -3559,12 +3559,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyQueryPool(VkDevice device, VkQueryPool queryPo unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyQueryPool(dev_data, queryPool, &qp_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyQueryPool(device, queryPool, pAllocator); - lock.lock(); if (queryPool != VK_NULL_HANDLE) { - PostCallRecordDestroyQueryPool(dev_data, queryPool, qp_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyQueryPool(dev_data, queryPool, qp_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyQueryPool(device, queryPool, pAllocator); } } static bool PreCallValidateGetQueryPoolResults(layer_data *dev_data, VkQueryPool query_pool, uint32_t first_query, @@ -3865,12 +3865,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyBuffer(VkDevice device, VkBuffer buffer, const unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyBuffer(dev_data, buffer, &buffer_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyBuffer(device, buffer, pAllocator); - lock.lock(); if (buffer != VK_NULL_HANDLE) { - PostCallRecordDestroyBuffer(dev_data, buffer, buffer_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyBuffer(dev_data, buffer, buffer_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyBuffer(device, buffer, pAllocator); } } @@ -3883,12 +3883,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyBufferView(VkDevice device, VkBufferView buffe // Validate state before calling down chain, update common data if we'll be calling down chain bool skip = PreCallValidateDestroyBufferView(dev_data, bufferView, &buffer_view_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyBufferView(device, bufferView, pAllocator); - lock.lock(); if (bufferView != VK_NULL_HANDLE) { - PostCallRecordDestroyBufferView(dev_data, bufferView, buffer_view_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyBufferView(dev_data, bufferView, buffer_view_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyBufferView(device, bufferView, pAllocator); } } @@ -3899,12 +3899,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyImage(VkDevice device, VkImage image, const Vk unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyImage(dev_data, image, &image_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyImage(device, image, pAllocator); - lock.lock(); if (image != VK_NULL_HANDLE) { - PostCallRecordDestroyImage(dev_data, image, image_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyImage(dev_data, image, image_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyImage(device, image, pAllocator); } } @@ -4283,12 +4283,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyImageView(VkDevice device, VkImageView imageVi unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyImageView(dev_data, imageView, &image_view_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyImageView(device, imageView, pAllocator); - lock.lock(); if (imageView != VK_NULL_HANDLE) { - PostCallRecordDestroyImageView(dev_data, imageView, image_view_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyImageView(dev_data, imageView, image_view_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyImageView(device, imageView, pAllocator); } } @@ -4297,6 +4297,7 @@ VKAPI_ATTR void VKAPI_CALL DestroyShaderModule(VkDevice device, VkShaderModule s layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); unique_lock_t lock(global_lock); + // Pre-record to avoid Destroy/Create race dev_data->shaderModuleMap.erase(shaderModule); lock.unlock(); @@ -4315,8 +4316,8 @@ static bool PreCallValidateDestroyPipeline(layer_data *dev_data, VkPipeline pipe return skip; } -static void PostCallRecordDestroyPipeline(layer_data *dev_data, VkPipeline pipeline, PIPELINE_STATE *pipeline_state, - VK_OBJECT obj_struct) { +static void PreCallRecordDestroyPipeline(layer_data *dev_data, VkPipeline pipeline, PIPELINE_STATE *pipeline_state, + VK_OBJECT obj_struct) { // Any bound cmd buffers are now invalid invalidateCommandBuffers(dev_data, pipeline_state->cb_bindings, obj_struct); dev_data->pipelineMap.erase(pipeline); @@ -4329,12 +4330,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyPipeline(VkDevice device, VkPipeline pipeline, unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyPipeline(dev_data, pipeline, &pipeline_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyPipeline(device, pipeline, pAllocator); - lock.lock(); if (pipeline != VK_NULL_HANDLE) { - PostCallRecordDestroyPipeline(dev_data, pipeline, pipeline_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyPipeline(dev_data, pipeline, pipeline_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyPipeline(device, pipeline, pAllocator); } } @@ -4342,6 +4343,7 @@ VKAPI_ATTR void VKAPI_CALL DestroyPipelineLayout(VkDevice device, VkPipelineLayo const VkAllocationCallbacks *pAllocator) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); unique_lock_t lock(global_lock); + // Pre-record to avoid Destroy/Create race dev_data->pipelineLayoutMap.erase(pipelineLayout); lock.unlock(); @@ -4360,8 +4362,8 @@ static bool PreCallValidateDestroySampler(layer_data *dev_data, VkSampler sample return skip; } -static void PostCallRecordDestroySampler(layer_data *dev_data, VkSampler sampler, SAMPLER_STATE *sampler_state, - VK_OBJECT obj_struct) { +static void PreCallRecordDestroySampler(layer_data *dev_data, VkSampler sampler, SAMPLER_STATE *sampler_state, + VK_OBJECT obj_struct) { // Any bound cmd buffers are now invalid if (sampler_state) invalidateCommandBuffers(dev_data, sampler_state->cb_bindings, obj_struct); dev_data->samplerMap.erase(sampler); @@ -4374,16 +4376,16 @@ VKAPI_ATTR void VKAPI_CALL DestroySampler(VkDevice device, VkSampler sampler, co unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroySampler(dev_data, sampler, &sampler_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroySampler(device, sampler, pAllocator); - lock.lock(); if (sampler != VK_NULL_HANDLE) { - PostCallRecordDestroySampler(dev_data, sampler, sampler_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroySampler(dev_data, sampler, sampler_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroySampler(device, sampler, pAllocator); } } -static void PostCallRecordDestroyDescriptorSetLayout(layer_data *dev_data, VkDescriptorSetLayout ds_layout) { +static void PreCallRecordDestroyDescriptorSetLayout(layer_data *dev_data, VkDescriptorSetLayout ds_layout) { auto layout_it = dev_data->descriptorSetLayoutMap.find(ds_layout); if (layout_it != dev_data->descriptorSetLayoutMap.end()) { layout_it->second.get()->MarkDestroyed(); @@ -4394,9 +4396,12 @@ static void PostCallRecordDestroyDescriptorSetLayout(layer_data *dev_data, VkDes VKAPI_ATTR void VKAPI_CALL DestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks *pAllocator) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); + { + lock_guard_t lock(global_lock); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyDescriptorSetLayout(dev_data, descriptorSetLayout); + } dev_data->dispatch_table.DestroyDescriptorSetLayout(device, descriptorSetLayout, pAllocator); - unique_lock_t lock(global_lock); - PostCallRecordDestroyDescriptorSetLayout(dev_data, descriptorSetLayout); } static bool PreCallValidateDestroyDescriptorPool(layer_data *dev_data, VkDescriptorPool pool, @@ -4434,6 +4439,7 @@ VKAPI_ATTR void VKAPI_CALL DestroyDescriptorPool(VkDevice device, VkDescriptorPo unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyDescriptorPool(dev_data, descriptorPool, &desc_pool_state, &obj_struct); if (!skip) { + // Pre-record to avoid Destroy/Create race PreCallRecordDestroyDescriptorPool(dev_data, descriptorPool, desc_pool_state, obj_struct); lock.unlock(); dev_data->dispatch_table.DestroyDescriptorPool(device, descriptorPool, pAllocator); @@ -4575,6 +4581,7 @@ VKAPI_ATTR void VKAPI_CALL DestroyCommandPool(VkDevice device, VkCommandPool com unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyCommandPool(dev_data, commandPool); if (!skip) { + // Pre-record to avoid Destroy/Create race PreCallRecordDestroyCommandPool(dev_data, commandPool); lock.unlock(); dev_data->dispatch_table.DestroyCommandPool(device, commandPool, pAllocator); @@ -4675,8 +4682,8 @@ static bool PreCallValidateDestroyFramebuffer(layer_data *dev_data, VkFramebuffe return skip; } -static void PostCallRecordDestroyFramebuffer(layer_data *dev_data, VkFramebuffer framebuffer, FRAMEBUFFER_STATE *framebuffer_state, - VK_OBJECT obj_struct) { +static void PreCallRecordDestroyFramebuffer(layer_data *dev_data, VkFramebuffer framebuffer, FRAMEBUFFER_STATE *framebuffer_state, + VK_OBJECT obj_struct) { invalidateCommandBuffers(dev_data, framebuffer_state->cb_bindings, obj_struct); dev_data->frameBufferMap.erase(framebuffer); } @@ -4688,12 +4695,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyFramebuffer(VkDevice device, VkFramebuffer fra unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyFramebuffer(dev_data, framebuffer, &framebuffer_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyFramebuffer(device, framebuffer, pAllocator); - lock.lock(); if (framebuffer != VK_NULL_HANDLE) { - PostCallRecordDestroyFramebuffer(dev_data, framebuffer, framebuffer_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyFramebuffer(dev_data, framebuffer, framebuffer_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyFramebuffer(device, framebuffer, pAllocator); } } @@ -4709,8 +4716,8 @@ static bool PreCallValidateDestroyRenderPass(layer_data *dev_data, VkRenderPass return skip; } -static void PostCallRecordDestroyRenderPass(layer_data *dev_data, VkRenderPass render_pass, RENDER_PASS_STATE *rp_state, - VK_OBJECT obj_struct) { +static void PreCallRecordDestroyRenderPass(layer_data *dev_data, VkRenderPass render_pass, RENDER_PASS_STATE *rp_state, + VK_OBJECT obj_struct) { invalidateCommandBuffers(dev_data, rp_state->cb_bindings, obj_struct); dev_data->renderPassMap.erase(render_pass); } @@ -4722,12 +4729,12 @@ VKAPI_ATTR void VKAPI_CALL DestroyRenderPass(VkDevice device, VkRenderPass rende unique_lock_t lock(global_lock); bool skip = PreCallValidateDestroyRenderPass(dev_data, renderPass, &rp_state, &obj_struct); if (!skip) { - lock.unlock(); - dev_data->dispatch_table.DestroyRenderPass(device, renderPass, pAllocator); - lock.lock(); if (renderPass != VK_NULL_HANDLE) { - PostCallRecordDestroyRenderPass(dev_data, renderPass, rp_state, obj_struct); + // Pre-record to avoid Destroy/Create race + PreCallRecordDestroyRenderPass(dev_data, renderPass, rp_state, obj_struct); } + lock.unlock(); + dev_data->dispatch_table.DestroyRenderPass(device, renderPass, pAllocator); } } @@ -4886,6 +4893,7 @@ VKAPI_ATTR VkResult VKAPI_CALL CreatePipelineCache(VkDevice device, const VkPipe VKAPI_ATTR void VKAPI_CALL DestroyPipelineCache(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks *pAllocator) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); + // Pre-record to avoid Destroy/Create race (if/when implemented) dev_data->dispatch_table.DestroyPipelineCache(device, pipelineCache, pAllocator); } @@ -5870,9 +5878,9 @@ static bool PreCallValidateFreeDescriptorSets(const layer_data *dev_data, VkDesc } return skip; } -// Sets have been removed from the pool so update underlying state -static void PostCallRecordFreeDescriptorSets(layer_data *dev_data, VkDescriptorPool pool, uint32_t count, - const VkDescriptorSet *descriptor_sets) { +// Sets are being returned to the pool so update the pool state +static void PreCallRecordFreeDescriptorSets(layer_data *dev_data, VkDescriptorPool pool, uint32_t count, + const VkDescriptorSet *descriptor_sets) { DESCRIPTOR_POOL_STATE *pool_state = GetDescriptorPoolState(dev_data, pool); // Update available descriptor sets in pool pool_state->availableSets += count; @@ -5899,14 +5907,15 @@ VKAPI_ATTR VkResult VKAPI_CALL FreeDescriptorSets(VkDevice device, VkDescriptorP // Make sure that no sets being destroyed are in-flight unique_lock_t lock(global_lock); bool skip = PreCallValidateFreeDescriptorSets(dev_data, descriptorPool, count, pDescriptorSets); - lock.unlock(); - if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; - VkResult result = dev_data->dispatch_table.FreeDescriptorSets(device, descriptorPool, count, pDescriptorSets); - if (VK_SUCCESS == result) { - lock.lock(); - PostCallRecordFreeDescriptorSets(dev_data, descriptorPool, count, pDescriptorSets); + VkResult result; + if (skip) { + result = VK_ERROR_VALIDATION_FAILED_EXT; + } else { + // A race here is invalid (descriptorPool should be externally sync'd), but code defensively against an invalid race + PreCallRecordFreeDescriptorSets(dev_data, descriptorPool, count, pDescriptorSets); lock.unlock(); + result = dev_data->dispatch_table.FreeDescriptorSets(device, descriptorPool, count, pDescriptorSets); } return result; } @@ -10863,6 +10872,7 @@ VKAPI_ATTR void VKAPI_CALL DestroySwapchainKHR(VkDevice device, VkSwapchainKHR s unique_lock_t lock(global_lock); auto swapchain_data = GetSwapchainNode(dev_data, swapchain); if (swapchain_data) { + // Pre-record to avoid Destroy/Create race if (swapchain_data->images.size() > 0) { for (auto swapchain_image : swapchain_data->images) { auto image_sub = dev_data->imageSubresourceMap.find(swapchain_image); @@ -11548,7 +11558,10 @@ VKAPI_ATTR void VKAPI_CALL DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR s HandleToUint64(instance), VALIDATION_ERROR_26c009e4, "vkDestroySurfaceKHR() called before its associated VkSwapchainKHR was destroyed."); } + + // Pre-record to avoid Destroy/Create race instance_data->surface_map.erase(surface); + lock.unlock(); if (!skip) { instance_data->dispatch_table.DestroySurfaceKHR(instance, surface, pAllocator); @@ -12346,6 +12359,7 @@ VKAPI_ATTR void VKAPI_CALL DestroyDescriptorUpdateTemplate(VkDevice device, VkDe const VkAllocationCallbacks *pAllocator) { layer_data *device_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); unique_lock_t lock(global_lock); + // Pre-record to avoid Destroy/Create race PreCallRecordDestroyDescriptorUpdateTemplate(device_data, descriptorUpdateTemplate); lock.unlock(); device_data->dispatch_table.DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator); @@ -12356,6 +12370,7 @@ VKAPI_ATTR void VKAPI_CALL DestroyDescriptorUpdateTemplateKHR(VkDevice device, const VkAllocationCallbacks *pAllocator) { layer_data *device_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); unique_lock_t lock(global_lock); + // Pre-record to avoid Destroy/Create race PreCallRecordDestroyDescriptorUpdateTemplate(device_data, descriptorUpdateTemplate); lock.unlock(); device_data->dispatch_table.DestroyDescriptorUpdateTemplateKHR(device, descriptorUpdateTemplate, pAllocator); -- cgit v1.2.3