layers: Add CommandBufferPool validation to MemTracker
diff --git a/layers/mem_tracker.cpp b/layers/mem_tracker.cpp
index bc901cc..4cbfb7d 100644
--- a/layers/mem_tracker.cpp
+++ b/layers/mem_tracker.cpp
@@ -53,22 +53,23 @@
static const VkDeviceMemory MEMTRACKER_SWAP_CHAIN_IMAGE_KEY = (VkDeviceMemory)(-1);
struct layer_data {
- debug_report_data *report_data;
- std::vector<VkDbgMsgCallback> logging_callback;
- VkLayerDispatchTable* device_dispatch_table;
- VkLayerInstanceDispatchTable* instance_dispatch_table;
- VkBool32 wsi_enabled;
- uint64_t currentFenceId;
+ debug_report_data *report_data;
+ std::vector<VkDbgMsgCallback> logging_callback;
+ VkLayerDispatchTable *device_dispatch_table;
+ VkLayerInstanceDispatchTable *instance_dispatch_table;
+ VkBool32 wsi_enabled;
+ uint64_t currentFenceId;
// Maps for tracking key structs related to MemTracker state
- unordered_map<VkCommandBuffer, MT_CB_INFO> cbMap;
- unordered_map<VkDeviceMemory, MT_MEM_OBJ_INFO> memObjMap;
- unordered_map<VkFence, MT_FENCE_INFO> fenceMap; // Map fence to fence info
- unordered_map<VkQueue, MT_QUEUE_INFO> queueMap;
- unordered_map<VkSwapchainKHR, MT_SWAP_CHAIN_INFO*> swapchainMap;
- unordered_map<VkSemaphore, MtSemaphoreState> semaphoreMap;
+ unordered_map<VkCommandBuffer, MT_CB_INFO> cbMap;
+ unordered_map<VkCommandPool, MT_CMD_POOL_INFO> commandPoolMap;
+ unordered_map<VkDeviceMemory, MT_MEM_OBJ_INFO> memObjMap;
+ unordered_map<VkFence, MT_FENCE_INFO> fenceMap;
+ unordered_map<VkQueue, MT_QUEUE_INFO> queueMap;
+ unordered_map<VkSwapchainKHR, MT_SWAP_CHAIN_INFO*> swapchainMap;
+ unordered_map<VkSemaphore, MtSemaphoreState> semaphoreMap;
// Images and Buffers are 2 objects that can have memory bound to them so they get special treatment
- unordered_map<uint64_t, MT_OBJ_BINDING_INFO> imageMap;
- unordered_map<uint64_t, MT_OBJ_BINDING_INFO> bufferMap;
+ unordered_map<uint64_t, MT_OBJ_BINDING_INFO> imageMap;
+ unordered_map<uint64_t, MT_OBJ_BINDING_INFO> bufferMap;
layer_data() :
report_data(nullptr),
@@ -83,6 +84,8 @@
static VkPhysicalDeviceMemoryProperties memProps;
+static VkBool32 clear_cmd_buf_and_mem_references(layer_data* my_data, const VkCommandBuffer cb);
+
// TODO : This can be much smarter, using separate locks for separate global data
static int globalLockInitialized = 0;
static loader_platform_thread_mutex globalLock;
@@ -140,9 +143,23 @@
// Add new CBInfo for this cb to map container
static void add_cmd_buf_info(layer_data* my_data,
- const VkCommandBuffer cb)
+ VkCommandPool commandPool, const VkCommandBuffer cb)
{
my_data->cbMap[cb].commandBuffer = cb;
+ my_data->commandPoolMap[commandPool].pCommandBuffers.push_front(cb);
+}
+
+// Delete CBInfo from container and clear mem references to CB
+static VkBool32 delete_cmd_buf_info(layer_data *my_data, VkCommandPool commandPool, const VkCommandBuffer cb)
+{
+ VkBool32 result = VK_TRUE;
+ result = clear_cmd_buf_and_mem_references(my_data, cb);
+ // Delete the CBInfo info
+ if (result == VK_TRUE) {
+ my_data->commandPoolMap[commandPool].pCommandBuffers.remove(cb);
+ my_data->cbMap.erase(cb);
+ }
+ return result;
}
// Return ptr to Info in CB map, or NULL if not found
@@ -1810,21 +1827,119 @@
}
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers(
- VkDevice device,
+ VkDevice device,
const VkCommandBufferAllocateInfo *pCreateInfo,
- VkCommandBuffer *pCommandBuffer)
+ VkCommandBuffer *pCommandBuffer)
{
layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
- VkResult result = my_data->device_dispatch_table->AllocateCommandBuffers(device, pCreateInfo, pCommandBuffer);
- // At time of cmd buffer creation, create global cmd buffer info for the returned cmd buffer
+ VkResult result = my_data->device_dispatch_table->AllocateCommandBuffers(device, pCreateInfo, pCommandBuffer);
+
loader_platform_thread_lock_mutex(&globalLock);
- if (*pCommandBuffer)
- add_cmd_buf_info(my_data, *pCommandBuffer);
+ if (VK_SUCCESS == result) { for (uint32_t i = 0; i < pCreateInfo->bufferCount; i++) { add_cmd_buf_info(my_data, pCreateInfo->commandPool, pCommandBuffer[i]);
+ }
+ }
printCBList(my_data, device);
loader_platform_thread_unlock_mutex(&globalLock);
return result;
}
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers(
+ VkDevice device,
+ VkCommandPool commandPool,
+ uint32_t commandBufferCount,
+ const VkCommandBuffer *pCommandBuffers)
+{
+ VkBool32 skipCall = VK_FALSE;
+ layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+
+ loader_platform_thread_lock_mutex(&globalLock);
+ for (uint32_t i = 0; i < commandBufferCount; i++) {
+ skipCall |= delete_cmd_buf_info(my_data, commandPool, pCommandBuffers[i]);
+ }
+ printCBList(my_data, device);
+ loader_platform_thread_unlock_mutex(&globalLock);
+
+ if (VK_FALSE == skipCall) {
+ my_data->device_dispatch_table->FreeCommandBuffers(device, commandPool, commandBufferCount, pCommandBuffers);
+ }
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool(
+ VkDevice device,
+ const VkCommandPoolCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkCommandPool* pCommandPool)
+{
+ layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ my_data->device_dispatch_table->CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
+
+ loader_platform_thread_lock_mutex(&globalLock);
+ // Add cmd pool to map
+ my_data->commandPoolMap[*pCommandPool].createFlags = pCreateInfo->flags;
+ loader_platform_thread_unlock_mutex(&globalLock);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyCommandPool(
+ VkDevice device,
+ VkCommandPool commandPool,
+ const VkAllocationCallbacks* pAllocator)
+{
+ VkBool32 commandBufferComplete = VK_FALSE;
+ VkBool32 skipCall = VK_FALSE;
+ // Verify that command buffers in pool are complete (not in-flight)
+ layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ for (auto it = my_data->commandPoolMap[commandPool].pCommandBuffers.begin();
+ it != my_data->commandPoolMap[commandPool].pCommandBuffers.end(); it++) {
+ commandBufferComplete = VK_FALSE;
+ skipCall = checkCBCompleted(my_data, *it, &commandBufferComplete);
+ if (VK_FALSE == commandBufferComplete) {
+ skipCall |= log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)(*it), 0,
+ MEMTRACK_RESET_CB_WHILE_IN_FLIGHT, "MEM", "Destroying Command Pool 0x%" PRIxLEAST64 " before "
+ "its command buffer (0x%" PRIxLEAST64 ") has completed.", reinterpret_cast<uint64_t>(commandPool),
+ reinterpret_cast<uint64_t>(*it));
+ }
+ }
+
+ if (VK_FALSE == skipCall) {
+ my_data->device_dispatch_table->DestroyCommandPool(device, commandPool, pAllocator);
+ }
+
+ auto item = my_data->commandPoolMap[commandPool].pCommandBuffers.begin();
+ // Remove command buffers from command buffer map
+ while (item != my_data->commandPoolMap[commandPool].pCommandBuffers.end()) {
+ auto del_item = item++;
+ delete_cmd_buf_info(my_data, commandPool, *del_item);
+ }
+ my_data->commandPoolMap.erase(commandPool);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandPool(
+ VkDevice device,
+ VkCommandPool commandPool,
+ VkCommandPoolResetFlags flags)
+{
+ layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+ VkBool32 commandBufferComplete = VK_FALSE;
+ VkBool32 skipCall = VK_FALSE;
+
+ // TODO: Check the commandPool's flags to see if reset is available for this pool.
+
+ auto it = my_data->commandPoolMap[commandPool].pCommandBuffers.begin();
+ // Verify that CB's in pool are complete (not in-flight)
+ while (it != my_data->commandPoolMap[commandPool].pCommandBuffers.end()) {
+ skipCall = checkCBCompleted(my_data, (*it), &commandBufferComplete);
+ if (VK_FALSE == commandBufferComplete) {
+ skipCall |= log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)(*it), 0,
+ MEMTRACK_RESET_CB_WHILE_IN_FLIGHT, "MEM", "Resetting CB %p before it has completed. You must check CB "
+ "flag before calling vkResetCommandBuffer().", (*it));
+ } else {
+ // Clear memory references at this point.
+ skipCall |= clear_cmd_buf_and_mem_references(my_data, (*it));
+ }
+ }
+
+}
+
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkBeginCommandBuffer(
VkCommandBuffer commandBuffer,
const VkCommandBufferBeginInfo *pBeginInfo)
@@ -1834,6 +1949,7 @@
VkBool32 skipCall = VK_FALSE;
VkBool32 commandBufferComplete = VK_FALSE;
loader_platform_thread_lock_mutex(&globalLock);
+
// This implicitly resets the Cmd Buffer so make sure any fence is done and then clear memory references
skipCall = checkCBCompleted(my_data, commandBuffer, &commandBufferComplete);
@@ -1870,6 +1986,9 @@
VkBool32 skipCall = VK_FALSE;
VkBool32 commandBufferComplete = VK_FALSE;
loader_platform_thread_lock_mutex(&globalLock);
+
+ // TODO: Validate that this cmdBuffer's command pool allows reset
+
// Verify that CB is complete (not in-flight)
skipCall = checkCBCompleted(my_data, commandBuffer, &commandBufferComplete);
if (VK_FALSE == commandBufferComplete) {
@@ -1885,6 +2004,7 @@
}
return result;
}
+
// TODO : For any vkCmdBind* calls that include an object which has mem bound to it,
// need to account for that mem now having binding to given commandBuffer
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdBindPipeline(
@@ -2712,6 +2832,14 @@
return (PFN_vkVoidFunction) vkCreateBufferView;
if (!strcmp(funcName, "vkAllocateCommandBuffers"))
return (PFN_vkVoidFunction) vkAllocateCommandBuffers;
+ if (!strcmp(funcName, "vkFreeCommandBuffers"))
+ return (PFN_vkVoidFunction) vkFreeCommandBuffers;
+ if (!strcmp(funcName, "vkCreateCommandPool"))
+ return (PFN_vkVoidFunction) vkCreateCommandPool;
+ if (!strcmp(funcName, "vkDestroyCommandPool"))
+ return (PFN_vkVoidFunction) vkDestroyCommandPool;
+ if (!strcmp(funcName, "vkResetCommandPool"))
+ return (PFN_vkVoidFunction) vkResetCommandPool;
if (!strcmp(funcName, "vkBeginCommandBuffer"))
return (PFN_vkVoidFunction) vkBeginCommandBuffer;
if (!strcmp(funcName, "vkEndCommandBuffer"))
diff --git a/layers/mem_tracker.h b/layers/mem_tracker.h
index 1963b38..cfc3342 100644
--- a/layers/mem_tracker.h
+++ b/layers/mem_tracker.h
@@ -110,9 +110,8 @@
VkMemoryAllocateInfo allocInfo;
list<MT_OBJ_HANDLE_TYPE> pObjBindings; // list container of objects bound to this memory
list<VkCommandBuffer> pCommandBufferBindings; // list container of cmd buffers that reference this mem object
- list<VkCommandBuffer> pCmdBufferBindings; // list container of cmd buffers that reference this mem object
MemRange memRange;
- void *pData, *pDriverData;
+ void *pData, *pDriverData;
};
// This only applies to Buffers and Images, which can have memory bound to them
@@ -126,10 +125,10 @@
// Track all command buffers
typedef struct _MT_CB_INFO {
- VkCommandBufferAllocateInfo createInfo;
+ VkCommandBufferAllocateInfo createInfo;
VkPipeline pipelines[VK_PIPELINE_BIND_POINT_RANGE_SIZE];
uint32_t attachmentCount;
- VkCommandBuffer commandBuffer;
+ VkCommandBuffer commandBuffer;
uint64_t fenceId;
VkFence lastSubmittedFence;
VkQueue lastSubmittedQueue;
@@ -139,10 +138,17 @@
_MT_CB_INFO():createInfo{},pipelines{},attachmentCount(0),fenceId(0),lastSubmittedFence{},lastSubmittedQueue{} {};
} MT_CB_INFO;
+
+// Track command pools and their command buffers
+typedef struct _MT_CMD_POOL_INFO {
+ VkCommandPoolCreateFlags createFlags;
+ list<VkCommandBuffer> pCommandBuffers; // list container of cmd buffers allocated from this pool
+} MT_CMD_POOL_INFO;
+
// Associate fenceId with a fence object
struct MT_FENCE_INFO {
- uint64_t fenceId; // Sequence number for fence at last submit
- VkQueue queue; // Queue that this fence is submitted against or NULL
+ uint64_t fenceId; // Sequence number for fence at last submit
+ VkQueue queue; // Queue that this fence is submitted against or NULL
VkFenceCreateInfo createInfo;
};
@@ -150,7 +156,7 @@
struct MT_QUEUE_INFO {
uint64_t lastRetiredId;
uint64_t lastSubmittedId;
- list<VkCommandBuffer> pQueueCommandBuffers;
+ list<VkCommandBuffer> pQueueCommandBuffers;
list<VkDeviceMemory> pMemRefList;
};