layers: Fix query tracking across multiple command buffers on the same queue
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp
index c7d9bc4..e8c4cc3 100644
--- a/layers/core_validation.cpp
+++ b/layers/core_validation.cpp
@@ -3675,6 +3675,7 @@
pCB->updateBuffers.clear();
clear_cmd_buf_and_mem_references(dev_data, pCB);
pCB->eventUpdates.clear();
+ pCB->queryUpdates.clear();
// Remove this cmdBuffer's reference from each FrameBuffer's CB ref list
for (auto framebuffer : pCB->framebuffers) {
@@ -4275,6 +4276,9 @@
for (auto eventStagePair : other_queue_data->second.eventToStageMap) {
queue_data->second.eventToStageMap[eventStagePair.first] = eventStagePair.second;
}
+ for (auto queryStatePair : other_queue_data->second.queryToStateMap) {
+ queue_data->second.queryToStateMap[queryStatePair.first] = queryStatePair.second;
+ }
}
// This is the core function for tracking command buffers. There are two primary ways command
@@ -4555,6 +4559,9 @@
for (auto &function : pCBNode->eventUpdates) {
skipCall |= function(queue);
}
+ for (auto &function : pCBNode->queryUpdates) {
+ skipCall |= function(queue);
+ }
}
}
}
@@ -7881,6 +7888,19 @@
pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
}
+bool setQueryState(VkQueue queue, VkCommandBuffer commandBuffer, QueryObject object, bool value) {
+ layer_data *dev_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map);
+ GLOBAL_CB_NODE *pCB = getCBNode(dev_data, commandBuffer);
+ if (pCB) {
+ pCB->queryToStateMap[object] = value;
+ }
+ auto queue_data = dev_data->queueMap.find(queue);
+ if (queue_data != dev_data->queueMap.end()) {
+ queue_data->second.queryToStateMap[object] = value;
+ }
+ return false;
+}
+
VKAPI_ATTR void VKAPI_CALL
CmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot, VkFlags flags) {
bool skipCall = false;
@@ -7915,7 +7935,8 @@
} else {
pCB->activeQueries.erase(query);
}
- pCB->queryToStateMap[query] = 1;
+ std::function<bool(VkQueue)> queryUpdate = std::bind(setQueryState, std::placeholders::_1, commandBuffer, query, 1);
+ pCB->queryUpdates.push_back(queryUpdate);
if (pCB->state == CB_RECORDING) {
skipCall |= addCmd(dev_data, pCB, CMD_ENDQUERY, "VkCmdEndQuery()");
} else {
@@ -7937,7 +7958,8 @@
for (uint32_t i = 0; i < queryCount; i++) {
QueryObject query = {queryPool, firstQuery + i};
pCB->waitedEventsBeforeQueryReset[query] = pCB->waitedEvents;
- pCB->queryToStateMap[query] = 0;
+ std::function<bool(VkQueue)> queryUpdate = std::bind(setQueryState, std::placeholders::_1, commandBuffer, query, 0);
+ pCB->queryUpdates.push_back(queryUpdate);
}
if (pCB->state == CB_RECORDING) {
skipCall |= addCmd(dev_data, pCB, CMD_RESETQUERYPOOL, "VkCmdResetQueryPool()");
@@ -7951,6 +7973,40 @@
dev_data->device_dispatch_table->CmdResetQueryPool(commandBuffer, queryPool, firstQuery, queryCount);
}
+bool validateQuery(VkQueue queue, GLOBAL_CB_NODE *pCB, VkQueryPool queryPool, uint32_t queryCount, uint32_t firstQuery) {
+ bool skip_call = false;
+ layer_data *dev_data = get_my_data_ptr(get_dispatch_key(pCB->commandBuffer), layer_data_map);
+ auto queue_data = dev_data->queueMap.find(queue);
+ if (queue_data == dev_data->queueMap.end())
+ return false;
+ for (uint32_t i = 0; i < queryCount; i++) {
+ QueryObject query = {queryPool, firstQuery + i};
+ auto query_data = queue_data->second.queryToStateMap.find(query);
+ bool fail = false;
+ if (query_data != queue_data->second.queryToStateMap.end()) {
+ if (!query_data->second) {
+ fail = true;
+ }
+ } else {
+ auto global_query_data = dev_data->queryToStateMap.find(query);
+ if (global_query_data != dev_data->queryToStateMap.end()) {
+ if (!global_query_data->second) {
+ fail = true;
+ }
+ } else {
+ fail = true;
+ }
+ }
+ if (fail) {
+ skip_call |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__,
+ DRAWSTATE_INVALID_QUERY, "DS",
+ "Requesting a copy from query to buffer with invalid query: queryPool 0x%" PRIx64 ", index %d",
+ reinterpret_cast<uint64_t &>(queryPool), firstQuery + i);
+ }
+ }
+ return skip_call;
+}
+
VKAPI_ATTR void VKAPI_CALL
CmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount,
VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) {
@@ -7976,15 +8032,9 @@
"vkCmdCopyQueryPoolResults()", "VK_BUFFER_USAGE_TRANSFER_DST_BIT");
#endif
if (pCB) {
- for (uint32_t i = 0; i < queryCount; i++) {
- QueryObject query = {queryPool, firstQuery + i};
- if (!pCB->queryToStateMap[query]) {
- skipCall |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0,
- __LINE__, DRAWSTATE_INVALID_QUERY, "DS",
- "Requesting a copy from query to buffer with invalid query: queryPool 0x%" PRIx64 ", index %d",
- (uint64_t)(queryPool), firstQuery + i);
- }
- }
+ std::function<bool(VkQueue)> queryUpdate =
+ std::bind(validateQuery, std::placeholders::_1, pCB, queryPool, queryCount, firstQuery);
+ pCB->queryUpdates.push_back(queryUpdate);
if (pCB->state == CB_RECORDING) {
skipCall |= addCmd(dev_data, pCB, CMD_COPYQUERYPOOLRESULTS, "vkCmdCopyQueryPoolResults()");
} else {
@@ -8103,7 +8153,8 @@
GLOBAL_CB_NODE *pCB = getCBNode(dev_data, commandBuffer);
if (pCB) {
QueryObject query = {queryPool, slot};
- pCB->queryToStateMap[query] = 1;
+ std::function<bool(VkQueue)> queryUpdate = std::bind(setQueryState, std::placeholders::_1, commandBuffer, query, 1);
+ pCB->queryUpdates.push_back(queryUpdate);
if (pCB->state == CB_RECORDING) {
skipCall |= addCmd(dev_data, pCB, CMD_WRITETIMESTAMP, "vkCmdWriteTimestamp()");
} else {
diff --git a/layers/core_validation.h b/layers/core_validation.h
index e44bbf8..d685734 100644
--- a/layers/core_validation.h
+++ b/layers/core_validation.h
@@ -250,6 +250,7 @@
#endif
std::vector<VkCommandBuffer> untrackedCmdBuffers;
std::unordered_map<VkEvent, VkPipelineStageFlags> eventToStageMap;
+ std::unordered_map<QueryObject, bool> queryToStateMap; // 0 is unavailable, 1 is available
};
class QUERY_POOL_NODE : public BASE_NODE {
diff --git a/layers/core_validation_types.h b/layers/core_validation_types.h
index 683eefc..c039418 100644
--- a/layers/core_validation_types.h
+++ b/layers/core_validation_types.h
@@ -439,6 +439,7 @@
std::vector<std::function<bool()>> validate_functions;
std::unordered_set<VkDeviceMemory> memObjs;
std::vector<std::function<bool(VkQueue)>> eventUpdates;
+ std::vector<std::function<bool(VkQueue)>> queryUpdates;
~GLOBAL_CB_NODE();
};
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 8aa1547..dc8f774 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -2784,6 +2784,116 @@
m_errorMonitor->VerifyNotFound();
}
+// This is a positive test. No errors should be generated.
+TEST_F(VkLayerTest, QueryAndCopyMultipleCommandBuffers) {
+ TEST_DESCRIPTION(
+ "Issue a query and copy from it on a second command buffer.");
+
+ if ((m_device->queue_props.empty()) ||
+ (m_device->queue_props[0].queueCount < 2))
+ return;
+
+ m_errorMonitor->ExpectSuccess();
+
+ VkQueryPool query_pool;
+ VkQueryPoolCreateInfo query_pool_create_info{};
+ query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+ query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP;
+ query_pool_create_info.queryCount = 1;
+ vkCreateQueryPool(m_device->device(), &query_pool_create_info, nullptr,
+ &query_pool);
+
+ VkCommandPool command_pool;
+ VkCommandPoolCreateInfo pool_create_info{};
+ pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
+ pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr,
+ &command_pool);
+
+ VkCommandBuffer command_buffer[2];
+ VkCommandBufferAllocateInfo command_buffer_allocate_info{};
+ command_buffer_allocate_info.sType =
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ command_buffer_allocate_info.commandPool = command_pool;
+ command_buffer_allocate_info.commandBufferCount = 2;
+ command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info,
+ command_buffer);
+
+ VkQueue queue = VK_NULL_HANDLE;
+ vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_,
+ 1, &queue);
+
+ uint32_t qfi = 0;
+ VkBufferCreateInfo buff_create_info = {};
+ buff_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ buff_create_info.size = 1024;
+ buff_create_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ buff_create_info.queueFamilyIndexCount = 1;
+ buff_create_info.pQueueFamilyIndices = &qfi;
+
+ VkResult err;
+ VkBuffer buffer;
+ err = vkCreateBuffer(m_device->device(), &buff_create_info, NULL, &buffer);
+ ASSERT_VK_SUCCESS(err);
+ VkMemoryAllocateInfo mem_alloc = {};
+ mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ mem_alloc.pNext = NULL;
+ mem_alloc.allocationSize = 1024;
+ mem_alloc.memoryTypeIndex = 0;
+
+ VkMemoryRequirements memReqs;
+ vkGetBufferMemoryRequirements(m_device->device(), buffer, &memReqs);
+ bool pass =
+ m_device->phy().set_memory_type(memReqs.memoryTypeBits, &mem_alloc, 0);
+ if (!pass) {
+ vkDestroyBuffer(m_device->device(), buffer, NULL);
+ return;
+ }
+
+ VkDeviceMemory mem;
+ err = vkAllocateMemory(m_device->device(), &mem_alloc, NULL, &mem);
+ ASSERT_VK_SUCCESS(err);
+ err = vkBindBufferMemory(m_device->device(), buffer, mem, 0);
+ ASSERT_VK_SUCCESS(err);
+
+ {
+ VkCommandBufferBeginInfo begin_info{};
+ begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ vkBeginCommandBuffer(command_buffer[0], &begin_info);
+
+ vkCmdResetQueryPool(command_buffer[0], query_pool, 0, 1);
+ vkCmdWriteTimestamp(command_buffer[0],
+ VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, query_pool, 0);
+
+ vkEndCommandBuffer(command_buffer[0]);
+
+ vkBeginCommandBuffer(command_buffer[1], &begin_info);
+
+ vkCmdCopyQueryPoolResults(command_buffer[1], query_pool, 0, 1, buffer,
+ 0, 0, 0);
+
+ vkEndCommandBuffer(command_buffer[1]);
+ }
+ {
+ VkSubmitInfo submit_info{};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 2;
+ submit_info.pCommandBuffers = command_buffer;
+ submit_info.signalSemaphoreCount = 0;
+ submit_info.pSignalSemaphores = nullptr;
+ vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
+ }
+
+ vkQueueWaitIdle(queue);
+
+ vkDestroyQueryPool(m_device->device(), query_pool, nullptr);
+ vkFreeCommandBuffers(m_device->device(), command_pool, 2, command_buffer);
+ vkDestroyCommandPool(m_device->device(), command_pool, NULL);
+
+ m_errorMonitor->VerifyNotFound();
+}
TEST_F(VkLayerTest, ResetEventThenSet) {
TEST_DESCRIPTION(