| /* |
| * Copyright (c) 2015-2025 The Khronos Group Inc. |
| * Copyright (c) 2015-2025 Valve Corporation |
| * Copyright (c) 2015-2025 LunarG, Inc. |
| * Copyright (c) 2015-2025 Google, Inc. |
| * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. |
| * |
| * 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 |
| */ |
| |
| #include <thread> |
| #include "../framework/layer_validation_tests.h" |
| #include "../framework/pipeline_helper.h" |
| #include "../framework/render_pass_helper.h" |
| #include "../framework/descriptor_helper.h" |
| #include "../framework/sync_helper.h" |
| #include "../framework/thread_helper.h" |
| |
| void VkBestPracticesLayerTest::InitBestPracticesFramework(const char *vendor_checks_to_enable) { |
| const VkLayerSettingEXT settings = {OBJECT_LAYER_NAME, vendor_checks_to_enable, VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &kVkTrue}; |
| const VkLayerSettingsCreateInfoEXT layer_settings_create_info{VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, nullptr, 1, |
| &settings}; |
| |
| if (vendor_checks_to_enable) { |
| features_.pNext = &layer_settings_create_info; |
| } |
| |
| AddRequiredExtensions(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME); |
| InitFramework(&features_); |
| } |
| |
| void VkBestPracticesLayerTest::InitBestPractices(const char* vendor_checks_to_enable) { |
| RETURN_IF_SKIP(InitBestPracticesFramework(vendor_checks_to_enable)); |
| RETURN_IF_SKIP(InitState()); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ReturnCodes) { |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| |
| AddSurfaceExtension(); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| RETURN_IF_SKIP(InitSwapchain()); |
| |
| // Attempt to force an invalid return code for an unsupported format |
| VkImageFormatProperties2 image_format_prop = vku::InitStructHelper(); |
| VkPhysicalDeviceImageFormatInfo2 image_format_info = vku::InitStructHelper(); |
| image_format_info.format = VK_FORMAT_R32G32B32_SFLOAT; |
| image_format_info.tiling = VK_IMAGE_TILING_LINEAR; |
| image_format_info.type = VK_IMAGE_TYPE_3D; |
| image_format_info.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; |
| |
| VkResult result = vk::GetPhysicalDeviceImageFormatProperties2(m_device->Physical(), &image_format_info, &image_format_prop); |
| // Only run this test if this super-wierd format is not supported |
| if (VK_SUCCESS != result) { |
| m_errorMonitor->SetDesiredWarning("BestPractices-Error-Result"); |
| vk::GetPhysicalDeviceImageFormatProperties2(m_device->Physical(), &image_format_info, &image_format_prop); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| if (IsPlatformMockICD()) { |
| GTEST_SKIP() << "Test not supported by MockICD because will always return VK_SUCCESS"; |
| } |
| |
| // Force a non-success success code by only asking for a subset of query results |
| uint32_t format_count; |
| std::vector<VkSurfaceFormatKHR> formats; |
| result = vk::GetPhysicalDeviceSurfaceFormatsKHR(Gpu(), m_surface, &format_count, NULL); |
| if (result != VK_SUCCESS || format_count <= 1) { |
| GTEST_SKIP() << "test requires 2 or more extensions available"; |
| } |
| format_count -= 1; |
| formats.resize(format_count); |
| |
| m_errorMonitor->SetDesiredFailureMsg(kVerboseBit, "BestPractices-Verbose-Success-Logging"); |
| result = vk::GetPhysicalDeviceSurfaceFormatsKHR(Gpu(), m_surface, &format_count, formats.data()); |
| ASSERT_TRUE(result > VK_SUCCESS); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SpecialUseExtensionsInstance) { |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| if (!InstanceExtensionSupported(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Did not find required instance extension"; |
| } |
| m_instance_extension_names.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); |
| m_instance_extension_names.emplace_back(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME); |
| |
| const VkLayerSettingEXT settings = {OBJECT_LAYER_NAME, "validate_best_practices", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, |
| &kVkTrue}; |
| const VkLayerSettingsCreateInfoEXT layer_settings_create_info{VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, nullptr, 1, |
| &settings}; |
| |
| // GetDebugCreateInfo() is last pNext chain set in GetInstanceCreateInfo() |
| const_cast<VkDebugUtilsMessengerCreateInfoEXT*>(m_errorMonitor->GetDebugCreateInfo())->pNext = &layer_settings_create_info; |
| |
| Monitor().SetDesiredWarning("BestPractices-specialuse-extension"); |
| VkInstance dummy_instance; |
| auto ici = GetInstanceCreateInfo(); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SpecialUseExtensionsDevice) { |
| AddRequiredExtensions(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| |
| VkDevice local_device; |
| VkDeviceCreateInfo dev_info = vku::InitStructHelper(); |
| VkDeviceQueueCreateInfo queue_info = vku::InitStructHelper(); |
| queue_info.queueFamilyIndex = 0; |
| queue_info.queueCount = 1; |
| float qp = 1; |
| queue_info.pQueuePriorities = &qp; |
| dev_info.queueCreateInfoCount = 1; |
| dev_info.pQueueCreateInfos = &queue_info; |
| dev_info.enabledLayerCount = 0; |
| dev_info.ppEnabledLayerNames = NULL; |
| dev_info.enabledExtensionCount = m_device_extension_names.size(); |
| dev_info.ppEnabledExtensionNames = m_device_extension_names.data(); |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-specialuse-extension"); |
| vk::CreateDevice(this->Gpu(), &dev_info, NULL, &local_device); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, CmdClearAttachmentTest) { |
| TEST_DESCRIPTION("Test for validating usage of vkCmdClearAttachments"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); |
| |
| // Main thing we care about for this test is that the VkImage obj we're |
| // clearing matches Color Attachment of FB |
| // Also pass down other dummy params to keep driver and paramchecker happy |
| VkClearAttachment color_attachment; |
| color_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| color_attachment.clearValue.color.float32[0] = 1.0; |
| color_attachment.clearValue.color.float32[1] = 1.0; |
| color_attachment.clearValue.color.float32[2] = 1.0; |
| color_attachment.clearValue.color.float32[3] = 1.0; |
| color_attachment.colorAttachment = 0; |
| VkClearRect clear_rect = {{{0, 0}, {m_width, m_height}}, 0, 1}; |
| |
| // Call for full-sized FB Color attachment prior to issuing a Draw |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-DrawState-ClearCmdBeforeDraw"); |
| |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, CmdClearAttachmentTestSecondary) { |
| TEST_DESCRIPTION("Test for validating usage of vkCmdClearAttachments with secondary command buffers"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| m_command_buffer.Begin(); |
| |
| vkt::CommandBuffer secondary_full_clear(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| vkt::CommandBuffer secondary_small_clear(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| VkCommandBufferBeginInfo begin_info = vku::InitStructHelper(); |
| begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; |
| VkCommandBufferInheritanceInfo inherit_info = vku::InitStructHelper(); |
| begin_info.pInheritanceInfo = &inherit_info; |
| inherit_info.subpass = 0; |
| inherit_info.renderPass = m_renderPassBeginInfo.renderPass; |
| |
| // Main thing we care about for this test is that the VkImage obj we're |
| // clearing matches Color Attachment of FB |
| // Also pass down other dummy params to keep driver and paramchecker happy |
| VkClearAttachment color_attachment; |
| color_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| color_attachment.clearValue.color.float32[0] = 1.0; |
| color_attachment.clearValue.color.float32[1] = 1.0; |
| color_attachment.clearValue.color.float32[2] = 1.0; |
| color_attachment.clearValue.color.float32[3] = 1.0; |
| color_attachment.colorAttachment = 0; |
| VkClearRect clear_rect_small = {{{0, 0}, {m_width - 1u, m_height - 1u}}, 0, 1}; |
| VkClearRect clear_rect = {{{0, 0}, {m_width, m_height}}, 0, 1}; |
| |
| m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); |
| { |
| // Small clears which don't cover the render area should not trigger the warning. |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect_small); |
| // Call for full-sized FB Color attachment prior to issuing a Draw |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-DrawState-ClearCmdBeforeDraw"); |
| // This test may also trigger other warnings |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-AMD-VkCommandBuffer-AvoidSecondaryCmdBuffers"); |
| |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect); |
| m_errorMonitor->VerifyFound(); |
| } |
| m_command_buffer.EndRenderPass(); |
| |
| secondary_small_clear.Begin(&begin_info); |
| secondary_full_clear.Begin(&begin_info); |
| vk::CmdClearAttachments(secondary_small_clear, 1, &color_attachment, 1, &clear_rect_small); |
| vk::CmdClearAttachments(secondary_full_clear, 1, &color_attachment, 1, &clear_rect); |
| secondary_small_clear.End(); |
| secondary_full_clear.End(); |
| |
| m_command_buffer.BeginRenderPass(m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); |
| { |
| // Small clears which don't cover the render area should not trigger the warning. |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_small_clear.handle()); |
| // Call for full-sized FB Color attachment prior to issuing a Draw |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-DrawState-ClearCmdBeforeDraw"); |
| // This test may also trigger other warnings |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-AMD-VkCommandBuffer-AvoidSecondaryCmdBuffers"); |
| |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_full_clear.handle()); |
| m_errorMonitor->VerifyFound(); |
| } |
| m_command_buffer.EndRenderPass(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ZeroSizeBlitRegion) { |
| TEST_DESCRIPTION("vkCmdBlitImage with a zero area region"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::Image image_src(*m_device, 128, 128, VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_TRANSFER_SRC_BIT); |
| vkt::Image image_dst(*m_device, 128, 128, VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| |
| VkImageBlit blit_region = {}; |
| blit_region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| blit_region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; |
| blit_region.srcOffsets[0] = {128, 0, 0}; |
| blit_region.srcOffsets[1] = {128, 128, 1}; |
| blit_region.dstOffsets[0] = {0, 128, 0}; |
| blit_region.dstOffsets[1] = {128, 128, 1}; |
| |
| m_command_buffer.Begin(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-DrawState-InvalidExtents-src"); |
| m_errorMonitor->SetDesiredWarning("BestPractices-DrawState-InvalidExtents-dst"); |
| vk::CmdBlitImage(m_command_buffer, image_src, VK_IMAGE_LAYOUT_GENERAL, image_dst, VK_IMAGE_LAYOUT_GENERAL, 1, &blit_region, |
| VK_FILTER_LINEAR); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SecondaryCommandBuffer) { |
| TEST_DESCRIPTION("Test for validating usage of vkCreateCommandPool with VK_COMMAND_BUFFER_LEVEL_SECONDARY"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| uint32_t queue_family_count; |
| vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &queue_family_count, nullptr); |
| |
| std::vector<VkQueueFamilyProperties> queue_family_props; |
| queue_family_props.resize(queue_family_count); |
| vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &queue_family_count, queue_family_props.data()); |
| |
| uint32_t queue_family_index = VK_QUEUE_FAMILY_IGNORED; |
| const VkQueueFlags sec_cmd_buf_queue_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT; |
| for (uint32_t i = 0; i < queue_family_count; ++i) { |
| if ((queue_family_props[i].queueFlags & sec_cmd_buf_queue_flags) == 0) { |
| queue_family_index = i; |
| break; |
| } |
| } |
| |
| if (queue_family_index == VK_QUEUE_FAMILY_IGNORED) { |
| GTEST_SKIP() << "No queue family found without support for secondary command buffers"; |
| } |
| |
| VkCommandPoolCreateInfo pool_create_info = vku::InitStructHelper(); |
| pool_create_info.queueFamilyIndex = queue_family_index; |
| vkt::CommandPool command_pool(*m_device, pool_create_info); |
| |
| VkCommandBuffer command_buffer = VK_NULL_HANDLE; |
| VkCommandBufferAllocateInfo alloc_info = vku::InitStructHelper(); |
| alloc_info.commandPool = command_pool; |
| alloc_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; |
| alloc_info.commandBufferCount = 1; |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkAllocateCommandBuffers-unusable-secondary"); |
| vk::AllocateCommandBuffers(device(), &alloc_info, &command_buffer); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::FreeCommandBuffers(device(), command_pool, 1, &command_buffer); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SmallDedicatedAllocation) { |
| TEST_DESCRIPTION("Test for small dedicated memory allocations"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkBindImageMemory-small-dedicated-allocation"); |
| |
| VkImageCreateInfo image_info = |
| vkt::Image::ImageCreateInfo2D(64, 64, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| // Create a small image with a dedicated allocation |
| vkt::Image image(*m_device, image_info, vkt::no_mem); |
| |
| vkt::DeviceMemory mem(*m_device, vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image.MemoryRequirements(), |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); |
| vk::BindImageMemory(device(), image, mem, 0); |
| |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, MSImageRequiresMemory) { |
| TEST_DESCRIPTION("Test for MS image that requires memory"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkCreateRenderPass-image-requires-memory"); |
| |
| VkAttachmentDescription attachment{}; |
| attachment.samples = VK_SAMPLE_COUNT_4_BIT; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachment.format = VK_FORMAT_B8G8R8A8_SRGB; |
| attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| VkSubpassDescription sd{}; |
| |
| VkRenderPassCreateInfo rp_info = vku::InitStructHelper(); |
| rp_info.attachmentCount = 1; |
| rp_info.pAttachments = &attachment; |
| rp_info.subpassCount = 1; |
| rp_info.pSubpasses = &sd; |
| |
| VkRenderPass rp; |
| vk::CreateRenderPass(device(), &rp_info, nullptr, &rp); |
| |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, AttachmentShouldNotBeTransient) { |
| TEST_DESCRIPTION("Test for non-lazy multisampled images"); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| VkAttachmentDescription attachment{}; |
| attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachment.format = VK_FORMAT_R8G8B8A8_UNORM; |
| attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| VkSubpassDescription sd{}; |
| |
| VkRenderPassCreateInfo rp_info = vku::InitStructHelper(); |
| rp_info.attachmentCount = 1; |
| rp_info.pAttachments = &attachment; |
| rp_info.subpassCount = 1; |
| rp_info.pSubpasses = &sd; |
| vkt::RenderPass rp(*m_device, rp_info); |
| |
| VkImageCreateInfo image_info = vkt::Image::ImageCreateInfo2D( |
| 1920, 1080, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT); |
| vkt::Image image(*m_device, image_info, vkt::set_layout); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, |
| "BestPractices-vkCreateFramebuffer-attachment-should-not-be-transient"); |
| vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle(), 1920, 1080); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, TooManyInstancedVertexBuffers) { |
| TEST_DESCRIPTION("Test for too many instanced vertex buffers"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, |
| "BestPractices-vkCreateGraphicsPipelines-too-many-instanced-vertex-buffers"); |
| |
| // This test may also trigger the small allocation warnings |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-vkBindImageMemory-small-dedicated-allocation"); |
| |
| // This test does not need for the shader to consume the vertex input |
| m_errorMonitor->SetAllowedFailureMsg("WARNING-Shader-OutputNotConsumed"); |
| |
| InitRenderTarget(); |
| |
| std::vector<VkVertexInputBindingDescription> bindings(2, VkVertexInputBindingDescription{}); |
| std::vector<VkVertexInputAttributeDescription> attributes(2, VkVertexInputAttributeDescription{}); |
| |
| bindings[0].binding = 0; |
| bindings[0].stride = 4; |
| bindings[0].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; |
| |
| attributes[0].binding = 0; |
| attributes[0].location = 0; |
| attributes[0].format = VK_FORMAT_R32_SFLOAT; |
| |
| bindings[1].binding = 1; |
| bindings[1].stride = 8; |
| bindings[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; |
| |
| attributes[1].binding = 1; |
| attributes[1].location = 1; |
| attributes[1].format = VK_FORMAT_R32_SFLOAT; |
| |
| VkPipelineVertexInputStateCreateInfo vi_state_ci = vku::InitStructHelper(); |
| vi_state_ci.vertexBindingDescriptionCount = static_cast<uint32_t>(bindings.size()); |
| vi_state_ci.pVertexBindingDescriptions = bindings.data(); |
| vi_state_ci.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributes.size()); |
| vi_state_ci.pVertexAttributeDescriptions = attributes.data(); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.vi_ci_ = vi_state_ci; |
| pipe.CreateGraphicsPipeline(); |
| |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ClearAttachmentsAfterLoad) { |
| TEST_DESCRIPTION("Test for clearing attachments after load"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::Image image(*m_device, m_width, m_height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| auto image_view = image.CreateView(); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddColorAttachment(0); |
| rp.CreateRenderPass(); |
| vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle(), m_width, m_height); |
| |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkCmdClearAttachments-clear-after-load-color"); |
| |
| // On tiled renderers, this can also trigger a warning about LOAD_OP_LOAD causing a readback |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-vkCmdBeginRenderPass-attachment-needs-readback"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-DrawState-ClearCmdBeforeDraw"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-RenderPass-redundant-store"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-RenderPass-redundant-clear"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-RenderPass-inefficient-clear"); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.BeginRenderPass(rp, fb, m_width, m_height); |
| |
| VkClearAttachment color_attachment; |
| color_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| color_attachment.clearValue.color.float32[0] = 1.0; |
| color_attachment.clearValue.color.float32[1] = 1.0; |
| color_attachment.clearValue.color.float32[2] = 1.0; |
| color_attachment.clearValue.color.float32[3] = 1.0; |
| color_attachment.colorAttachment = 0; |
| VkClearRect clear_rect = {{{0, 0}, {m_width, m_height}}, 0, 1}; |
| |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect); |
| |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ClearAttachmentsAfterLoadSecondary) { |
| TEST_DESCRIPTION("Test for clearing attachments after load with secondary command buffers"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| // On tiled renderers, this can also trigger a warning about LOAD_OP_LOAD causing a readback |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-vkCmdBeginRenderPass-attachment-needs-readback"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-RenderPass-redundant-store"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-RenderPass-redundant-clear"); |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-RenderPass-inefficient-clear"); |
| |
| vkt::Image image(*m_device, m_width, m_height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| auto image_view = image.CreateView(); |
| |
| RenderPassSingleSubpass rp(*this); |
| rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, |
| VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); |
| rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); |
| rp.AddColorAttachment(0); |
| rp.CreateRenderPass(); |
| vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle(), m_width, m_height); |
| |
| CreatePipelineHelper pipe_masked(*this); |
| pipe_masked.gp_ci_.renderPass = rp; |
| pipe_masked.cb_attachments_.colorWriteMask = 0; |
| pipe_masked.CreateGraphicsPipeline(); |
| |
| CreatePipelineHelper pipe_writes(*this); |
| pipe_writes.gp_ci_.renderPass = rp; |
| pipe_writes.cb_attachments_.colorWriteMask = 0xf; |
| pipe_writes.CreateGraphicsPipeline(); |
| |
| m_command_buffer.Begin(); |
| |
| VkClearAttachment color_attachment; |
| color_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| color_attachment.clearValue.color.float32[0] = 1.0; |
| color_attachment.clearValue.color.float32[1] = 1.0; |
| color_attachment.clearValue.color.float32[2] = 1.0; |
| color_attachment.clearValue.color.float32[3] = 1.0; |
| color_attachment.colorAttachment = 0; |
| VkClearRect clear_rect = {{{0, 0}, {m_width, m_height}}, 0, 1}; |
| |
| VkRenderPassBeginInfo render_pass_begin_info = vku::InitStructHelper(); |
| render_pass_begin_info.renderPass = rp; |
| render_pass_begin_info.framebuffer = fb; |
| // need full clear |
| render_pass_begin_info.renderArea.extent = {m_width, m_height}; |
| |
| // Plain clear after load. |
| m_command_buffer.BeginRenderPass(render_pass_begin_info); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkCmdClearAttachments-clear-after-load-color"); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-DrawState-ClearCmdBeforeDraw"); |
| { |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect); |
| m_errorMonitor->VerifyFound(); |
| } |
| m_command_buffer.EndRenderPass(); |
| |
| // Test that a masked write is ignored before clear |
| m_command_buffer.BeginRenderPass(render_pass_begin_info); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkCmdClearAttachments-clear-after-load-color"); |
| { |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_masked); |
| vk::CmdDraw(m_command_buffer, 1, 0, 0, 0); |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect); |
| m_errorMonitor->VerifyFound(); |
| } |
| m_command_buffer.EndRenderPass(); |
| |
| // Test that an actual write will not trigger the clear warning |
| m_command_buffer.BeginRenderPass(render_pass_begin_info); |
| { |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_writes); |
| vk::CmdDraw(m_command_buffer, 1, 0, 0, 0); |
| vk::CmdClearAttachments(m_command_buffer, 1, &color_attachment, 1, &clear_rect); |
| } |
| m_command_buffer.EndRenderPass(); |
| |
| // Try the same thing, but now with secondary command buffers. |
| VkCommandBufferBeginInfo begin_info = vku::InitStructHelper(); |
| begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; |
| VkCommandBufferInheritanceInfo inherit_info = vku::InitStructHelper(); |
| begin_info.pInheritanceInfo = &inherit_info; |
| inherit_info.subpass = 0; |
| inherit_info.renderPass = rp; |
| |
| vkt::CommandBuffer secondary_clear(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| vkt::CommandBuffer secondary_draw_masked(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| vkt::CommandBuffer secondary_draw_write(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| |
| secondary_clear.Begin(&begin_info); |
| secondary_draw_masked.Begin(&begin_info); |
| secondary_draw_write.Begin(&begin_info); |
| |
| vk::CmdClearAttachments(secondary_clear, 1, &color_attachment, 1, &clear_rect); |
| |
| vk::CmdBindPipeline(secondary_draw_masked, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_masked); |
| vk::CmdDraw(secondary_draw_masked, 1, 0, 0, 0); |
| |
| vk::CmdBindPipeline(secondary_draw_write, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_writes); |
| vk::CmdDraw(secondary_draw_write, 1, 0, 0, 0); |
| |
| secondary_clear.End(); |
| secondary_draw_masked.End(); |
| secondary_draw_write.End(); |
| |
| // Plain clear after load. |
| m_command_buffer.BeginRenderPass(render_pass_begin_info, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkCmdClearAttachments-clear-after-load-color"); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-DrawState-ClearCmdBeforeDraw"); |
| { |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_clear.handle()); |
| m_errorMonitor->VerifyFound(); |
| } |
| m_command_buffer.EndRenderPass(); |
| |
| // Test that a masked write is ignored before clear |
| m_command_buffer.BeginRenderPass(render_pass_begin_info, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-vkCmdClearAttachments-clear-after-load-color"); |
| { |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_draw_masked.handle()); |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_clear.handle()); |
| m_errorMonitor->VerifyFound(); |
| } |
| m_command_buffer.EndRenderPass(); |
| |
| // Test that an actual write will not trigger the clear warning |
| m_command_buffer.BeginRenderPass(render_pass_begin_info, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); |
| { |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_draw_write.handle()); |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_clear.handle()); |
| } |
| m_command_buffer.EndRenderPass(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, TripleBufferingTest) { |
| TEST_DESCRIPTION("Test for usage of triple buffering"); |
| |
| AddSurfaceExtension(); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| RETURN_IF_SKIP(InitSurface()); |
| InitSwapchainInfo(); |
| |
| VkBool32 supported; |
| vk::GetPhysicalDeviceSurfaceSupportKHR(Gpu(), m_device->graphics_queue_node_index_, m_surface, &supported); |
| if (!supported) { |
| GTEST_SKIP() << "Graphics queue does not support present"; |
| } |
| |
| bool fifo_present = false; |
| for (const auto &present_mode : m_surface_present_modes) { |
| if (present_mode == VK_PRESENT_MODE_FIFO_KHR) { |
| fifo_present = true; |
| break; |
| } |
| } |
| if (!fifo_present) { |
| GTEST_SKIP() << "fifo present mode not supported"; |
| } |
| |
| VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| VkSurfaceTransformFlagBitsKHR preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; |
| |
| VkSwapchainCreateInfoKHR swapchain_create_info = vku::InitStructHelper(); |
| swapchain_create_info.surface = m_surface; |
| swapchain_create_info.minImageCount = 2; |
| swapchain_create_info.imageFormat = m_surface_formats[0].format; |
| swapchain_create_info.imageColorSpace = m_surface_formats[0].colorSpace; |
| swapchain_create_info.imageExtent = m_surface_capabilities.minImageExtent; |
| swapchain_create_info.imageArrayLayers = 1; |
| swapchain_create_info.imageUsage = imageUsage; |
| swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| swapchain_create_info.preTransform = preTransform; |
| swapchain_create_info.compositeAlpha = m_surface_composite_alpha; |
| swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; |
| swapchain_create_info.clipped = VK_FALSE; |
| swapchain_create_info.oldSwapchain = 0; |
| |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, |
| "BestPractices-vkCreateSwapchainKHR-suboptimal-swapchain-image-count"); |
| // skip core checks |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkSwapchainCreateInfoKHR-presentMode-02839"); |
| m_swapchain.Init(*m_device, swapchain_create_info); |
| m_errorMonitor->VerifyFound(); |
| |
| // Lazy way to not query |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkSwapchainCreateInfoKHR-presentMode-02839"); |
| swapchain_create_info.minImageCount = 3; |
| m_swapchain.Init(*m_device, swapchain_create_info); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SwapchainCreationTest) { |
| TEST_DESCRIPTION("Test for correct swapchain creation"); |
| |
| AddSurfaceExtension(); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| RETURN_IF_SKIP(InitSurface()); |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| m_surface_composite_alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; |
| #else |
| m_surface_composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
| #endif |
| |
| VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| VkSurfaceTransformFlagBitsKHR preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; |
| |
| VkSwapchainCreateInfoKHR swapchain_create_info = vku::InitStructHelper(); |
| swapchain_create_info.surface = m_surface; |
| swapchain_create_info.minImageCount = 3; |
| swapchain_create_info.imageArrayLayers = 1; |
| swapchain_create_info.imageUsage = imageUsage; |
| swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| swapchain_create_info.preTransform = preTransform; |
| swapchain_create_info.compositeAlpha = m_surface_composite_alpha; |
| swapchain_create_info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; |
| swapchain_create_info.clipped = VK_FALSE; |
| swapchain_create_info.oldSwapchain = 0; |
| |
| // Test for successful swapchain creation when GetPhysicalDeviceSurfaceCapabilitiesKHR() and |
| // GetPhysicalDeviceSurfaceFormatsKHR() are queried as expected and GetPhysicalDeviceSurfacePresentModesKHR() is not called but |
| // the present mode is VK_PRESENT_MODE_FIFO_KHR |
| vk::GetPhysicalDeviceSurfaceCapabilitiesKHR(Gpu(), m_surface, &m_surface_capabilities); |
| |
| uint32_t format_count; |
| vk::GetPhysicalDeviceSurfaceFormatsKHR(Gpu(), m_surface, &format_count, nullptr); |
| if (format_count != 0) { |
| m_surface_formats.resize(format_count); |
| vk::GetPhysicalDeviceSurfaceFormatsKHR(Gpu(), m_surface, &format_count, m_surface_formats.data()); |
| } |
| |
| swapchain_create_info.imageFormat = m_surface_formats[0].format; |
| swapchain_create_info.imageColorSpace = m_surface_formats[0].colorSpace; |
| swapchain_create_info.imageExtent = m_surface_capabilities.minImageExtent; |
| |
| // GetPhysicalDeviceSurfacePresentModesKHR() not called before trying to create a swapchain |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkCreateSwapchainKHR-present-mode-no-surface"); |
| |
| // Warning is thrown any time the present mode is not VK_PRESENT_MODE_FIFO_KHR, but only with ARM BP |
| m_errorMonitor->SetAllowedFailureMsg("BestPractices-Arm-vkCreateSwapchainKHR-swapchain-presentmode-not-fifo"); |
| |
| // present mode might not be supported |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkSwapchainCreateInfoKHR-presentMode-01281"); |
| // Lazy way to not query |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkSwapchainCreateInfoKHR-presentMode-02839"); |
| m_swapchain.Init(*m_device, swapchain_create_info); |
| m_errorMonitor->VerifyFound(); |
| |
| // Lazy way to not query |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkSwapchainCreateInfoKHR-presentMode-02839"); |
| swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; |
| m_swapchain.Init(*m_device, swapchain_create_info); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ExpectedQueryDetails) { |
| TEST_DESCRIPTION("Check that GetPhysicalDeviceQueueFamilyProperties is working as expected"); |
| |
| // Vulkan 1.1 required to test vkGetPhysicalDeviceQueueFamilyProperties2 |
| app_info_.apiVersion = VK_API_VERSION_1_1; |
| // VK_KHR_get_physical_device_properties2 required to test vkGetPhysicalDeviceQueueFamilyProperties2KHR |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| const vkt::PhysicalDevice phys_device_obj(gpu_); |
| |
| std::vector<VkQueueFamilyProperties> queue_family_props; |
| |
| // Ensure we can find a graphics queue family. |
| uint32_t queue_count = 0; |
| vk::GetPhysicalDeviceQueueFamilyProperties(phys_device_obj, &queue_count, nullptr); |
| |
| queue_family_props.resize(queue_count); |
| vk::GetPhysicalDeviceQueueFamilyProperties(phys_device_obj, &queue_count, queue_family_props.data()); |
| |
| // Now for GetPhysicalDeviceQueueFamilyProperties2 |
| std::vector<VkQueueFamilyProperties2> queue_family_props2; |
| vk::GetPhysicalDeviceQueueFamilyProperties2(phys_device_obj, &queue_count, nullptr); |
| |
| queue_family_props2.resize(queue_count); |
| for (uint32_t i = 0; i < queue_count; i++) { |
| queue_family_props2[i] = vku::InitStructHelper(); |
| } |
| vk::GetPhysicalDeviceQueueFamilyProperties2(phys_device_obj, &queue_count, queue_family_props2.data()); |
| |
| // And for GetPhysicalDeviceQueueFamilyProperties2KHR |
| vk::GetPhysicalDeviceQueueFamilyProperties2KHR(phys_device_obj, &queue_count, nullptr); |
| |
| queue_family_props2.resize(queue_count); |
| vk::GetPhysicalDeviceQueueFamilyProperties2KHR(phys_device_obj, &queue_count, queue_family_props2.data()); |
| |
| vkt::Device device(phys_device_obj, m_device_extension_names); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, MissingQueryDetails) { |
| TEST_DESCRIPTION("Check that GetPhysicalDeviceQueueFamilyProperties generates appropriate query warning"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| const vkt::PhysicalDevice phys_device_obj(gpu_); |
| |
| std::vector<VkQueueFamilyProperties> queue_family_props(1); |
| uint32_t queue_count = static_cast<uint32_t>(queue_family_props.size()); |
| |
| // might only be a queue_count of 1, so check and then do "real" test to make sure error is detected |
| m_errorMonitor->SetUnexpectedError("BestPractices-GetPhysicalDeviceQueueFamilyProperties-CountMismatch"); |
| vk::GetPhysicalDeviceQueueFamilyProperties(phys_device_obj, &queue_count, queue_family_props.data()); |
| m_errorMonitor->VerifyFound(); |
| |
| if (queue_count > 1) { |
| m_errorMonitor->SetDesiredWarning("BestPractices-GetPhysicalDeviceQueueFamilyProperties-CountMismatch"); |
| vk::GetPhysicalDeviceQueueFamilyProperties(phys_device_obj, &queue_count, queue_family_props.data()); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| // Now get information correctly |
| vkt::QueueCreateInfoArray queue_info(phys_device_obj.queue_properties_, true); |
| // Only request creation with queuefamilies that have at least one queue |
| std::vector<VkDeviceQueueCreateInfo> create_queue_infos; |
| auto qci = queue_info.Data(); |
| for (uint32_t j = 0; j < queue_info.Size(); ++j) { |
| if (qci[j].queueCount) { |
| create_queue_infos.push_back(qci[j]); |
| } |
| } |
| |
| VkPhysicalDeviceFeatures all_features{}; |
| VkDeviceCreateInfo device_ci = vku::InitStructHelper(); |
| device_ci.queueCreateInfoCount = create_queue_infos.size(); |
| device_ci.pQueueCreateInfos = create_queue_infos.data(); |
| device_ci.enabledLayerCount = 0; |
| device_ci.ppEnabledLayerNames = NULL; |
| device_ci.enabledExtensionCount = 0; |
| device_ci.ppEnabledExtensionNames = nullptr; |
| device_ci.pEnabledFeatures = &all_features; |
| |
| // vkGetPhysicalDeviceFeatures has not been called, so this should produce a warning |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkCreateDevice-physical-device-features-not-retrieved"); |
| VkDevice device; |
| vk::CreateDevice(phys_device_obj, &device_ci, nullptr, &device); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, CreatePipelineVsFsTypeMismatchArraySize) { |
| TEST_DESCRIPTION("Test that an error is produced for mismatched array sizes across the vertex->fragment shader interface"); |
| |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| const char *vsSource = R"glsl( |
| #version 450 |
| layout(location=0) out float x[2]; |
| void main(){ |
| x[0] = 0; x[1] = 0; |
| gl_Position = vec4(1); |
| } |
| )glsl"; |
| const char *fsSource = R"glsl( |
| #version 450 |
| layout(location=0) in float x[1]; |
| layout(location=0) out vec4 color; |
| void main(){ |
| color = vec4(x[0]); |
| } |
| )glsl"; |
| |
| VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| const auto set_info = [&](CreatePipelineHelper &helper) { |
| helper.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| }; |
| CreatePipelineHelper::OneshotTest(*this, set_info, kPerformanceWarningBit, "WARNING-Shader-OutputNotConsumed"); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, WorkgroupSizeDeprecated) { |
| TEST_DESCRIPTION("SPIR-V 1.6 deprecated WorkgroupSize build-in."); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const char *spv_source = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %uint = OpTypeInt 32 0 |
| %uint_1 = OpConstant %uint 1 |
| %v3uint = OpTypeVector %uint 3 |
| %gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto set_info = [&](CreateComputePipelineHelper& helper) { |
| helper.cs_ = VkShaderObj(*m_device, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); |
| }; |
| CreateComputePipelineHelper::OneshotTest(*this, set_info, kWarningBit, "BestPractices-SpirvDeprecated_WorkgroupSize"); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ImageExtendedUsageWithoutMutableFormat) { |
| TEST_DESCRIPTION("Create image with extended usage bit but not mutable format bit."); |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_2_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| auto image_ci = vkt::Image::ImageCreateInfo2D(256, 256, 1, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| image_ci.flags = VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; |
| VkImage image; |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkCreateImage-CreateFlags"); |
| vk::CreateImage(device(), &image_ci, nullptr, &image); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| #if GTEST_IS_THREADSAFE |
| TEST_F(VkBestPracticesLayerTest, ThreadUpdateDescriptorUpdateAfterBindNoCollision) { |
| TEST_DESCRIPTION("Two threads updating the same UAB descriptor set, expected not to generate a threading error"); |
| |
| AddRequiredExtensions(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_3_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::descriptorBindingStorageBufferUpdateAfterBind); |
| RETURN_IF_SKIP(Init()); |
| InitRenderTarget(); |
| |
| OneOffDescriptorIndexingSet descriptor_set(m_device, { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, |
| nullptr, VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, |
| nullptr, VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT}, |
| }); |
| vkt::Buffer buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); |
| |
| ThreadTestData data; |
| data.device = device(); |
| data.descriptorSet = descriptor_set.set_; |
| data.binding = 0; |
| data.buffer = buffer; |
| std::atomic<bool> bailout{false}; |
| data.bailout = &bailout; |
| m_errorMonitor->SetBailout(data.bailout); |
| |
| // Update descriptors from another thread. |
| std::thread thread(UpdateDescriptor, &data); |
| // Update descriptors from this thread at the same time. |
| |
| ThreadTestData data2; |
| data2.device = device(); |
| data2.descriptorSet = descriptor_set.set_; |
| data2.binding = 1; |
| data2.buffer = buffer; |
| data2.bailout = &bailout; |
| |
| UpdateDescriptor(&data2); |
| |
| thread.join(); |
| |
| m_errorMonitor->SetBailout(NULL); |
| } |
| #endif // GTEST_IS_THREADSAFE |
| |
| TEST_F(VkBestPracticesLayerTest, TransitionFromUndefinedToReadOnly) { |
| TEST_DESCRIPTION("Transition image layout from undefined to read only"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::Image image(*m_device, 128, 128, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| |
| VkClearColorValue color_clear_value = {}; |
| color_clear_value.uint32[0] = 255; |
| VkImageSubresourceRange clear_range; |
| clear_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| clear_range.baseMipLevel = 0; |
| clear_range.baseArrayLayer = 0; |
| clear_range.layerCount = 1; |
| clear_range.levelCount = 1; |
| |
| VkImageMemoryBarrier img_barrier = vku::InitStructHelper(); |
| img_barrier.srcAccessMask = 0; |
| img_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; |
| img_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| img_barrier.image = image; |
| img_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
| |
| m_command_buffer.Begin(); |
| |
| vk::CmdClearColorImage(m_command_buffer, image, VK_IMAGE_LAYOUT_GENERAL, &color_clear_value, 1, &clear_range); |
| |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkCmdPipelineBarrier-pImageMemoryBarriers-02820"); // skip core checks |
| m_errorMonitor->SetDesiredWarning("BestPractices-ImageMemoryBarrier-TransitionUndefinedToReadOnly"); |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, |
| 0, nullptr, 1, &img_barrier); |
| m_errorMonitor->VerifyFound(); |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, OverAllocateFromDescriptorPool) { |
| TEST_DESCRIPTION("Attempt to allocate more sets and descriptors than descriptor pool has available."); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| VkDescriptorPoolSize ds_type_count = {VK_DESCRIPTOR_TYPE_SAMPLER, 8}; |
| VkDescriptorPoolCreateInfo ds_pool_ci = vku::InitStructHelper(); |
| ds_pool_ci.flags = 0; |
| ds_pool_ci.maxSets = 3; |
| ds_pool_ci.poolSizeCount = 1; |
| ds_pool_ci.pPoolSizes = &ds_type_count; |
| |
| vkt::DescriptorPool ds_pool(*m_device, ds_pool_ci); |
| const vkt::DescriptorSetLayout ds_layout_samp(*m_device, {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}); |
| |
| // Try to allocate 2 sets when pool only has 1 set |
| VkDescriptorSet descriptor_sets[4]; |
| VkDescriptorSetLayout set_layouts[2] = {ds_layout_samp, ds_layout_samp}; |
| VkDescriptorSetAllocateInfo alloc_info = vku::InitStructHelper(); |
| alloc_info.descriptorSetCount = 2; |
| alloc_info.descriptorPool = ds_pool; |
| alloc_info.pSetLayouts = set_layouts; |
| vk::AllocateDescriptorSets(device(), &alloc_info, &descriptor_sets[0]); |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkAllocateDescriptorSets-EmptyDescriptorPool"); |
| vk::AllocateDescriptorSets(device(), &alloc_info, &descriptor_sets[2]); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, OverAllocateTypeFromDescriptorPool) { |
| TEST_DESCRIPTION("Attempt to allocate more sets and descriptors than descriptor pool has available."); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| VkDescriptorPoolSize ds_type_count = {VK_DESCRIPTOR_TYPE_SAMPLER, 3}; |
| VkDescriptorPoolCreateInfo ds_pool_ci = vku::InitStructHelper(); |
| ds_pool_ci.flags = 0; |
| ds_pool_ci.maxSets = 4; |
| ds_pool_ci.poolSizeCount = 1; |
| ds_pool_ci.pPoolSizes = &ds_type_count; |
| |
| vkt::DescriptorPool ds_pool(*m_device, ds_pool_ci); |
| |
| const vkt::DescriptorSetLayout ds_layout_samp(*m_device, {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}); |
| |
| // Try to allocate 2 sets when pool only has 1 set |
| VkDescriptorSet descriptor_sets[4]; |
| VkDescriptorSetLayout set_layouts[2] = {ds_layout_samp, ds_layout_samp}; |
| VkDescriptorSetAllocateInfo alloc_info = vku::InitStructHelper(); |
| alloc_info.descriptorSetCount = 2; |
| alloc_info.descriptorPool = ds_pool; |
| alloc_info.pSetLayouts = set_layouts; |
| vk::AllocateDescriptorSets(device(), &alloc_info, &descriptor_sets[0]); |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkAllocateDescriptorSets-EmptyDescriptorPoolType"); |
| vk::AllocateDescriptorSets(device(), &alloc_info, &descriptor_sets[2]); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, RenderPassClearWithoutLoadOpClear) { |
| TEST_DESCRIPTION("Test for clearing a RenderPass with non-zero clearValueCount without any VK_ATTACHMENT_LOAD_OP_CLEAR"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| // Setup necessary objects correctly |
| |
| // Bigger size to avoid small allocation best practices warning |
| const unsigned int w = 1920; |
| const unsigned int h = 1080; |
| |
| vkt::Image image(*m_device, w, h, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| // Setup RenderPass |
| VkAttachmentDescription attachment{}; |
| attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // Specify that we do nothing with the contents of the attached image |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| attachment.format = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| VkAttachmentReference ar{}; |
| ar.attachment = 0; |
| ar.layout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| VkSubpassDescription spd{}; |
| spd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| spd.colorAttachmentCount = 1; |
| spd.pColorAttachments = &ar; |
| |
| VkRenderPassCreateInfo rp_info = vku::InitStructHelper(); |
| rp_info.attachmentCount = 1; |
| rp_info.pAttachments = &attachment; |
| rp_info.subpassCount = 1; |
| rp_info.pSubpasses = &spd; |
| |
| vkt::RenderPass rp(*m_device, rp_info); |
| vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle(), w, h); |
| |
| m_command_buffer.Begin(); |
| |
| // Create a useless VkClearValue |
| VkClearValue cv{}; |
| cv.color = VkClearColorValue{}; |
| std::fill(std::begin(cv.color.float32), std::begin(cv.color.float32) + 4, 0.0f); |
| |
| VkRenderPassBeginInfo begin_info = vku::InitStructHelper(); |
| begin_info.clearValueCount = 1; // Pass one clearValue, in conflict with attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE |
| begin_info.pClearValues = &cv; |
| begin_info.renderPass = rp; |
| begin_info.renderArea.extent = {w, h}; |
| begin_info.framebuffer = fb; |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-ClearValueWithoutLoadOpClear"); |
| m_command_buffer.BeginRenderPass(begin_info); |
| m_errorMonitor->VerifyFound(); |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, RenderPassClearValueCountHigherThanAttachmentCount) { |
| TEST_DESCRIPTION( |
| "Test for beginning a RenderPass with VkRenderPassBeginInfo.clearValueCount > VkRenderPassCreateInfo.attachmentCount"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| // Setup necessary objects correctly |
| |
| // Bigger size to avoid small allocation best practices warning |
| const unsigned int w = 1920; |
| const unsigned int h = 1080; |
| |
| vkt::Image image(*m_device, w, h, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| // Setup RenderPass |
| VkAttachmentDescription attachment{}; |
| attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachment.format = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| VkAttachmentReference ar{}; |
| ar.attachment = 0; |
| ar.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| VkSubpassDescription spd{}; |
| spd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| spd.colorAttachmentCount = 1; |
| spd.pColorAttachments = &ar; |
| |
| VkRenderPassCreateInfo rp_info = vku::InitStructHelper(); |
| rp_info.attachmentCount = 1; // There is only one attachment |
| rp_info.pAttachments = &attachment; |
| rp_info.subpassCount = 1; |
| rp_info.pSubpasses = &spd; |
| |
| vkt::RenderPass rp(*m_device, rp_info); |
| vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle(), w, h); |
| |
| m_command_buffer.Begin(); |
| |
| // Create two VkClearValues |
| VkClearValue cv[2]; |
| |
| // Create a useful VkClearValue |
| cv[0].color = VkClearColorValue{}; |
| std::fill(std::begin(cv[0].color.float32), std::begin(cv[0].color.float32) + 4, 0.0f); |
| |
| // Create a useless VkClearValue |
| cv[1].color = VkClearColorValue{}; |
| std::fill(std::begin(cv[1].color.float32), std::begin(cv[1].color.float32) + 4, 0.0f); |
| |
| VkRenderPassBeginInfo begin_info = vku::InitStructHelper(); |
| begin_info.clearValueCount = 2; // Pass 2 clearValues, in conflict with VkRenderPassCreateInfo.attachmentCount == 1 meaning the |
| // second clearValue will be ignored |
| begin_info.pClearValues = cv; |
| begin_info.renderPass = rp; |
| begin_info.renderArea.extent = {w, h}; |
| begin_info.framebuffer = fb; |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-ClearValueCountHigherThanAttachmentCount"); |
| m_command_buffer.BeginRenderPass(begin_info); |
| m_errorMonitor->VerifyFound(); |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, DontCareThenLoad) { |
| TEST_DESCRIPTION("Test for storing an attachment with STORE_OP_DONT_CARE then loading with LOAD_OP_LOAD"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| // Setup necessary objects correctly |
| |
| const unsigned int w = 100; |
| const unsigned int h = 100; |
| |
| vkt::Image image(*m_device, w, h, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| image.SetLayout(VK_IMAGE_LAYOUT_GENERAL); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| // Setup first RenderPass |
| VkAttachmentDescription attachment{}; |
| attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clearing as only modification |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Dont care even though we will load afterwards |
| attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachment.format = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| VkAttachmentReference ar{}; |
| ar.attachment = 0; |
| ar.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| VkSubpassDescription spd{}; |
| spd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| spd.colorAttachmentCount = 1; |
| spd.pColorAttachments = &ar; |
| |
| VkRenderPassCreateInfo rp_info = vku::InitStructHelper(); |
| rp_info.attachmentCount = 1; |
| rp_info.pAttachments = &attachment; |
| rp_info.subpassCount = 1; |
| rp_info.pSubpasses = &spd; |
| |
| vkt::RenderPass rp1(*m_device, rp_info); |
| |
| // Setup second RenderPass |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; // Loading even though was stored with dont care |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| vkt::RenderPass rp2(*m_device, rp_info); |
| vkt::Framebuffer fb(*m_device, rp1, 1, &image_view.handle(), w, h); |
| |
| m_command_buffer.Begin(); |
| |
| // All white |
| VkClearValue cv; |
| cv.color = VkClearColorValue{}; |
| std::fill(std::begin(cv.color.float32), std::begin(cv.color.float32) + 4, 1.0f); |
| |
| // Begin first renderpass |
| m_command_buffer.BeginRenderPass(rp1, fb, w, h, 1, &cv); |
| |
| m_command_buffer.EndRenderPass(); |
| |
| // Begin second renderpass |
| m_command_buffer.BeginRenderPass(rp2, fb, w, h, 0, nullptr); |
| |
| m_command_buffer.EndRenderPass(); |
| |
| m_command_buffer.End(); |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-StoreOpDontCareThenLoadOpLoad"); |
| m_default_queue->Submit(m_command_buffer); |
| m_errorMonitor->VerifyFound(); |
| |
| m_default_queue->Wait(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ExclusiveImageMultiQueueUsage) { |
| TEST_DESCRIPTION("Test for using a queue exclusive image on multiple queues"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::Queue *graphics_queue = m_device->QueuesWithGraphicsCapability()[0]; |
| |
| vkt::Queue *compute_queue = nullptr; |
| for (uint32_t i = 0; i < m_device->QueuesWithComputeCapability().size(); ++i) { |
| auto cqi = m_device->QueuesWithComputeCapability()[i]; |
| if (cqi->family_index != graphics_queue->family_index) { |
| compute_queue = cqi; |
| break; |
| } |
| } |
| |
| if (compute_queue == nullptr) { |
| GTEST_SKIP() << "No separate queue family from graphics queue"; |
| } |
| |
| // Setup necessary objects correctly |
| |
| const unsigned int w = 100; |
| const unsigned int h = 100; |
| |
| vkt::Image image(*m_device, w, h, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT); |
| image.SetLayout(VK_IMAGE_LAYOUT_GENERAL); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| // Setup RenderPass |
| VkAttachmentDescription attachment{}; |
| attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
| attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clearing so warning will not trigger on second pass |
| attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // Store written image for next queue family |
| attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| attachment.format = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| VkAttachmentReference ar{}; |
| ar.attachment = 0; |
| ar.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| VkSubpassDescription spd{}; |
| spd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| spd.colorAttachmentCount = 1; |
| spd.pColorAttachments = &ar; |
| |
| VkRenderPassCreateInfo rp_info = vku::InitStructHelper(); |
| rp_info.attachmentCount = 1; |
| rp_info.pAttachments = &attachment; |
| rp_info.subpassCount = 1; |
| rp_info.pSubpasses = &spd; |
| |
| vkt::RenderPass rp(*m_device, rp_info); |
| vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle(), w, h); |
| |
| vkt::CommandPool graphics_pool(*m_device, graphics_queue->family_index); |
| |
| vkt::CommandBuffer graphics_buffer(*m_device, graphics_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| |
| VkClearValue cv; |
| cv.color = VkClearColorValue{}; |
| std::fill(std::begin(cv.color.float32), std::begin(cv.color.float32) + 4, 1.0f); |
| |
| VkRenderPassBeginInfo begin_info = vku::InitStructHelper(); |
| begin_info.clearValueCount = 1; |
| begin_info.pClearValues = &cv; |
| begin_info.renderPass = rp; |
| begin_info.renderArea.extent = {w, h}; |
| begin_info.framebuffer = fb; |
| |
| // Prepare compute |
| |
| const char *cs = R"glsl(#version 450 |
| layout(local_size_x=1, local_size_y=1) in; |
| layout(set=0, binding=0, rgba32f) uniform image2D img; |
| void main(){ |
| vec4 v = imageLoad(img, ivec2(gl_GlobalInvocationID.xy)); |
| } |
| )glsl"; |
| |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, cs, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.dsl_bindings_[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; |
| pipe.dsl_bindings_[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; |
| pipe.CreateComputePipeline(); |
| |
| vkt::Sampler sampler(*m_device, SafeSaneSamplerCreateInfo()); |
| |
| pipe.descriptor_set_.WriteDescriptorImageInfo(0, image_view, sampler, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, |
| VK_IMAGE_LAYOUT_GENERAL); |
| pipe.descriptor_set_.UpdateDescriptorSets(); |
| |
| vkt::CommandPool compute_pool(*m_device, compute_queue->family_index); |
| |
| vkt::CommandBuffer compute_buffer(*m_device, compute_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| |
| // Record command buffers without queue transition |
| |
| // Record graphics command buffer |
| graphics_buffer.Begin(); |
| |
| graphics_buffer.BeginRenderPass(begin_info); |
| |
| graphics_buffer.EndRenderPass(); |
| |
| graphics_buffer.End(); |
| |
| graphics_queue->Submit(graphics_buffer); |
| graphics_queue->Wait(); |
| |
| // Record compute command buffer |
| compute_buffer.Begin(); |
| |
| vk::CmdBindPipeline(compute_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| |
| vk::CmdBindDescriptorSets(compute_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, |
| &pipe.descriptor_set_.set_, 0, nullptr); |
| |
| vk::CmdDispatch(compute_buffer, w, h, 1); |
| |
| compute_buffer.End(); |
| |
| // Warning should trigger as we are potentially accessing undefined resources |
| m_errorMonitor->SetDesiredWarning("BestPractices-ConcurrentUsageOfExclusiveImage"); |
| compute_queue->Submit(compute_buffer); |
| compute_queue->Wait(); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::ResetCommandPool(device(), graphics_pool, 0); |
| vk::ResetCommandPool(device(), compute_pool, 0); |
| |
| // Record command buffers with queue transition |
| |
| // Queue transition barrier, same for release and acquire |
| VkImageMemoryBarrier barrier = vku::InitStructHelper(); |
| barrier.image = image; |
| barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; // only matters for release |
| barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; // only matters for acquire |
| barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; |
| barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; |
| barrier.srcQueueFamilyIndex = graphics_queue->family_index; |
| barrier.dstQueueFamilyIndex = compute_queue->family_index; |
| barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; |
| barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; |
| |
| // Record graphics command buffer |
| graphics_buffer.Begin(); |
| |
| graphics_buffer.BeginRenderPass(begin_info); |
| |
| graphics_buffer.EndRenderPass(); |
| |
| vk::CmdPipelineBarrier(graphics_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, |
| VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &barrier); |
| |
| graphics_buffer.End(); |
| graphics_queue->Submit(graphics_buffer); |
| graphics_queue->Wait(); |
| |
| // Record compute command buffer |
| compute_buffer.Begin(); |
| |
| vk::CmdPipelineBarrier(compute_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &barrier); |
| |
| vk::CmdBindPipeline(compute_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| |
| vk::CmdBindDescriptorSets(compute_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, |
| &pipe.descriptor_set_.set_, 0, nullptr); |
| |
| vk::CmdDispatch(compute_buffer, w, h, 1); |
| |
| compute_buffer.End(); |
| |
| // Warning shouldn't trigger |
| m_errorMonitor->SetDesiredWarning("BestPractices-ConcurrentUsageOfExclusiveImage"); |
| compute_queue->Submit(compute_buffer); |
| compute_queue->Wait(); |
| m_errorMonitor->Finish(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, ImageMemoryBarrierAccessLayoutCombinations) { |
| TEST_DESCRIPTION("Transition image layout from undefined to read only"); |
| AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::Image image(*m_device, 128, 128, VK_FORMAT_B8G8R8A8_UNORM, |
| VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); |
| |
| VkClearColorValue color_clear_value = {}; |
| color_clear_value.uint32[0] = 255; |
| VkImageSubresourceRange clear_range; |
| clear_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| clear_range.baseMipLevel = 0; |
| clear_range.baseArrayLayer = 0; |
| clear_range.layerCount = 1; |
| clear_range.levelCount = 1; |
| |
| VkImageMemoryBarrier img_barrier = vku::InitStructHelper(); |
| img_barrier.srcAccessMask = 0; |
| img_barrier.dstAccessMask = 0; |
| img_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; |
| img_barrier.image = image; |
| img_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
| |
| m_command_buffer.Begin(); |
| |
| vk::CmdClearColorImage(m_command_buffer, image, VK_IMAGE_LAYOUT_GENERAL, &color_clear_value, 1, &clear_range); |
| |
| // GENERAL - Any |
| // note: the table in PR 2918 originally said that 0 was not allowed, but this was incorrect. See Issue #4735 |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; |
| img_barrier.dstAccessMask = 0; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, |
| 0, nullptr, 1, &img_barrier); |
| |
| // Every table entry includes an implicit "can be 0" |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| img_barrier.dstAccessMask = 0; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, |
| 0, nullptr, 1, &img_barrier); |
| |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| img_barrier.dstAccessMask = 0; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, |
| 0, nullptr, 1, &img_barrier); |
| |
| // PRESENT_SRC_KHR - Must be 0 |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| img_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; |
| m_errorMonitor->SetDesiredWarning("BestPractices-ImageBarrierAccessLayout"); |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, |
| 0, nullptr, 1, &img_barrier); |
| m_errorMonitor->VerifyFound(); |
| |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| img_barrier.dstAccessMask = 0; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, |
| 0, nullptr, 1, &img_barrier); |
| |
| { |
| VkImageMemoryBarrier2 img_barrier2 = vku::InitStructHelper(); |
| img_barrier2.srcAccessMask = 0; |
| img_barrier2.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| img_barrier2.image = image; |
| img_barrier2.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
| |
| img_barrier2.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; |
| img_barrier2.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkImageMemoryBarrier2-dstAccessMask-03903"); // skip core checks |
| m_errorMonitor->SetDesiredWarning("BestPractices-ImageBarrierAccessLayout"); |
| m_command_buffer.BarrierKHR(img_barrier2); |
| m_errorMonitor->VerifyFound(); |
| |
| // make sure bits above UINT32_MAX are detected |
| img_barrier2.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| img_barrier2.dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT; |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkImageMemoryBarrier2-dstAccessMask-03907"); // skip core checks |
| m_errorMonitor->SetDesiredWarning("BestPractices-ImageBarrierAccessLayout"); |
| m_command_buffer.BarrierKHR(img_barrier2); |
| m_errorMonitor->VerifyFound(); |
| |
| img_barrier2.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| img_barrier2.dstAccessMask = 0; |
| m_command_buffer.BarrierKHR(img_barrier2); |
| |
| m_command_buffer.End(); |
| } |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, NonSimultaneousSecondaryMarksPrimary) { |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| |
| secondary.Begin(); |
| secondary.End(); |
| |
| VkCommandBufferBeginInfo cbbi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
| nullptr, |
| VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, |
| nullptr, |
| }; |
| |
| m_command_buffer.Begin(&cbbi); |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkCmdExecuteCommands-CommandBufferSimultaneousUse"); |
| vk::CmdExecuteCommands(m_command_buffer, 1, &secondary.handle()); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, NoCreateSwapchainPresentModes) { |
| TEST_DESCRIPTION("With swapchain maintenance 1, CreateSwapchain with VkPresentModesCreateInfoEXT"); |
| |
| AddSurfaceExtension(); |
| AddRequiredExtensions(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredExtensions(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| |
| RETURN_IF_SKIP(InitState()); |
| RETURN_IF_SKIP(InitSurface()); |
| m_errorMonitor->SetAllowedFailureMsg("VUID-VkSwapchainCreateInfoKHR-presentMode-02839"); // skip core checks |
| m_errorMonitor->SetDesiredWarning("BestPractices-vkCreateSwapchainKHR-no-VkSwapchainPresentModesCreateInfoKHR-provided"); |
| m_swapchain = CreateSwapchain(m_surface, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, PipelineWithoutRenderPassOrRenderingInfo) { |
| TEST_DESCRIPTION("Create pipeline with VK_NULL_HANDLE render pass and no VkPipelineRenderingCreateInfo in pNext chain"); |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::dynamicRendering); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.gp_ci_.renderPass = VK_NULL_HANDLE; |
| pipe.cb_ci_.attachmentCount = 0; |
| pipe.CreateGraphicsPipeline(); |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-Pipeline-NoRendering"); |
| pipe.CreateGraphicsPipeline(); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, GetQueryPoolResultsWithoutBegin) { |
| TEST_DESCRIPTION("Get query pool results without ever beginning the query"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| vkt::QueryPool query_pool(*m_device, VK_QUERY_TYPE_OCCLUSION, 1); |
| |
| m_command_buffer.Begin(); |
| vk::CmdResetQueryPool(m_command_buffer, query_pool, 0u, 1u); |
| m_command_buffer.End(); |
| |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| |
| uint32_t data = 0u; |
| m_errorMonitor->SetDesiredWarning("BestPractices-QueryPool-Unavailable"); |
| vk::GetQueryPoolResults(*m_device, query_pool, 0u, 1u, sizeof(uint32_t), &data, sizeof(uint32_t), 0u); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, NonOptimalResolveFormat) { |
| TEST_DESCRIPTION("Create a render pass with a resolve attachment that is not optimal"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; |
| |
| VkSubpassResolvePerformanceQueryEXT performance_query = vku::InitStructHelper(); |
| VkFormatProperties2 format_properties2 = vku::InitStructHelper(&performance_query); |
| vk::GetPhysicalDeviceFormatProperties2(Gpu(), format, &format_properties2); |
| if (performance_query.optimal == VK_TRUE) { |
| GTEST_SKIP() << "VkSubpassResolvePerformanceQueryEXT::optimal required to be VK_FALSE."; |
| } |
| |
| VkAttachmentReference color_attachment; |
| color_attachment.attachment = 0u; |
| color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| VkAttachmentReference resolve_attachment; |
| resolve_attachment.attachment = 1u; |
| resolve_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| VkAttachmentDescription attachments[2]; |
| attachments[0] = {}; |
| attachments[0].format = format; |
| attachments[0].samples = VK_SAMPLE_COUNT_2_BIT; |
| attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachments[1] = {}; |
| attachments[1].format = format; |
| attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; |
| attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachments[1].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachments[1].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| VkSubpassDescription subpass = {}; |
| subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| subpass.colorAttachmentCount = 1u; |
| subpass.pColorAttachments = &color_attachment; |
| subpass.pResolveAttachments = &resolve_attachment; |
| |
| VkRenderPassCreateInfo render_pass_ci = vku::InitStructHelper(); |
| render_pass_ci.attachmentCount = 2u; |
| render_pass_ci.pAttachments = attachments; |
| render_pass_ci.subpassCount = 1u; |
| render_pass_ci.pSubpasses = &subpass; |
| |
| VkRenderPass render_pass; |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, |
| "BestPractices-vkCreateRenderPass-SubpassResolve-NonOptimalFormat"); |
| vk::CreateRenderPass(*m_device, &render_pass_ci, nullptr, &render_pass); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, PartialPushConstantSetEnd) { |
| TEST_DESCRIPTION("Set only a part of push constants at end of a struct"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| const char *const vsSource = R"glsl( |
| #version 450 |
| layout(push_constant, std430) uniform foo { uint x[2]; } constants; |
| void main(){ |
| gl_Position = vec4(constants.x[0] * constants.x[1]); |
| } |
| )glsl"; |
| |
| VkShaderObj const vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj const fs(*m_device, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| uint32_t data[2] = {1u, 2u}; |
| VkPushConstantRange push_constant_range = {VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(data)}; |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {}, {push_constant_range}); |
| pipe.CreateGraphicsPipeline(); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); |
| vk::CmdPushConstants(m_command_buffer, pipe.pipeline_layout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(uint32_t), data); |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-PushConstants"); |
| vk::CmdDraw(m_command_buffer, 3, 1, 0, 0); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::CmdPushConstants(m_command_buffer, pipe.pipeline_layout_, VK_SHADER_STAGE_VERTEX_BIT, sizeof(uint32_t), sizeof(uint32_t), |
| &data[1]); |
| vk::CmdDraw(m_command_buffer, 3, 1, 0, 0); |
| |
| m_command_buffer.EndRenderPass(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, PartialPushConstantSetMiddle) { |
| TEST_DESCRIPTION("Set only a part of push constants in middle of as struct"); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| const char *const vsSource = R"glsl( |
| #version 450 |
| layout(push_constant, std430) uniform foo { |
| uint a; // set |
| uint b; // not set |
| uint c; // set |
| } constants; |
| void main(){ |
| gl_Position = vec4(float(constants.a * constants.b * constants.c)); |
| } |
| )glsl"; |
| |
| VkShaderObj const vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT); |
| VkShaderObj const fs(*m_device, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT); |
| |
| uint32_t data = 1u; |
| VkPushConstantRange push_constant_range = {VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(uint32_t) * 3}; |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {}, {push_constant_range}); |
| pipe.CreateGraphicsPipeline(); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); |
| vk::CmdPushConstants(m_command_buffer, pipe.pipeline_layout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(uint32_t), &data); |
| vk::CmdPushConstants(m_command_buffer, pipe.pipeline_layout_, VK_SHADER_STAGE_VERTEX_BIT, sizeof(uint32_t) * 2, |
| sizeof(uint32_t), &data); |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-PushConstants"); |
| vk::CmdDraw(m_command_buffer, 3, 1, 0, 0); |
| m_errorMonitor->VerifyFound(); |
| |
| m_command_buffer.EndRenderPass(); |
| m_command_buffer.End(); |
| } |
| |
| // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7495 |
| TEST_F(VkBestPracticesLayerTest, IgnoreResolveImageView) { |
| TEST_DESCRIPTION("Help warn user when they might have resolveMode set to NONE by accident"); |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::dynamicRendering); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| InitRenderTarget(); |
| |
| VkImageCreateInfo image_create_info = vku::InitStructHelper(); |
| image_create_info.imageType = VK_IMAGE_TYPE_2D; |
| image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; |
| image_create_info.extent = {32, 32, 1}; |
| image_create_info.mipLevels = 1; |
| image_create_info.arrayLayers = 1; |
| image_create_info.samples = VK_SAMPLE_COUNT_4_BIT; |
| image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; |
| image_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| vkt::Image image(*m_device, image_create_info, vkt::set_layout); |
| vkt::ImageView image_view = image.CreateView(); |
| |
| image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; |
| vkt::Image resolve_image(*m_device, image_create_info, vkt::set_layout); |
| vkt::ImageView resolve_image_view = resolve_image.CreateView(); |
| |
| VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper(); |
| color_attachment.imageView = image_view; |
| color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| color_attachment.resolveMode = VK_RESOLVE_MODE_NONE; |
| color_attachment.resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL; |
| color_attachment.resolveImageView = resolve_image_view; |
| |
| VkRenderingInfo begin_rendering_info = vku::InitStructHelper(); |
| begin_rendering_info.colorAttachmentCount = 1; |
| begin_rendering_info.pColorAttachments = &color_attachment; |
| begin_rendering_info.layerCount = 1; |
| begin_rendering_info.renderArea = {{0, 0}, {1, 1}}; |
| |
| m_command_buffer.Begin(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-VkRenderingInfo-ResolveModeNone"); |
| m_command_buffer.BeginRendering(begin_rendering_info); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEvent) { |
| TEST_DESCRIPTION("Signal event two times"); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEvent2) { |
| TEST_DESCRIPTION("Signal event two times using CmdSetEvent2 api"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| VkDependencyInfo dep_info = DependencyInfo(barrier); |
| |
| m_command_buffer.Begin(); |
| vk::CmdSetEvent2(m_command_buffer, event, &dep_info); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| vk::CmdSetEvent2(m_command_buffer, event, &dep_info); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetEventSignaledByHost) { |
| TEST_DESCRIPTION("Set event that was previously set be the host"); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| vk::SetEvent(*m_device, event); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| m_command_buffer.End(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| m_default_queue->Submit(m_command_buffer); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEventMultipleSubmits) { |
| TEST_DESCRIPTION("Set event from different submits"); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| m_command_buffer.End(); |
| m_default_queue->Submit(m_command_buffer); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| cb2.End(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_device->Wait(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEventMultipleSubmits2) { |
| TEST_DESCRIPTION("Set event from multiple submits using QueueSubmit2 api"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| m_command_buffer.End(); |
| m_default_queue->Submit2(m_command_buffer); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| cb2.End(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| m_default_queue->Submit2(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_device->Wait(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEventSecondary) { |
| TEST_DESCRIPTION("Set event in the primary command buffer and then one more time in the secondary"); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| vkt::CommandBuffer secondary_cb(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| secondary_cb.Begin(); |
| secondary_cb.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| secondary_cb.End(); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| m_command_buffer.ExecuteCommands(secondary_cb); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEventSecondary2) { |
| TEST_DESCRIPTION("Set event in different secondary command buffers"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| vkt::CommandBuffer cb(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| VkCommandBufferInheritanceInfo inheritanc_info = vku::InitStructHelper(); |
| VkCommandBufferBeginInfo cb_begin_info = vku::InitStructHelper(); |
| cb_begin_info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; |
| cb_begin_info.pInheritanceInfo = &inheritanc_info; |
| cb.Begin(&cb_begin_info); |
| cb.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| cb.End(); |
| const VkCommandBuffer secondary_cbs[2] = {cb, cb}; |
| |
| m_command_buffer.Begin(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| vk::CmdExecuteCommands(m_command_buffer, 2, secondary_cbs); |
| m_errorMonitor->VerifyFound(); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, SetSignaledEventSecondary3) { |
| TEST_DESCRIPTION("Set event in the secondary command buffer and in the primary from different submissions"); |
| RETURN_IF_SKIP(InitBestPractices()); |
| |
| vkt::Event event(*m_device); |
| |
| vkt::CommandBuffer secondary_cb(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); |
| secondary_cb.Begin(); |
| secondary_cb.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| secondary_cb.End(); |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.ExecuteCommands(secondary_cb); |
| m_command_buffer.End(); |
| m_default_queue->Submit(m_command_buffer); |
| |
| vkt::CommandBuffer cb2(*m_device, m_command_pool); |
| cb2.Begin(); |
| cb2.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); |
| cb2.End(); |
| m_errorMonitor->SetDesiredWarning("BestPractices-Event-SignalSignaledEvent"); |
| m_default_queue->Submit(cb2); |
| m_errorMonitor->VerifyFound(); |
| m_device->Wait(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, PartialPushConstantSetEndCompute) { |
| TEST_DESCRIPTION("Set only a part of push constants at end of a struct"); |
| |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| const char *const csSource = R"glsl( |
| #version 450 |
| layout(push_constant, std430) uniform foo { uint x[2]; } constants; |
| layout(set = 0, binding = 0) buffer bar { vec4 r; } res; |
| void main(){ |
| res.r = vec4(constants.x[0] * constants.x[1]); |
| } |
| )glsl"; |
| |
| uint32_t data[2] = {1u, 2u}; |
| VkPushConstantRange push_constant_range = {VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(data)}; |
| |
| vkt::Buffer buffer(*m_device, 16u, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}, |
| }); |
| descriptor_set.WriteDescriptorBufferInfo(0, buffer, 0u, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| descriptor_set.UpdateDescriptorSets(); |
| |
| CreateComputePipelineHelper pipe(*this); |
| pipe.cs_ = VkShaderObj(*m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT); |
| pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_}, {push_constant_range}); |
| pipe.CreateComputePipeline(); |
| |
| m_command_buffer.Begin(); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0u, 1u, &descriptor_set.set_, |
| 0u, nullptr); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); |
| vk::CmdPushConstants(m_command_buffer, pipe.pipeline_layout_, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), data); |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-PushConstants"); |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::CmdPushConstants(m_command_buffer, pipe.pipeline_layout_, VK_SHADER_STAGE_COMPUTE_BIT, sizeof(uint32_t), sizeof(uint32_t), |
| &data[1]); |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, UnneededQueueFamilyOwnershipTransfer) { |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::maintenance9); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| RETURN_IF_SKIP(InitState()); |
| |
| std::optional<uint32_t> transfer_only_family = m_device->TransferOnlyQueueFamily(); |
| if (!transfer_only_family.has_value()) { |
| GTEST_SKIP() << "Transfer-only queue family is required"; |
| } |
| vkt::CommandPool transfer_pool(*m_device, transfer_only_family.value(), VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); |
| vkt::CommandBuffer transfer_cb(*m_device, transfer_pool); |
| |
| vkt::Buffer buffer(*m_device, 256, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| VkBufferMemoryBarrier2 acquire_barrier = vku::InitStructHelper(); |
| acquire_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| acquire_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| acquire_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| acquire_barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| acquire_barrier.srcQueueFamilyIndex = m_default_queue->family_index; |
| acquire_barrier.dstQueueFamilyIndex = transfer_only_family.value(); |
| acquire_barrier.buffer = buffer; |
| acquire_barrier.offset = 0; |
| acquire_barrier.size = 256; |
| |
| transfer_cb.Begin(); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-PipelineBarrier-unneeded-QFOT"); |
| transfer_cb.Barrier(acquire_barrier); |
| m_errorMonitor->VerifyFound(); |
| transfer_cb.End(); |
| |
| VkBufferMemoryBarrier2 release_barrier = vku::InitStructHelper(); |
| release_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| release_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| release_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| release_barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| release_barrier.srcQueueFamilyIndex = transfer_only_family.value(); |
| release_barrier.dstQueueFamilyIndex = m_default_queue->family_index; |
| release_barrier.buffer = buffer; |
| release_barrier.offset = 0; |
| release_barrier.size = 256; |
| |
| transfer_cb.Begin(); |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-PipelineBarrier-unneeded-QFOT"); |
| transfer_cb.Barrier(release_barrier); |
| m_errorMonitor->VerifyFound(); |
| transfer_cb.End(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, BadDestroy) { |
| TEST_DESCRIPTION( |
| "In PreCallRecordDestroyDevice, make sure CommandBufferSubState is destroyed before destroying device state and validation " |
| "does not crash"); |
| RETURN_IF_SKIP(InitBestPracticesFramework()); |
| |
| // Workaround for overzealous layers checking even the guaranteed 0th queue family |
| const auto q_props = vkt::PhysicalDevice(Gpu()).queue_properties_; |
| ASSERT_TRUE(q_props.size() > 0); |
| ASSERT_TRUE(q_props[0].queueCount > 0); |
| |
| const float q_priority[] = {1.0f}; |
| VkDeviceQueueCreateInfo queue_ci = vku::InitStructHelper(); |
| queue_ci.queueFamilyIndex = 0; |
| queue_ci.queueCount = 1; |
| queue_ci.pQueuePriorities = q_priority; |
| |
| VkDeviceCreateInfo device_ci = vku::InitStructHelper(); |
| device_ci.queueCreateInfoCount = 1; |
| device_ci.pQueueCreateInfos = &queue_ci; |
| |
| VkDevice leaky_device; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateDevice(Gpu(), &device_ci, nullptr, &leaky_device)); |
| |
| VkCommandPool command_pool; |
| VkCommandPoolCreateInfo pool_create_info = vku::InitStructHelper(); |
| pool_create_info.queueFamilyIndex = 0; |
| vk::CreateCommandPool(leaky_device, &pool_create_info, nullptr, &command_pool); |
| |
| VkCommandBuffer command_buffer = VK_NULL_HANDLE; |
| VkCommandBufferAllocateInfo command_buffer_allocate_info = vku::InitStructHelper(); |
| command_buffer_allocate_info.commandPool = command_pool; |
| command_buffer_allocate_info.commandBufferCount = 1; |
| command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
| vk::AllocateCommandBuffers(leaky_device, &command_buffer_allocate_info, &command_buffer); |
| |
| m_errorMonitor->SetDesiredError("VUID-vkDestroyDevice-device-05137"); |
| m_errorMonitor->SetDesiredError("VUID-vkDestroyDevice-device-05137"); |
| // Those 2 will come from self validation if it is enabled |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkDestroyDevice-device-05137"); |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkDestroyDevice-device-05137"); |
| vk::DestroyDevice(leaky_device, nullptr); |
| m_errorMonitor->VerifyFound(); |
| |
| // There's no way we can destroy the command pool at this point. Even though DestroyDevice failed, the loader has already |
| // removed references to the device |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkDestroyDevice-device-05137"); |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkDestroyDevice-device-05137"); |
| m_errorMonitor->SetAllowedFailureMsg("VUID-vkDestroyInstance-instance-00629"); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, MutableDescriptors) { |
| AddRequiredExtensions(VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::mutableDescriptorType); |
| RETURN_IF_SKIP(InitBestPractices()); |
| VkDescriptorType descriptor_types[] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}; |
| |
| VkMutableDescriptorTypeListEXT mutable_descriptor_type_lists[2] = {{1, &descriptor_types[0]}, {1, &descriptor_types[1]}}; |
| |
| VkMutableDescriptorTypeCreateInfoEXT mdtci = vku::InitStructHelper(); |
| mdtci.mutableDescriptorTypeListCount = 2; |
| mdtci.pMutableDescriptorTypeLists = mutable_descriptor_type_lists; |
| |
| VkDescriptorPoolSize pool_size = {VK_DESCRIPTOR_TYPE_MUTABLE_EXT, 4}; |
| VkDescriptorPoolCreateInfo ds_pool_ci = vku::InitStructHelper(&mdtci); |
| ds_pool_ci.maxSets = 2; |
| ds_pool_ci.poolSizeCount = 1; |
| ds_pool_ci.pPoolSizes = &pool_size; |
| |
| m_errorMonitor->SetDesiredWarning("BestPractices-MutableDescriptor-TypeListCount"); |
| vkt::DescriptorPool pool(*m_device, ds_pool_ci); |
| m_errorMonitor->VerifyFound(); |
| } |
| |
| TEST_F(VkBestPracticesLayerTest, MaxPreferredWorkGroupInvocations) { |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_EXT_MESH_SHADER_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::meshShader); |
| AddRequiredFeature(vkt::Feature::taskShader); |
| RETURN_IF_SKIP(InitBestPractices()); |
| InitRenderTarget(); |
| |
| VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = vku::InitStructHelper(); |
| GetPhysicalDeviceProperties2(mesh_shader_properties); |
| |
| if (mesh_shader_properties.maxPreferredTaskWorkGroupInvocations >= 32 || |
| mesh_shader_properties.maxPreferredMeshWorkGroupInvocations >= 32) { |
| GTEST_SKIP() << "Preferred values are too high"; |
| } |
| |
| const char* task_source = R"glsl( |
| #version 460 |
| #extension GL_EXT_mesh_shader : enable |
| layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in; |
| void main() { |
| EmitMeshTasksEXT(1u, 1u, 1u); |
| } |
| )glsl"; |
| |
| const char* mesh_source = R"glsl( |
| #version 460 |
| #extension GL_EXT_mesh_shader : enable |
| layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in; |
| layout(max_vertices = 3, max_primitives=1) out; |
| layout(triangles) out; |
| void main() { |
| SetMeshOutputsEXT(3,1); |
| } |
| )glsl"; |
| |
| { |
| VkShaderObj ts_over(*m_device, task_source, VK_SHADER_STAGE_TASK_BIT_EXT, SPV_ENV_VULKAN_1_2); |
| VkShaderObj ms(*m_device, kMeshMinimalGlsl, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2); |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {ts_over.GetStageCreateInfo(), ms.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()}; |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-Mesh-MaxPreferredWorkGroupInvocations"); |
| pipe.CreateGraphicsPipeline(); |
| m_errorMonitor->VerifyFound(); |
| } |
| { |
| VkShaderObj ms_over(*m_device, mesh_source, VK_SHADER_STAGE_MESH_BIT_EXT, SPV_ENV_VULKAN_1_2); |
| CreatePipelineHelper pipe(*this); |
| pipe.shader_stages_ = {ms_over.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()}; |
| m_errorMonitor->SetDesiredFailureMsg(kPerformanceWarningBit, "BestPractices-Mesh-MaxPreferredWorkGroupInvocations"); |
| pipe.CreateGraphicsPipeline(); |
| m_errorMonitor->VerifyFound(); |
| } |
| } |