layers: MR82, Add lifetime validation for buffers to DrawState
Conflicts:
layers/draw_state.cpp
layers/draw_state.h
diff --git a/layers/draw_state.cpp b/layers/draw_state.cpp
index a43e89f..8cc5ff0 100644
--- a/layers/draw_state.cpp
+++ b/layers/draw_state.cpp
@@ -100,7 +100,7 @@
unordered_map<VkImageView, unique_ptr<VkImageViewCreateInfo>> imageViewMap;
unordered_map<VkImage, unique_ptr<VkImageCreateInfo>> imageMap;
unordered_map<VkBufferView, unique_ptr<VkBufferViewCreateInfo>> bufferViewMap;
- unordered_map<VkBuffer, unique_ptr<VkBufferCreateInfo>> bufferMap;
+ unordered_map<VkBuffer, BUFFER_NODE> bufferMap;
unordered_map<VkPipeline, PIPELINE_NODE*> pipelineMap;
unordered_map<VkCommandPool, CMD_POOL_INFO> commandPoolMap;
unordered_map<VkDescriptorPool, DESCRIPTOR_POOL_NODE*> descriptorPoolMap;
@@ -108,6 +108,9 @@
unordered_map<VkDescriptorSetLayout, LAYOUT_NODE*> descriptorSetLayoutMap;
unordered_map<VkPipelineLayout, PIPELINE_LAYOUT_NODE> pipelineLayoutMap;
unordered_map<VkDeviceMemory, VkImage> memImageMap;
+ unordered_map<VkFence, FENCE_NODE> fenceMap;
+ unordered_map<VkQueue, QUEUE_NODE> queueMap;
+ unordered_map<VkDevice, DEVICE_NODE> deviceMap;
// Map for layout chains
unordered_map<void*, GLOBAL_CB_NODE*> commandBufferMap;
unordered_map<VkFramebuffer, VkFramebufferCreateInfo*> frameBufferMap;
@@ -1357,7 +1360,7 @@
if ((pWDS->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) ||
(pWDS->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)) {
for (uint32_t j=0; j<pWDS->descriptorCount; ++j) {
- bufferSize = my_data->bufferMap[pWDS->pBufferInfo[j].buffer]->size;
+ bufferSize = my_data->bufferMap[pWDS->pBufferInfo[j].buffer].create_info->size;
if ((pSet->dynamicOffsets[dynOffsetIndex] + pWDS->pBufferInfo[j].offset + pWDS->pBufferInfo[j].range) > bufferSize) {
result |= log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, (uint64_t)pSet->set, __LINE__, DRAWSTATE_DYNAMIC_OFFSET_OVERFLOW, "DS",
"VkDescriptorSet (%#" PRIxLEAST64 ") bound as set #%u has dynamic offset %u. Combined with offet %#" PRIxLEAST64 " and range %#" PRIxLEAST64 " from its update, this oversteps its buffer (%#" PRIxLEAST64 ") which has a size of %#" PRIxLEAST64 ".",
@@ -1411,7 +1414,7 @@
if (pPipe->vtxBindingCount > 0) {
VkPipelineVertexInputStateCreateInfo *vtxInCI = &pPipe->vertexInputCI;
for (uint32_t i = 0; i < vtxInCI->vertexBindingDescriptionCount; i++) {
- if ((pCB->boundVtxBuffers.size() < (i+1)) || (pCB->boundVtxBuffers[i] == VK_NULL_HANDLE)) {
+ if ((pCB->currentDrawData.buffers.size() < (i+1)) || (pCB->currentDrawData.buffers[i] == VK_NULL_HANDLE)) {
result |= log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT) 0, 0, __LINE__, DRAWSTATE_VTX_INDEX_OUT_OF_BOUNDS, "DS",
"The Pipeline State Object (%#" PRIxLEAST64 ") expects that this Command Buffer's vertex binding Index %d should be set via vkCmdBindVertexBuffers.",
(uint64_t)pCB->lastBoundPipeline, i);
@@ -1419,7 +1422,7 @@
}
}
} else {
- if (!pCB->boundVtxBuffers.empty()) {
+ if (!pCB->currentDrawData.buffers.empty()) {
result |= log_msg(my_data->report_data, VK_DEBUG_REPORT_PERF_WARN_BIT_EXT, (VkDebugReportObjectTypeEXT) 0, 0, __LINE__, DRAWSTATE_VTX_INDEX_OUT_OF_BOUNDS,
"DS", "Vertex buffers are bound to command buffer (%#" PRIxLEAST64 ") but no vertex buffers are attached to this Pipeline State Object (%#" PRIxLEAST64 ").",
(uint64_t)pCB->commandBuffer, (uint64_t)pCB->lastBoundPipeline);
@@ -2518,8 +2521,9 @@
pCB->activeSubpass = 0;
pCB->framebuffer = 0;
pCB->boundDescriptorSets.clear();
+ pCB->drawData.clear();
+ pCB->currentDrawData.buffers.clear();
pCB->imageLayoutMap.clear();
- pCB->boundVtxBuffers.clear();
}
}
@@ -2969,6 +2973,84 @@
return skip_call;
}
+bool validateAndIncrementResources(layer_data* my_data, GLOBAL_CB_NODE* pCB) {
+ bool skip_call = false;
+ for (auto drawDataElement : pCB->drawData) {
+ for (auto buffer : drawDataElement.buffers) {
+ auto buffer_data = my_data->bufferMap.find(buffer);
+ if (buffer_data == my_data->bufferMap.end()) {
+ skip_call |= log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, 0, DRAWSTATE_INVALID_BUFFER, "DS",
+ "Cannot submit cmd buffer using deleted buffer %" PRIu64 ".", reinterpret_cast<uint64_t>(buffer));
+ } else {
+ buffer_data->second.in_use.fetch_add(1);
+ }
+ }
+ }
+}
+
+void decrementResources(layer_data* my_data, VkCommandBuffer cmdBuffer) {
+ GLOBAL_CB_NODE* pCB = getCBNode(my_data, cmdBuffer);
+ for (auto drawDataElement : pCB->drawData) {
+ for (auto buffer : drawDataElement.buffers) {
+ auto buffer_data = my_data->bufferMap.find(buffer);
+ if (buffer_data != my_data->bufferMap.end()) {
+ buffer_data->second.in_use.fetch_sub(1);
+ }
+ }
+ }
+}
+
+void decrementResources(layer_data* my_data, uint32_t fenceCount, const VkFence* pFences) {
+ for (uint32_t i = 0; i < fenceCount; ++i) {
+ 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;
+ if (fence_data->second.priorFence != VK_NULL_HANDLE) {
+ decrementResources(my_data, 1, &fence_data->second.priorFence);
+ }
+ for (auto cmdBuffer : fence_data->second.cmdBuffers) {
+ decrementResources(my_data, cmdBuffer);
+ }
+ }
+}
+
+void decrementResources(layer_data* my_data, VkQueue queue) {
+ auto queue_data = my_data->queueMap.find(queue);
+ if (queue_data != my_data->queueMap.end()) {
+ for (auto cmdBuffer : queue_data->second.untrackedCmdBuffers) {
+ decrementResources(my_data, cmdBuffer);
+ }
+ decrementResources(my_data, 1, &queue_data->second.priorFence);
+ }
+}
+
+void trackCommandBuffers(layer_data* my_data, VkQueue queue, uint32_t cmdBufferCount, const VkCommandBuffer* pCmdBuffers, VkFence fence) {
+ auto queue_data = my_data->queueMap.find(queue);
+ if (fence != VK_NULL_HANDLE) {
+ VkFence priorFence = VK_NULL_HANDLE;
+ 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);
+ }
+ queue_data->second.untrackedCmdBuffers.clear();
+ }
+ my_data->fenceMap[fence].cmdBuffers.clear();
+ my_data->fenceMap[fence].priorFence = priorFence;
+ my_data->fenceMap[fence].needsSignaled = true;
+ for (uint32_t i = 0; i < cmdBufferCount; ++i) {
+ my_data->fenceMap[fence].cmdBuffers.push_back(pCmdBuffers[i]);
+ }
+ } else {
+ if (queue_data != my_data->queueMap.end()) {
+ for (uint32_t i = 0; i < cmdBufferCount; ++i) {
+ queue_data->second.untrackedCmdBuffers.push_back(pCmdBuffers[i]);
+ }
+ }
+ }
+}
+
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence)
{
VkBool32 skipCall = VK_FALSE;
@@ -2988,6 +3070,7 @@
pCB = getCBNode(dev_data, submit->pCommandBuffers[i]);
loader_platform_thread_lock_mutex(&globalLock);
pCB->submitCount++; // increment submit count
+ skipCall |= validateAndIncrementResources(dev_data, pCB);
if ((pCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) && (pCB->submitCount > 1)) {
skipCall |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_COMMAND_BUFFER_SINGLE_SUBMIT_VIOLATION, "DS",
"CB %#" PRIxLEAST64 " was begun w/ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT set, but has been submitted %#" PRIxLEAST64 " times.",
@@ -3002,12 +3085,61 @@
}
loader_platform_thread_unlock_mutex(&globalLock);
}
+ trackCommandBuffers(dev_data, queue, submit->commandBufferCount, submit->pCommandBuffers, fence);
}
if (VK_FALSE == skipCall)
return dev_data->device_dispatch_table->QueueSubmit(queue, submitCount, pSubmits, fence);
return VK_ERROR_VALIDATION_FAILED_EXT;
}
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkWaitForFences(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout)
+{
+ layer_data* dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ VkResult result = dev_data->device_dispatch_table->WaitForFences(device, fenceCount, pFences, waitAll, timeout);
+ if ((waitAll || fenceCount == 1) && result == VK_SUCCESS) {
+ decrementResources(dev_data, fenceCount, pFences);
+ }
+ return result;
+}
+
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus(VkDevice device, VkFence fence)
+{
+ layer_data* dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ VkResult result = dev_data->device_dispatch_table->GetFenceStatus(device, fence);
+ if (result == VK_SUCCESS) {
+ decrementResources(dev_data, 1, &fence);
+ }
+ return result;
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue)
+{
+ layer_data* dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ dev_data->device_dispatch_table->GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
+ dev_data->deviceMap[device].queues.push_back(*pQueue);
+ dev_data->queueMap[*pQueue].device = device;
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle(VkQueue queue)
+{
+ layer_data* dev_data = get_my_data_ptr(get_dispatch_key(queue), layer_data_map);
+ decrementResources(dev_data, queue);
+ return dev_data->device_dispatch_table->QueueWaitIdle(queue);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle(VkDevice device)
+{
+ layer_data* dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ auto device_data = dev_data->deviceMap.find(device);
+ if (device_data != dev_data->deviceMap.end()) {
+ for (auto queue : device_data->second.queues) {
+ decrementResources(dev_data, queue);
+ }
+ }
+ return dev_data->device_dispatch_table->DeviceWaitIdle(device);
+}
+
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator)
{
get_my_data_ptr(get_dispatch_key(device), layer_data_map)->device_dispatch_table->DestroyFence(device, fence, pAllocator);
@@ -3032,10 +3164,29 @@
// TODO : Clean up any internal data structures using this obj.
}
+bool validateIdleBuffer(const layer_data* my_data, VkBuffer buffer) {
+ bool skip_call = false;
+ auto buffer_data = my_data->bufferMap.find(buffer);
+ if (buffer_data == my_data->bufferMap.end()) {
+ skip_call |= log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, 0, DRAWSTATE_DOUBLE_DESTROY, "DS",
+ "Cannot free buffer %" PRIxLEAST64 " that has not been allocated.", reinterpret_cast<uint64_t>(buffer));
+ } else {
+ if (buffer_data->second.in_use.load()) {
+ skip_call |= log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, 0, DRAWSTATE_OBJECT_INUSE, "DS",
+ "Cannot free buffer %" PRIxLEAST64 " that is in use by a command buffer.", reinterpret_cast<uint64_t>(buffer));
+
+ }
+ }
+ return skip_call;
+}
+
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator)
{
layer_data* dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
- dev_data->device_dispatch_table->DestroyBuffer(device, buffer, pAllocator);
+ bool skip_call = false;
+ if (!validateIdleBuffer(dev_data, buffer)) {
+ dev_data->device_dispatch_table->DestroyBuffer(device, buffer, pAllocator);
+ }
dev_data->bufferMap.erase(buffer);
}
@@ -3189,7 +3340,8 @@
if (VK_SUCCESS == result) {
loader_platform_thread_lock_mutex(&globalLock);
// TODO : This doesn't create deep copy of pQueueFamilyIndices so need to fix that if/when we want that data to be valid
- dev_data->bufferMap[*pBuffer] = unique_ptr<VkBufferCreateInfo>(new VkBufferCreateInfo(*pCreateInfo));
+ dev_data->bufferMap[*pBuffer].create_info = unique_ptr<VkBufferCreateInfo>(new VkBufferCreateInfo(*pCreateInfo));
+ dev_data->bufferMap[*pBuffer].in_use.store(0);
loader_platform_thread_unlock_mutex(&globalLock);
}
return result;
@@ -4077,6 +4229,20 @@
dev_data->device_dispatch_table->CmdBindIndexBuffer(commandBuffer, buffer, offset, indexType);
}
+void updateResourceTracking(GLOBAL_CB_NODE* pCB, uint32_t startBinding, uint32_t bindingCount, const VkBuffer* pBuffers) {
+ uint32_t end = startBinding + bindingCount;
+ if (pCB->currentDrawData.buffers.size() < end) {
+ pCB->currentDrawData.buffers.resize(end);
+ }
+ for (uint32_t i = 0; i < bindingCount; ++i) {
+ pCB->currentDrawData.buffers[i + startBinding] = pBuffers[i];
+ }
+}
+
+void updateResourceTrackingOnDraw(GLOBAL_CB_NODE* pCB) {
+ pCB->drawData.push_back(pCB->currentDrawData);
+}
+
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers(
VkCommandBuffer commandBuffer,
uint32_t firstBinding,
@@ -4089,12 +4255,10 @@
GLOBAL_CB_NODE* pCB = getCBNode(dev_data, commandBuffer);
if (pCB) {
addCmd(dev_data, pCB, CMD_BINDVERTEXBUFFER, "vkCmdBindVertexBuffer()");
- if ((firstBinding + bindingCount) > pCB->boundVtxBuffers.size()) {
- pCB->boundVtxBuffers.resize(firstBinding+bindingCount, VK_NULL_HANDLE);
- }
- for (auto i = 0; i < bindingCount; i++) {
- pCB->boundVtxBuffers[i+firstBinding] = pBuffers[i];
- }
+ pCB->lastVtxBinding = startBinding + bindingCount -1;
+ updateResourceTracking(pCB, startBinding, bindingCount, pBuffers);
+ } else {
+ skipCall |= report_error_no_cb_begin(dev_data, commandBuffer, "vkCmdBindVertexBuffer()");
}
if (VK_FALSE == skipCall)
dev_data->device_dispatch_table->CmdBindVertexBuffers(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets);
@@ -4113,6 +4277,9 @@
skipCall |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_INFO_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_NONE, "DS",
"vkCmdDraw() call #%" PRIu64 ", reporting DS state:", g_drawCount[DRAW]++);
skipCall |= synchAndPrintDSConfig(dev_data, commandBuffer);
+ if (VK_FALSE == skipCall) {
+ updateResourceTrackingOnDraw(pCB);
+ }
skipCall |= outsideRenderPass(dev_data, pCB, "vkCmdDraw");
}
if (VK_FALSE == skipCall)
@@ -4132,6 +4299,9 @@
skipCall |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_INFO_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_NONE, "DS",
"vkCmdDrawIndexed() call #%" PRIu64 ", reporting DS state:", g_drawCount[DRAW_INDEXED]++);
skipCall |= synchAndPrintDSConfig(dev_data, commandBuffer);
+ if (VK_FALSE == skipCall) {
+ updateResourceTrackingOnDraw(pCB);
+ }
skipCall |= outsideRenderPass(dev_data, pCB, "vkCmdDrawIndexed");
}
if (VK_FALSE == skipCall)
@@ -4151,6 +4321,9 @@
skipCall |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_INFO_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_NONE, "DS",
"vkCmdDrawIndirect() call #%" PRIu64 ", reporting DS state:", g_drawCount[DRAW_INDIRECT]++);
skipCall |= synchAndPrintDSConfig(dev_data, commandBuffer);
+ if (VK_FALSE == skipCall) {
+ updateResourceTrackingOnDraw(pCB);
+ }
skipCall |= outsideRenderPass(dev_data, pCB, "vkCmdDrawIndirect");
}
if (VK_FALSE == skipCall)
@@ -4170,6 +4343,9 @@
skipCall |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_INFO_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, 0, __LINE__, DRAWSTATE_NONE, "DS",
"vkCmdDrawIndexedIndirect() call #%" PRIu64 ", reporting DS state:", g_drawCount[DRAW_INDEXED_INDIRECT]++);
skipCall |= synchAndPrintDSConfig(dev_data, commandBuffer);
+ if (VK_FALSE == skipCall) {
+ updateResourceTrackingOnDraw(pCB);
+ }
skipCall |= outsideRenderPass(dev_data, pCB, "vkCmdDrawIndexedIndirect");
}
if (VK_FALSE == skipCall)
@@ -5596,6 +5772,12 @@
return (PFN_vkVoidFunction) vkDestroyDevice;
if (!strcmp(funcName, "vkQueueSubmit"))
return (PFN_vkVoidFunction) vkQueueSubmit;
+ if (!strcmp(funcName, "vkWaitForFences"))
+ return (PFN_vkVoidFunction) vkWaitForFences;
+ if (!strcmp(funcName, "vkGetFenceStatus"))
+ return (PFN_vkVoidFunction) vkGetFenceStatus;
+ if (!strcmp(funcName, "vkGetDeviceQueue"))
+ return (PFN_vkVoidFunction) vkGetDeviceQueue;
if (!strcmp(funcName, "vkDestroyInstance"))
return (PFN_vkVoidFunction) vkDestroyInstance;
if (!strcmp(funcName, "vkDestroyDevice"))