| /* Copyright (c) 2025 The Khronos Group Inc. |
| * Copyright (c) 2025 Valve Corporation |
| * Copyright (c) 2025 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "../framework/sync_val_tests.h" |
| |
| struct NegativeSyncValReporting : public VkSyncValTest {}; |
| |
| TEST_F(NegativeSyncValReporting, DebugRegion) { |
| TEST_DESCRIPTION("Prior access debug region reporting: single debug region"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| m_command_buffer.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| |
| m_errorMonitor->SetDesiredError("RegionA"); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DebugRegion2) { |
| TEST_DESCRIPTION("Prior access debug region reporting: two nested debug regions"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| m_command_buffer.Begin(); |
| |
| // Command buffer can start with end label command. It can close debug region |
| // started in the previous command buffer (that's why primary command buffer labels |
| // are validated at sumbit time). Start command buffer with EndLabel command to |
| // check it is handled properly. |
| // |
| // NOTE: the following command crashes CI Linux-Mesa-6800 driver but works |
| // in other configurations. Disabled for now. |
| // vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| |
| m_errorMonitor->SetDesiredError("RegionA::RegionB"); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DebugRegion3) { |
| TEST_DESCRIPTION( |
| "Prior access debug region reporting: there is a nested region but prior access happens in the top level region"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_d(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| m_command_buffer.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| m_command_buffer.Copy(buffer_a, buffer_d); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| |
| // this is where prior access happens for the reported hazard |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| |
| m_errorMonitor->SetDesiredError("RegionA"); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DebugRegion4) { |
| TEST_DESCRIPTION("Prior access debug region reporting: multiple nested debug regions"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::Event event(*m_device); |
| vkt::Event event2(*m_device); |
| |
| m_command_buffer.Begin(); |
| label.pLabelName = "VulkanFrame"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| |
| label.pLabelName = "ResetEvent"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| vk::CmdResetEvent(m_command_buffer, event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| |
| label.pLabelName = "FirstPass"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| |
| label.pLabelName = "CopyAToB"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| |
| label.pLabelName = "ResetEvent2"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| vk::CmdResetEvent(m_command_buffer, event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| |
| m_errorMonitor->SetDesiredError("VulkanFrame::FirstPass::CopyAToB"); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); // FirstPass |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); // VulkanFrame |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DebugRegion_Secondary) { |
| TEST_DESCRIPTION("Prior access debug region reporting: secondary command buffer"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer secondary_cb(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| secondary_cb.Begin(); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(secondary_cb, &label); |
| secondary_cb.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(secondary_cb); |
| secondary_cb.End(); |
| |
| m_command_buffer.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_cb.handle()); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| m_errorMonitor->SetDesiredError("RegionA::RegionB"); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DebugRegion_Secondary2) { |
| TEST_DESCRIPTION("Prior access debug region reporting: secondary command buffer first access validation"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer secondary_cb0(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| secondary_cb0.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(secondary_cb0, &label); |
| secondary_cb0.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(secondary_cb0); |
| secondary_cb0.End(); |
| |
| vkt::CommandBuffer secondary_cb1(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| secondary_cb1.Begin(); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(secondary_cb1, &label); |
| secondary_cb1.Copy(buffer_c, buffer_a); |
| vk::CmdEndDebugUtilsLabelEXT(secondary_cb1); |
| secondary_cb1.End(); |
| |
| m_command_buffer.Begin(); |
| m_errorMonitor->SetDesiredError("RegionA"); |
| VkCommandBuffer secondary_cbs[2] = {secondary_cb0, secondary_cb1}; |
| vk::CmdExecuteCommands(m_command_buffer, 2, secondary_cbs); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion) { |
| TEST_DESCRIPTION("Prior access debug region reporting: single debug region per command buffer"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| |
| label.pLabelName = "RegionA"; |
| cb0.Begin(); |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| cb0.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb0); |
| cb0.End(); |
| |
| label.pLabelName = "RegionB"; |
| cb1.Begin(); |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| cb1.Copy(buffer_c, buffer_a); |
| vk::CmdEndDebugUtilsLabelEXT(cb1); |
| cb1.End(); |
| |
| m_errorMonitor->SetDesiredError("RegionA"); |
| m_default_queue->Submit({cb0, cb1}); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion2) { |
| TEST_DESCRIPTION("Prior access debug region reporting: previous access is in the previous submission"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| label.pLabelName = "RegionA"; |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| cb0.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb0); |
| cb0.End(); |
| m_default_queue->Submit(cb0); |
| |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| cb1.Copy(buffer_c, buffer_a); |
| cb1.End(); |
| m_errorMonitor->SetDesiredError("RegionA"); |
| m_default_queue->Submit(cb1); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion3) { |
| TEST_DESCRIPTION("Prior access debug region reporting: multiple nested debug regions"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| |
| vkt::Event event(*m_device); // events are not used for some specific functionality, only to create additional debug regions |
| vkt::Event event2(*m_device); |
| |
| // CommandBuffer0 |
| label.pLabelName = "VulkanFrame_CommandBuffer0"; |
| cb0.Begin(); |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| |
| label.pLabelName = "ResetEvent"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| vk::CmdResetEvent(cb0, event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); |
| vk::CmdEndDebugUtilsLabelEXT(cb0); |
| |
| label.pLabelName = "FirstPass"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| |
| label.pLabelName = "CopyAToB"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| cb0.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb0); |
| |
| vk::CmdEndDebugUtilsLabelEXT(cb0); // FirstPass |
| vk::CmdEndDebugUtilsLabelEXT(cb0); // VulkanFrame_CommandBuffer0 |
| cb0.End(); |
| |
| // CommandBuffer1 |
| label.pLabelName = "VulkanFrame_CommandBuffer1"; |
| cb1.Begin(); |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| |
| label.pLabelName = "ResetEvent2"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| vk::CmdResetEvent(cb1, event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); |
| vk::CmdEndDebugUtilsLabelEXT(cb1); |
| |
| label.pLabelName = "SecondPass"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| |
| label.pLabelName = "CopyCToA"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| cb1.Copy(buffer_c, buffer_a); |
| vk::CmdEndDebugUtilsLabelEXT(cb1); |
| |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // SecondPass |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // VulkanFrame_CommandBuffer1 |
| cb1.End(); |
| |
| m_errorMonitor->SetDesiredError("VulkanFrame_CommandBuffer0::FirstPass::CopyAToB"); |
| m_default_queue->Submit({cb0, cb1}); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion4) { |
| TEST_DESCRIPTION("Prior access debug region reporting: debug region is formed by two command buffers"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| cb0.End(); |
| m_default_queue->Submit(cb0); |
| |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| cb1.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // RegionB |
| cb1.End(); |
| m_default_queue->Submit(cb1); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.Copy(buffer_c, buffer_a); |
| vk::CmdEndDebugUtilsLabelEXT(cb2); // RegionA |
| cb2.End(); |
| m_errorMonitor->SetDesiredError("RegionA::RegionB"); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion5) { |
| TEST_DESCRIPTION("Prior access debug region reporting: debug region is formed by two command buffers from the same submit"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| cb0.End(); |
| |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| cb1.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // RegionB |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // RegionA |
| cb1.End(); |
| |
| m_default_queue->Submit({cb0, cb1}); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.Copy(buffer_c, buffer_a); |
| cb2.End(); |
| m_errorMonitor->SetDesiredError("RegionA::RegionB"); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion6) { |
| TEST_DESCRIPTION("Prior access debug region reporting: debug region is formed by two batches from the same submit"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| cb0.End(); |
| |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb1, &label); |
| cb1.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // RegionB |
| vk::CmdEndDebugUtilsLabelEXT(cb1); // RegionA |
| cb1.End(); |
| |
| VkSubmitInfo submit_infos[2]; |
| submit_infos[0] = vku::InitStructHelper(); |
| submit_infos[0].commandBufferCount = 1; |
| submit_infos[0].pCommandBuffers = &cb0.handle(); |
| submit_infos[1] = vku::InitStructHelper(); |
| submit_infos[1].commandBufferCount = 1; |
| submit_infos[1].pCommandBuffers = &cb1.handle(); |
| vk::QueueSubmit(*m_default_queue, 2, submit_infos, VK_NULL_HANDLE); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.Copy(buffer_c, buffer_a); |
| cb2.End(); |
| m_errorMonitor->SetDesiredError("RegionA::RegionB"); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| // Regression test for part 1 of https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7502 |
| TEST_F(NegativeSyncValReporting, QSDebugRegion7) { |
| TEST_DESCRIPTION("Prior access debug region reporting: command buffer has labels but hazardous command is not in debug region"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| cb0.Copy(buffer_a, buffer_b); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| vk::CmdEndDebugUtilsLabelEXT(cb0); |
| cb0.End(); |
| m_default_queue->Submit(cb0); |
| |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| cb1.Copy(buffer_c, buffer_a); |
| cb1.End(); |
| m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-READ"); |
| m_default_queue->Submit(cb1); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, QSDebugRegion_Secondary) { |
| TEST_DESCRIPTION("Prior access debug region reporting: secondary command buffer"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_c(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer secondary_cb(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| secondary_cb.Begin(); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(secondary_cb, &label); |
| secondary_cb.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(secondary_cb); |
| secondary_cb.End(); |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb0, &label); |
| vk::CmdExecuteCommands(cb0, 1, &secondary_cb.handle()); |
| vk::CmdEndDebugUtilsLabelEXT(cb0); |
| cb0.End(); |
| m_default_queue->Submit(cb0); |
| |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| cb1.Copy(buffer_c, buffer_a); |
| cb1.End(); |
| m_errorMonitor->SetDesiredError("RegionA::RegionB"); |
| m_default_queue->Submit(cb1); |
| m_errorMonitor->VerifyFound(); // SYNC-HAZARD-WRITE-AFTER-READ error message |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, StaleLabelCommand) { |
| TEST_DESCRIPTION("Try to access stale label command when core validation error breaks state invariants"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| m_command_buffer.Begin(); |
| // Issue a bunch of label commands |
| label.pLabelName = "RegionA"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(m_command_buffer, &label); |
| vk::CmdEndDebugUtilsLabelEXT(m_command_buffer); |
| // At this point 4 label commands were recorded. |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_command_buffer.End(); |
| |
| m_default_queue->Submit(m_command_buffer); |
| |
| // Suppress error because we reset command buffer that is still in use. |
| // Simulate situation when core validation is disabled and synchronization |
| // validation runs with existing core validation error. This should not lead |
| // to syncval crashes (but does not guarantee correct syncval behavior). |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkBeginCommandBuffer-commandBuffer-00049"); |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkQueueSubmit-pCommandBuffers-00071"); |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_command_buffer.End(); |
| |
| // This will detect write-after-write hazard. During error reporting debug label |
| // information says that prior read occured after the 4th label command. Attempt |
| // to access those label commands can lead to crash if out of bounds check is missing. |
| m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE"); |
| m_default_queue->Submit(m_command_buffer); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportBufferResource_SubmitTime) { |
| TEST_DESCRIPTION("Test that hazardous buffer is reported"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| buffer_a.SetName("BufferA"); |
| vkt::Buffer buffer_b(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| buffer_b.SetName("BufferB"); |
| VkBufferCopy region = {0, 0, 256}; |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| vk::CmdCopyBuffer(cb0, buffer_a, buffer_b, 1, ®ion); |
| cb0.End(); |
| m_default_queue->Submit(cb0); |
| |
| // WAW for BufferB. Expect BufferB in the error message but not BufferA. |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| vk::CmdCopyBuffer(cb1, buffer_a, buffer_b, 1, ®ion); |
| cb1.End(); |
| const char* contains_b_but_not_a = "(?=.*BufferB)(?!.*BufferA)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", contains_b_but_not_a); |
| m_default_queue->Submit(cb1); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportImageResource_SubmitTime) { |
| TEST_DESCRIPTION("Test that hazardous image is reported"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer(*m_device, 64 * 64 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| buffer.SetName("BufferA"); |
| |
| vkt::Image image(*m_device, 64, 64, VK_FORMAT_B8G8R8A8_UNORM, |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| image.SetName("ImageB"); |
| image.SetLayout(VK_IMAGE_LAYOUT_GENERAL); |
| |
| vkt::Image image2(*m_device, 64, 64, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| image2.SetName("ImageC"); |
| image2.SetLayout(VK_IMAGE_LAYOUT_GENERAL); |
| |
| VkImageCopy image_copy{}; |
| image_copy.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| image_copy.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| image_copy.extent = {64, 64, 1}; |
| |
| VkBufferImageCopy buffer_image_copy{}; |
| buffer_image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| buffer_image_copy.imageExtent = {64, 64, 1}; |
| |
| vkt::CommandBuffer cb0(*m_device, m_command_pool); |
| cb0.Begin(); |
| vk::CmdCopyImage(cb0, image, VK_IMAGE_LAYOUT_GENERAL, image2, VK_IMAGE_LAYOUT_GENERAL, 1, &image_copy); |
| cb0.End(); |
| m_default_queue->Submit(cb0); |
| |
| // WAR for ImageB. Expect ImageB in the error message but not BufferA or ImageC. |
| vkt::CommandBuffer cb1(*m_device, m_command_pool); |
| cb1.Begin(); |
| vk::CmdCopyBufferToImage(cb1, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &buffer_image_copy); |
| cb1.End(); |
| const char* contains_b_but_not_a_c = "(?=.*ImageB)(?!.*BufferA)(?!.*ImageC)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-READ", contains_b_but_not_a_c); |
| m_default_queue->Submit(cb1); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportDescriptorBuffer_SubmitTime) { |
| TEST_DESCRIPTION("Test that hazardous buffer is reported"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_a.SetName("BufferA"); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_b.SetName("BufferB"); |
| |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| void main(){ |
| values_b[0] = values_a[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| // Submit dispatch that writes to buffer_b |
| vkt::CommandBuffer cb(*m_device, m_command_pool); |
| cb.Begin(); |
| vk::CmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, 0, nullptr); |
| vk::CmdDispatch(cb, 1, 1, 1); |
| cb.End(); |
| m_default_queue->Submit(cb); |
| |
| // One more dispatch that writes to the same buffer (WAW hazard). Expect BufferB in the error message but not BufferA. |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| vk::CmdBindPipeline(cb2, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(cb2, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, 0, nullptr); |
| vk::CmdDispatch(cb2, 1, 1, 1); |
| cb2.End(); |
| const char* contains_b_but_not_a = "(?=.*BufferB)(?!.*BufferA)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", contains_b_but_not_a); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportDescriptorBuffer2_SubmitTime) { |
| TEST_DESCRIPTION("Test that hazardous buffer is reported"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_a.SetName("BufferA"); |
| vkt::Buffer buffer_c(*m_device, 128, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_c.SetName("BufferC"); |
| vkt::Buffer buffer_e(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_e.SetName("BufferD"); |
| |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_c, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(2, buffer_e, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_c { uint values_c[]; }; |
| layout(set=0, binding=2) buffer buf_e { uint values_e[]; }; |
| void main(){ |
| values_c[0] = values_e[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| // Submit dispatch that writes to buffer_b |
| vkt::CommandBuffer cb(*m_device, m_command_pool); |
| cb.Begin(); |
| vk::CmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, 0, nullptr); |
| vk::CmdDispatch(cb, 1, 1, 1); |
| cb.End(); |
| m_default_queue->Submit(cb); |
| |
| // Submit copy that writes to the same buffer (WAW hazard). Expect BufferC in the error message but not BufferA/BufferE |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.Copy(buffer_a, buffer_c); |
| cb2.End(); |
| const char* contains_c_but_not_a_and_e = "(?=.*BufferC)(?!.*BufferA)(?!.*BufferE)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", contains_c_but_not_a_and_e); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportDescriptorBuffer3_SubmitTime) { |
| TEST_DESCRIPTION("Different buffer of the same shader is reported depending on the previous access"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_copy_src(*m_device, 128, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| buffer_copy_src.SetName("BufferSrc"); |
| vkt::Buffer buffer_a(*m_device, 128, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_a.SetName("BufferA"); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_b.SetName("BufferB"); |
| vkt::Buffer buffer_c(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_c.SetName("BufferC"); |
| vkt::Buffer buffer_d(*m_device, 128, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer_d.SetName("BufferD"); |
| |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(2, buffer_c, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(3, buffer_d, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| layout(set=0, binding=2) buffer buf_c { uint values_c[]; }; |
| layout(set=0, binding=3) buffer buf_d { uint values_d[]; }; |
| void main(){ |
| // Depending on which buffer was written before (B or D) one of the following writes causes a WAW hazard |
| values_b[0] = values_a[0]; |
| values_d[0] = values_c[0]; |
| |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| // Create WAW with bufferB. Copy-write then dispatch-write. |
| vkt::CommandBuffer cb(*m_device, m_command_pool); |
| cb.Begin(); |
| cb.Copy(buffer_copy_src, buffer_b); |
| cb.End(); |
| m_default_queue->Submit(cb); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| vk::CmdBindPipeline(cb2, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(cb2, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, 0, nullptr); |
| vk::CmdDispatch(cb2, 1, 1, 1); |
| cb2.End(); |
| const char* contains_b_but_not_d = "(?=.*BufferB)(?!.*BufferD)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", contains_b_but_not_d); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| |
| // Create WAW with bufferD. Copy-write then dispatch-write. |
| vkt::CommandBuffer cb3(*m_device, m_command_pool); |
| cb3.Begin(); |
| cb3.Copy(buffer_copy_src, buffer_d); |
| cb3.End(); |
| m_default_queue->Submit(cb3); |
| const char* contains_buffer_d_but_not_b = "(?=.*BufferD)(?!.*BufferB)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", contains_buffer_d_but_not_b); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportDescriptorImage_SubmitTime) { |
| TEST_DESCRIPTION("Test that hazardous image is reported"); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| buffer.SetName("BufferA"); |
| |
| vkt::Image image(*m_device, 64, 64, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_STORAGE_BIT); |
| image.SetLayout(VK_IMAGE_LAYOUT_GENERAL); |
| image.SetName("ImageB"); |
| |
| vkt::ImageView view = image.CreateView(); |
| |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorImageInfo(1, view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_IMAGE_LAYOUT_GENERAL); |
| descriptor_set.UpdateDescriptorSets(); |
| |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1, rgba8) uniform image2D image_b; |
| void main(){ |
| imageStore(image_b, ivec2(2, 5), vec4(1.0, 0.5, 0.5, 1.0)); |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| // Submit dispatch that writes to image |
| vkt::CommandBuffer cb(*m_device, m_command_pool); |
| cb.Begin(); |
| vk::CmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, 0, nullptr); |
| vk::CmdDispatch(cb, 1, 1, 1); |
| cb.End(); |
| m_default_queue->Submit(cb); |
| |
| // One more dispatch that writes to the same image (WAW hazard). Expect ImageB in the error message but not BufferA. |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| vk::CmdBindPipeline(cb2, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(cb2, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, 0, nullptr); |
| vk::CmdDispatch(cb2, 1, 1, 1); |
| cb2.End(); |
| const char* contains_b_but_not_a = "(?=.*ImageB)(?!.*BufferA)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", contains_b_but_not_a); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DebugLabelRegionsFromSecondaryCommandBuffers) { |
| TEST_DESCRIPTION("Prior access debug region reporting: debug region is formed by two command buffers"); |
| |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncValFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer buffer_a(*m_device, 256, buffer_usage); |
| vkt::Buffer buffer_b(*m_device, 256, buffer_usage); |
| VkDebugUtilsLabelEXT label = vku::InitStructHelper(); |
| |
| vkt::CommandBuffer cb(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| cb.Begin(VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT); |
| label.pLabelName = "RegionB"; |
| vk::CmdBeginDebugUtilsLabelEXT(cb, &label); |
| cb.Copy(buffer_a, buffer_b); |
| vk::CmdEndDebugUtilsLabelEXT(cb); |
| cb.End(); |
| |
| vkt::CommandBuffer primary_0(*m_device, m_command_pool); |
| primary_0.Begin(); |
| label.pLabelName = "primary_0"; |
| vk::CmdBeginDebugUtilsLabelEXT(primary_0, &label); |
| vk::CmdExecuteCommands(primary_0, 1, &cb.handle()); |
| vk::CmdEndDebugUtilsLabelEXT(primary_0); |
| primary_0.End(); |
| |
| vkt::CommandBuffer primary_1(*m_device, m_command_pool); |
| primary_1.Begin(); |
| label.pLabelName = "primary_1"; |
| vk::CmdBeginDebugUtilsLabelEXT(primary_1, &label); |
| vk::CmdExecuteCommands(primary_1, 1, &cb.handle()); |
| vk::CmdEndDebugUtilsLabelEXT(primary_1); |
| primary_1.End(); |
| |
| const char* region_patterns = "(?=.*primary_0::RegionB)(?=.*primary_1::RegionB)"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", region_patterns); |
| m_default_queue->Submit({primary_0, primary_1}); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ExpandedMetaStage) { |
| TEST_DESCRIPTION("Check barrier is printed in compact form"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::Buffer buffer_b(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; // Read access mask does not protect subsequent copy writes |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_command_buffer.Barrier(barrier); |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", |
| "VK_ACCESS_2_SHADER_READ_BIT accesses at VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT"); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ExpandedMetaStage2) { |
| TEST_DESCRIPTION("Check barrier is printed in compact form"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::Buffer buffer_b(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT; // Read access mask does not protect subsequent copy writes |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_command_buffer.Barrier(barrier); |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", |
| "VK_ACCESS_2_MEMORY_READ_BIT accesses at VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT"); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ExpandedMetaStage3) { |
| TEST_DESCRIPTION("Check barrier is printed in compact form"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::Buffer buffer_b(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT; // Shader write access mask does not protect subsequent copy writes |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_command_buffer.Barrier(barrier); |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", |
| "VK_ACCESS_2_SHADER_WRITE_BIT accesses at VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT"); |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportAllTransferMetaStage) { |
| TEST_DESCRIPTION("Check that error message reports accesses on all transfer stages in compact form (uses meta stage)"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| void main(){ |
| values_b[0] = values_a[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.buffer = buffer_b; |
| barrier.size = 128; |
| |
| m_command_buffer.Begin(); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, |
| 0, nullptr); |
| |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| // This barrier makes copy accesses visible to the transfer stage but not to the compute stage |
| m_command_buffer.Barrier(barrier); |
| |
| // Check that error reporting merged internal representation of transfer stage accesses into a compact form |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", |
| "VK_ACCESS_2_TRANSFER_WRITE_BIT accesses at VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT"); |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DoNotReportUnsupportedStage) { |
| TEST_DESCRIPTION("Check that unsupported stage does not add information to the error message"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| void main(){ |
| values_b[0] = values_a[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| // TRANSFER READ and/or WRITE are the only accesses supported by COPY/RESOLVE/BLIT/CLEAR stages. |
| // ACCELERATION_STRUCTURE_COPY_BIT_KHR supports more accesses, but because ray tracing |
| // extension is not enabled we don't need to take them into account and can report result in a |
| // short "all accesses" form. |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT; |
| barrier.buffer = buffer_b; |
| barrier.size = 128; |
| |
| m_command_buffer.Begin(); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, |
| 0, nullptr); |
| |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| // This barrier makes previous writes visible to COPY stage but not to the following COMPUTE stage |
| m_command_buffer.Barrier(barrier); |
| |
| // If error reporting does not skip unsupported ACCELERATION_STRUCTURE_COPY_BIT_KHR then the following error message won't |
| // be able to use short form (TRANSFER_WRITE+TRANSFER_READ != "all accesses" in that case) |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", "all accesses at VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT"); |
| |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportShaderReadMetaAccess) { |
| // There was a regression with 1.4.312 headers when SHADER_READ was not detected properly |
| // and was replaced with an expansion that included SHADER_TILE_ATTACHMENT_READ access. |
| TEST_DESCRIPTION("Test that SHADER_READ is reported and does not leak sub-accesses that are enabled by extensions"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| // This test does not enable extensions that add SHADER_READ sub-accesses. |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| vkt::Buffer buffer_c(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| void main(){ |
| values_b[0] = values_a[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| |
| // This barrier makes previous writes visible to VERTEX stage but not to the COMPUTE stage |
| m_command_buffer.Barrier(barrier); |
| |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, |
| 0, nullptr); |
| |
| // Test that destination stage mask from the barrier (VK_ACCESS_2_SHADER_READ_BIT) is reported in the original form. |
| // The variables in play here: |
| // * VERTEX_SHADER stage does not support SHADER_TILE_ATTACHMENT_READ accesses |
| // * tile shading extension is also not enabled |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-READ-AFTER-WRITE", |
| "VK_ACCESS_2_SHADER_READ_BIT accesses at VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT"); |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportShaderReadMetaAccess2) { |
| TEST_DESCRIPTION("Test that SHADER_READ is reported and does not leak sub-accesses that are enabled by extensions"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| // This test enables extension that adds sub-access (SHADER_TILE_ATTACHMENT_READ) to SHADER_READ |
| AddRequiredExtensions(VK_QCOM_TILE_SHADING_EXTENSION_NAME); |
| |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| vkt::Buffer buffer_c(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| void main(){ |
| values_b[0] = values_a[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer_c, buffer_a); |
| |
| // This barrier makes previous writes visible to VERTEX stage but not to the COMPUTE stage |
| m_command_buffer.Barrier(barrier); |
| |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, |
| 0, nullptr); |
| |
| // Test that destination stage mask from the barrier (VK_ACCESS_2_SHADER_READ_BIT) is reported in the original form. |
| // The variables in play here: |
| // * VERTEX_SHADER stage does not support SHADER_TILE_ATTACHMENT_READ accesses |
| // * but we also enable tile shading extension that adds sub-accesses to SHADER_READ (for fragment and compute stages) |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-READ-AFTER-WRITE", |
| "VK_ACCESS_2_SHADER_READ_BIT accesses at VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT"); |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, ReportAccelerationStructureCopyAccesses) { |
| TEST_DESCRIPTION("Check that TRANSFER_READ+WRITE is not replaced with ALL accesses for ACCELERATION_STRUCTURE_COPY stage"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredFeature(vkt::Feature::rayTracingMaintenance1); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Buffer buffer_a(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::Buffer buffer_b(*m_device, 128, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer_a, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.WriteDescriptorBufferInfo(1, buffer_b, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| const char* cs_source = R"glsl( |
| #version 450 |
| layout(set=0, binding=0) buffer buf_a { uint values_a[]; }; |
| layout(set=0, binding=1) buffer buf_b { uint values_b[]; }; |
| void main(){ |
| values_b[0] = values_a[0]; |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}); |
| pipe.CreateComputePipeline(); |
| |
| // Protect TRANSFER_READ+WRITE accesses |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT; |
| barrier.buffer = buffer_b; |
| barrier.size = 128; |
| |
| m_command_buffer.Begin(); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_, |
| 0, nullptr); |
| |
| m_command_buffer.Copy(buffer_a, buffer_b); |
| // This barrier makes previous writes visible to COPY stage but not to the following COMPUTE stage |
| m_command_buffer.Barrier(barrier); |
| |
| // ACCELERATION_STRUCTURE_COPY_BIT_KHR supports more accesses than TRANSFER_READ+WRITE, |
| // so the latter combination can't be replaced with "all accesses" |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", |
| "VK_ACCESS_2_TRANSFER_READ_BIT|VK_ACCESS_2_TRANSFER_WRITE_BIT"); |
| |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, DoNotUseShortcutForSimpleAccessMask) { |
| TEST_DESCRIPTION("Check that for access mask with at most 2 bits set we don't use ALL accesses shortcut"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredFeature(vkt::Feature::dynamicRendering); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| vkt::Buffer buffer(*m_device, 32 * 32 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| VkRenderingAttachmentInfo attachment = vku::InitStructHelper(); |
| attachment.imageLayout = VK_IMAGE_LAYOUT_GENERAL; |
| attachment.imageView = image_view; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| |
| VkRenderingInfo rendering_info = vku::InitStructHelper(); |
| rendering_info.renderArea = {{0, 0}, {32, 32}}; |
| rendering_info.layerCount = 1; |
| rendering_info.colorAttachmentCount = 1; |
| rendering_info.pColorAttachments = &attachment; |
| |
| VkBufferImageCopy region{}; |
| region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| region.imageExtent = {32, 32, 1}; |
| |
| VkImageMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; |
| barrier.image = image; |
| barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
| |
| m_command_buffer.Begin(); |
| // Generate accesses on COLOR_ATTACHMENT_OUTPUT stage that the barrier will connect with |
| m_command_buffer.BeginRendering(rendering_info); |
| m_command_buffer.EndRendering(); |
| |
| // This barrier does not protect writes on the transfer stage. The following copy generates WAW |
| m_command_buffer.Barrier(barrier); |
| |
| // Test that access mask is printed directly and is not replaced with "all accesses" shortcut |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-AFTER-WRITE", |
| "VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT|VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT"); |
| vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, LayoutTrasitionErrorHasImageHandle) { |
| TEST_DESCRIPTION("Ensure that layout transition error contains image handle"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| if (!m_second_queue) { |
| GTEST_SKIP() << "Two queues are needed"; |
| } |
| |
| vkt::Image image(*m_device, 64, 64, VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| image.SetName("ImageA"); |
| |
| VkImageMemoryBarrier2 image_barrier = vku::InitStructHelper(); |
| image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| image_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; |
| image_barrier.image = image; |
| image_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Barrier(image_barrier); |
| m_command_buffer.End(); |
| |
| m_second_command_buffer.Begin(); |
| m_second_command_buffer.Barrier(image_barrier); |
| m_second_command_buffer.End(); |
| |
| m_default_queue->Submit2(m_command_buffer); |
| |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-RACING-WRITE", "ImageA"); |
| m_second_queue->Submit2(m_second_command_buffer); |
| m_errorMonitor->VerifyFound(); |
| |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(NegativeSyncValReporting, CommandBufferMatchesQueue) { |
| TEST_DESCRIPTION("Submit command buffers to different queues. Check that message correctly pairs command buffer and queue"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredExtensions(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitSyncVal()); |
| |
| vkt::Queue* transfer_queue = m_device->TransferOnlyQueue(); |
| if (!transfer_queue) { |
| GTEST_SKIP() << "Transfer-only queue is not present"; |
| } |
| |
| vkt::Image image(*m_device, 64, 64, VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| vkt::Buffer buffer(*m_device, 64 * 64, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| |
| VkBufferImageCopy region = {}; |
| region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| region.imageExtent = {64, 64, 1}; |
| |
| VkImageMemoryBarrier2 image_barrier = vku::InitStructHelper(); |
| image_barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE; |
| image_barrier.srcAccessMask = VK_ACCESS_2_NONE; |
| image_barrier.dstStageMask = VK_PIPELINE_STAGE_2_NONE; |
| image_barrier.dstAccessMask = VK_ACCESS_2_NONE; |
| image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| image_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; |
| image_barrier.image = image; |
| image_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
| |
| m_default_queue->SetName(*m_device, "GraphicsQueue"); |
| m_command_buffer.SetName(*m_device, "GraphicsCB"); |
| |
| vkt::CommandPool transfer_pool(*m_device, transfer_queue->family_index); |
| vkt::CommandBuffer transfer_cb(*m_device, transfer_pool); |
| transfer_queue->SetName(*m_device, "TransferQueue"); |
| transfer_cb.SetName(*m_device, "TransferCB"); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Barrier(image_barrier); |
| m_command_buffer.End(); |
| m_default_queue->Submit2(m_command_buffer); |
| |
| transfer_cb.Begin(); |
| vk::CmdCopyBufferToImage(transfer_cb, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); |
| transfer_cb.End(); |
| // Transfer command buffer should be paired with transfer queue (and the same for grapics). |
| const char* transfer_cb_then_q_graphics_cb_then_q = ".*TransferCB.*TransferQueue.*GraphicsCB.*GraphicsQueue.*"; |
| m_errorMonitor->SetDesiredErrorRegex("SYNC-HAZARD-WRITE-RACING-WRITE", transfer_cb_then_q_graphics_cb_then_q); |
| transfer_queue->Submit2(transfer_cb); |
| m_errorMonitor->VerifyFound(); |
| m_default_queue->Wait(); |
| } |