diff --git a/tests/RHI/Vulkan/unit/CMakeLists.txt b/tests/RHI/Vulkan/unit/CMakeLists.txt index edbab08d..a8964f96 100644 --- a/tests/RHI/Vulkan/unit/CMakeLists.txt +++ b/tests/RHI/Vulkan/unit/CMakeLists.txt @@ -9,6 +9,7 @@ if(NOT TARGET Vulkan::Vulkan) endif() set(TEST_SOURCES + fixtures/VulkanTestFixture.cpp test_backend_specific.cpp ) diff --git a/tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.cpp b/tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.cpp new file mode 100644 index 00000000..205eb293 --- /dev/null +++ b/tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.cpp @@ -0,0 +1,363 @@ +#include "VulkanTestFixture.h" + +#if defined(XCENGINE_SUPPORT_VULKAN) + +#include + +#include +#include +#include + +#include "XCEngine/RHI/RHIFactory.h" +#include "XCEngine/RHI/Vulkan/VulkanCommon.h" + +namespace XCEngine { +namespace RHI { + +namespace { + +VkImageLayout ToImageLayout(ResourceStates state) { + switch (state) { + case ResourceStates::RenderTarget: + return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + case ResourceStates::DepthWrite: + return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + case ResourceStates::DepthRead: + return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + case ResourceStates::CopySrc: + return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + case ResourceStates::CopyDst: + return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + case ResourceStates::Present: + return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + case ResourceStates::PixelShaderResource: + case ResourceStates::NonPixelShaderResource: + case ResourceStates::GenericRead: + return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + case ResourceStates::UnorderedAccess: + return VK_IMAGE_LAYOUT_GENERAL; + case ResourceStates::Common: + case ResourceStates::VertexAndConstantBuffer: + case ResourceStates::IndexBuffer: + default: + return VK_IMAGE_LAYOUT_UNDEFINED; + } +} + +VkAccessFlags ToAccessMask(ResourceStates state) { + switch (state) { + case ResourceStates::RenderTarget: + return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + case ResourceStates::DepthWrite: + return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + case ResourceStates::DepthRead: + return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + case ResourceStates::CopySrc: + return VK_ACCESS_TRANSFER_READ_BIT; + case ResourceStates::CopyDst: + return VK_ACCESS_TRANSFER_WRITE_BIT; + case ResourceStates::PixelShaderResource: + case ResourceStates::NonPixelShaderResource: + return VK_ACCESS_SHADER_READ_BIT; + case ResourceStates::GenericRead: + return VK_ACCESS_MEMORY_READ_BIT; + case ResourceStates::UnorderedAccess: + return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + case ResourceStates::Present: + case ResourceStates::Common: + case ResourceStates::VertexAndConstantBuffer: + case ResourceStates::IndexBuffer: + default: + return 0; + } +} + +VkPipelineStageFlags ToStageMask(ResourceStates state) { + switch (state) { + case ResourceStates::RenderTarget: + return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + case ResourceStates::DepthWrite: + case ResourceStates::DepthRead: + return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + case ResourceStates::CopySrc: + case ResourceStates::CopyDst: + return VK_PIPELINE_STAGE_TRANSFER_BIT; + case ResourceStates::PixelShaderResource: + return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + case ResourceStates::NonPixelShaderResource: + return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + case ResourceStates::GenericRead: + return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + case ResourceStates::UnorderedAccess: + return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + case ResourceStates::Present: + return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + case ResourceStates::Common: + case ResourceStates::VertexAndConstantBuffer: + case ResourceStates::IndexBuffer: + default: + return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } +} + +constexpr uint32_t kWriteRedComputeSpirv[] = { + 0x07230203, 0x00010000, 0x0008000B, 0x00000017, 0x00000000, 0x00020011, 0x00000001, 0x0006000B, + 0x00000001, 0x4C534C47, 0x6474732E, 0x3035342E, 0x00000000, 0x0003000E, 0x00000000, 0x00000001, + 0x0005000F, 0x00000005, 0x00000004, 0x6E69616D, 0x00000000, 0x00060010, 0x00000004, 0x00000011, + 0x00000001, 0x00000001, 0x00000001, 0x00030003, 0x00000002, 0x000001C2, 0x00040005, 0x00000004, + 0x6E69616D, 0x00000000, 0x00040005, 0x00000009, 0x616D4975, 0x00006567, 0x00030047, 0x00000009, + 0x00000019, 0x00040047, 0x00000009, 0x00000021, 0x00000000, 0x00040047, 0x00000009, 0x00000022, + 0x00000000, 0x00040047, 0x00000016, 0x0000000B, 0x00000019, 0x00020013, 0x00000002, 0x00030021, + 0x00000003, 0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00090019, 0x00000007, 0x00000006, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000004, 0x00040020, 0x00000008, + 0x00000000, 0x00000007, 0x0004003B, 0x00000008, 0x00000009, 0x00000000, 0x00040015, 0x0000000B, + 0x00000020, 0x00000001, 0x00040017, 0x0000000C, 0x0000000B, 0x00000002, 0x0004002B, 0x0000000B, + 0x0000000D, 0x00000000, 0x0005002C, 0x0000000C, 0x0000000E, 0x0000000D, 0x0000000D, 0x00040017, + 0x0000000F, 0x00000006, 0x00000004, 0x0004002B, 0x00000006, 0x00000010, 0x3F800000, 0x0004002B, + 0x00000006, 0x00000011, 0x00000000, 0x0007002C, 0x0000000F, 0x00000012, 0x00000010, 0x00000011, + 0x00000011, 0x00000010, 0x00040015, 0x00000013, 0x00000020, 0x00000000, 0x00040017, 0x00000014, + 0x00000013, 0x00000003, 0x0004002B, 0x00000013, 0x00000015, 0x00000001, 0x0006002C, 0x00000014, + 0x00000016, 0x00000015, 0x00000015, 0x00000015, 0x00050036, 0x00000002, 0x00000004, 0x00000000, + 0x00000003, 0x000200F8, 0x00000005, 0x0004003D, 0x00000007, 0x0000000A, 0x00000009, 0x00040063, + 0x0000000A, 0x0000000E, 0x00000012, 0x000100FD, 0x00010038 +}; + +} // namespace + +void VulkanGraphicsFixture::SetUp() { + m_device = RHIFactory::CreateRHIDevice(RHIType::Vulkan); + ASSERT_NE(m_device, nullptr); + + RHIDeviceDesc deviceDesc = {}; + deviceDesc.enableDebugLayer = false; + ASSERT_TRUE(m_device->Initialize(deviceDesc)); + + CommandQueueDesc queueDesc = {}; + queueDesc.queueType = static_cast(CommandQueueType::Direct); + m_queue = m_device->CreateCommandQueue(queueDesc); + ASSERT_NE(m_queue, nullptr); +} + +void VulkanGraphicsFixture::TearDown() { + if (m_queue != nullptr) { + m_queue->WaitForIdle(); + m_queue->Shutdown(); + delete m_queue; + m_queue = nullptr; + } + + if (m_device != nullptr) { + m_device->Shutdown(); + delete m_device; + m_device = nullptr; + } +} + +std::wstring VulkanGraphicsFixture::ResolveShaderPath(const wchar_t* relativePath) const { + wchar_t exePath[MAX_PATH] = {}; + const DWORD length = GetModuleFileNameW(nullptr, exePath, MAX_PATH); + std::filesystem::path rootPath = + length > 0 ? std::filesystem::path(exePath).parent_path() : std::filesystem::current_path(); + while (!rootPath.empty() && + !(std::filesystem::exists(rootPath / "tests" / "RHI") && + std::filesystem::exists(rootPath / "engine" / "include" / "XCEngine"))) { + rootPath = rootPath.parent_path(); + } + return (rootPath / relativePath).wstring(); +} + +TextureDesc VulkanGraphicsFixture::CreateColorTextureDesc(uint32_t width, uint32_t height) const { + TextureDesc desc = {}; + desc.width = width; + desc.height = height; + desc.depth = 1; + desc.mipLevels = 1; + desc.arraySize = 1; + desc.format = static_cast(Format::R8G8B8A8_UNorm); + desc.textureType = static_cast(TextureType::Texture2D); + desc.sampleCount = 1; + return desc; +} + +TextureDesc VulkanGraphicsFixture::CreateDepthTextureDesc(uint32_t width, uint32_t height) const { + TextureDesc desc = {}; + desc.width = width; + desc.height = height; + desc.depth = 1; + desc.mipLevels = 1; + desc.arraySize = 1; + desc.format = static_cast(Format::D24_UNorm_S8_UInt); + desc.textureType = static_cast(TextureType::Texture2D); + desc.sampleCount = 1; + return desc; +} + +RHICommandList* VulkanGraphicsFixture::CreateCommandList() const { + CommandListDesc desc = {}; + desc.commandListType = static_cast(CommandQueueType::Direct); + return m_device->CreateCommandList(desc); +} + +RHIShader* VulkanGraphicsFixture::CreateWriteRedComputeShader() const { + ShaderCompileDesc shaderDesc = {}; + shaderDesc.sourceLanguage = ShaderLanguage::SPIRV; + shaderDesc.profile = L"cs_6_0"; + shaderDesc.source.resize(sizeof(kWriteRedComputeSpirv)); + std::memcpy(shaderDesc.source.data(), kWriteRedComputeSpirv, sizeof(kWriteRedComputeSpirv)); + return m_device->CreateShader(shaderDesc); +} + +RHIShader* VulkanGraphicsFixture::CreateWriteRedComputeShaderFromGlsl() const { + static const char* computeSource = R"(#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D uImage; +void main() { + imageStore(uImage, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0)); +} +)"; + + ShaderCompileDesc shaderDesc = {}; + shaderDesc.sourceLanguage = ShaderLanguage::GLSL; + shaderDesc.source.assign(computeSource, computeSource + std::strlen(computeSource)); + return m_device->CreateShader(shaderDesc); +} + +void VulkanGraphicsFixture::SubmitAndWait(RHICommandList* commandList) { + ASSERT_NE(commandList, nullptr); + commandList->Close(); + void* commandLists[] = { commandList }; + m_queue->ExecuteCommandLists(1, commandLists); + m_queue->WaitForIdle(); +} + +std::vector VulkanGraphicsFixture::ReadTextureRgba8(VulkanTexture* texture) { + EXPECT_NE(texture, nullptr); + EXPECT_EQ(texture->GetFormat(), Format::R8G8B8A8_UNorm); + + auto* device = static_cast(m_device); + const uint32_t width = texture->GetWidth(); + const uint32_t height = texture->GetHeight(); + const VkDeviceSize bufferSize = static_cast(width) * static_cast(height) * 4; + + VkBuffer stagingBuffer = VK_NULL_HANDLE; + VkDeviceMemory stagingMemory = VK_NULL_HANDLE; + VkCommandPool commandPool = VK_NULL_HANDLE; + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; + + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = bufferSize; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + EXPECT_EQ(vkCreateBuffer(device->GetDevice(), &bufferInfo, nullptr, &stagingBuffer), VK_SUCCESS); + + VkMemoryRequirements memoryRequirements = {}; + vkGetBufferMemoryRequirements(device->GetDevice(), stagingBuffer, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo = {}; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.allocationSize = memoryRequirements.size; + allocateInfo.memoryTypeIndex = device->FindMemoryType( + memoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + EXPECT_NE(allocateInfo.memoryTypeIndex, UINT32_MAX); + EXPECT_EQ(vkAllocateMemory(device->GetDevice(), &allocateInfo, nullptr, &stagingMemory), VK_SUCCESS); + EXPECT_EQ(vkBindBufferMemory(device->GetDevice(), stagingBuffer, stagingMemory, 0), VK_SUCCESS); + + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = device->GetGraphicsQueueFamilyIndex(); + EXPECT_EQ(vkCreateCommandPool(device->GetDevice(), &poolInfo, nullptr, &commandPool), VK_SUCCESS); + + VkCommandBufferAllocateInfo commandBufferInfo = {}; + commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBufferInfo.commandPool = commandPool; + commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBufferInfo.commandBufferCount = 1; + EXPECT_EQ(vkAllocateCommandBuffers(device->GetDevice(), &commandBufferInfo, &commandBuffer), VK_SUCCESS); + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + EXPECT_EQ(vkBeginCommandBuffer(commandBuffer, &beginInfo), VK_SUCCESS); + + const ResourceStates previousState = texture->GetState(); + + VkImageMemoryBarrier toCopySource = {}; + toCopySource.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + toCopySource.oldLayout = ToImageLayout(previousState); + toCopySource.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + toCopySource.srcAccessMask = ToAccessMask(previousState); + toCopySource.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + toCopySource.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toCopySource.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toCopySource.image = texture->GetImage(); + toCopySource.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + toCopySource.subresourceRange.baseMipLevel = 0; + toCopySource.subresourceRange.levelCount = 1; + toCopySource.subresourceRange.baseArrayLayer = 0; + toCopySource.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier( + commandBuffer, + ToStageMask(previousState), + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &toCopySource); + + VkBufferImageCopy copyRegion = {}; + copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyRegion.imageSubresource.mipLevel = 0; + copyRegion.imageSubresource.baseArrayLayer = 0; + copyRegion.imageSubresource.layerCount = 1; + copyRegion.imageExtent.width = width; + copyRegion.imageExtent.height = height; + copyRegion.imageExtent.depth = 1; + vkCmdCopyImageToBuffer( + commandBuffer, + texture->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + stagingBuffer, + 1, + ©Region); + + VkImageMemoryBarrier restoreBarrier = toCopySource; + restoreBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + restoreBarrier.newLayout = ToImageLayout(previousState); + restoreBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + restoreBarrier.dstAccessMask = ToAccessMask(previousState); + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + ToStageMask(previousState), + 0, + 0, nullptr, + 0, nullptr, + 1, &restoreBarrier); + + EXPECT_EQ(vkEndCommandBuffer(commandBuffer), VK_SUCCESS); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + EXPECT_EQ(vkQueueSubmit(device->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE), VK_SUCCESS); + EXPECT_EQ(vkQueueWaitIdle(device->GetGraphicsQueue()), VK_SUCCESS); + + void* mappedData = nullptr; + EXPECT_EQ(vkMapMemory(device->GetDevice(), stagingMemory, 0, bufferSize, 0, &mappedData), VK_SUCCESS); + std::vector pixels(static_cast(bufferSize)); + std::copy_n(static_cast(mappedData), pixels.size(), pixels.data()); + vkUnmapMemory(device->GetDevice(), stagingMemory); + + vkDestroyCommandPool(device->GetDevice(), commandPool, nullptr); + vkFreeMemory(device->GetDevice(), stagingMemory, nullptr); + vkDestroyBuffer(device->GetDevice(), stagingBuffer, nullptr); + return pixels; +} + +} // namespace RHI +} // namespace XCEngine + +#endif diff --git a/tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.h b/tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.h new file mode 100644 index 00000000..97e49ee3 --- /dev/null +++ b/tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.h @@ -0,0 +1,43 @@ +#pragma once + +#if defined(XCENGINE_SUPPORT_VULKAN) + +#include + +#include +#include +#include + +#include "XCEngine/RHI/RHICommandList.h" +#include "XCEngine/RHI/RHICommandQueue.h" +#include "XCEngine/RHI/RHIDevice.h" +#include "XCEngine/RHI/RHIShader.h" +#include "XCEngine/RHI/RHITexture.h" +#include "XCEngine/RHI/Vulkan/VulkanDevice.h" +#include "XCEngine/RHI/Vulkan/VulkanTexture.h" + +namespace XCEngine { +namespace RHI { + +class VulkanGraphicsFixture : public ::testing::Test { +protected: + void SetUp() override; + void TearDown() override; + + std::wstring ResolveShaderPath(const wchar_t* relativePath) const; + TextureDesc CreateColorTextureDesc(uint32_t width, uint32_t height) const; + TextureDesc CreateDepthTextureDesc(uint32_t width, uint32_t height) const; + RHICommandList* CreateCommandList() const; + RHIShader* CreateWriteRedComputeShader() const; + RHIShader* CreateWriteRedComputeShaderFromGlsl() const; + void SubmitAndWait(RHICommandList* commandList); + std::vector ReadTextureRgba8(VulkanTexture* texture); + + RHIDevice* m_device = nullptr; + RHICommandQueue* m_queue = nullptr; +}; + +} // namespace RHI +} // namespace XCEngine + +#endif diff --git a/tests/RHI/Vulkan/unit/test_backend_specific.cpp b/tests/RHI/Vulkan/unit/test_backend_specific.cpp index 240a4ce8..69476504 100644 --- a/tests/RHI/Vulkan/unit/test_backend_specific.cpp +++ b/tests/RHI/Vulkan/unit/test_backend_specific.cpp @@ -10,372 +10,20 @@ #include #include -#include "XCEngine/RHI/RHICommandList.h" -#include "XCEngine/RHI/RHICommandQueue.h" +#include "fixtures/VulkanTestFixture.h" #include "XCEngine/RHI/RHIDescriptorPool.h" #include "XCEngine/RHI/RHIDescriptorSet.h" -#include "XCEngine/RHI/RHIDevice.h" #include "XCEngine/RHI/RHIFramebuffer.h" -#include "XCEngine/RHI/RHIFactory.h" #include "XCEngine/RHI/RHIPipelineLayout.h" #include "XCEngine/RHI/RHIPipelineState.h" #include "XCEngine/RHI/RHIRenderPass.h" #include "XCEngine/RHI/RHIResourceView.h" -#include "XCEngine/RHI/RHIShader.h" -#include "XCEngine/RHI/RHITexture.h" -#include "XCEngine/RHI/Vulkan/VulkanCommon.h" -#include "XCEngine/RHI/Vulkan/VulkanDevice.h" #include "XCEngine/RHI/Vulkan/VulkanTexture.h" using namespace XCEngine::RHI; namespace { -std::wstring ResolveShaderPath(const wchar_t* relativePath) { - wchar_t exePath[MAX_PATH] = {}; - const DWORD length = GetModuleFileNameW(nullptr, exePath, MAX_PATH); - std::filesystem::path rootPath = - length > 0 ? std::filesystem::path(exePath).parent_path() : std::filesystem::current_path(); - while (!rootPath.empty() && - !(std::filesystem::exists(rootPath / "tests" / "RHI") && - std::filesystem::exists(rootPath / "engine" / "include" / "XCEngine"))) { - rootPath = rootPath.parent_path(); - } - return (rootPath / relativePath).wstring(); -} - -VkImageLayout ToImageLayout(ResourceStates state) { - switch (state) { - case ResourceStates::RenderTarget: - return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - case ResourceStates::DepthWrite: - return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - case ResourceStates::DepthRead: - return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; - case ResourceStates::CopySrc: - return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - case ResourceStates::CopyDst: - return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - case ResourceStates::Present: - return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - case ResourceStates::PixelShaderResource: - case ResourceStates::NonPixelShaderResource: - case ResourceStates::GenericRead: - return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - case ResourceStates::UnorderedAccess: - return VK_IMAGE_LAYOUT_GENERAL; - case ResourceStates::Common: - case ResourceStates::VertexAndConstantBuffer: - case ResourceStates::IndexBuffer: - default: - return VK_IMAGE_LAYOUT_UNDEFINED; - } -} - -VkAccessFlags ToAccessMask(ResourceStates state) { - switch (state) { - case ResourceStates::RenderTarget: - return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - case ResourceStates::DepthWrite: - return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - case ResourceStates::DepthRead: - return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - case ResourceStates::CopySrc: - return VK_ACCESS_TRANSFER_READ_BIT; - case ResourceStates::CopyDst: - return VK_ACCESS_TRANSFER_WRITE_BIT; - case ResourceStates::PixelShaderResource: - case ResourceStates::NonPixelShaderResource: - return VK_ACCESS_SHADER_READ_BIT; - case ResourceStates::GenericRead: - return VK_ACCESS_MEMORY_READ_BIT; - case ResourceStates::UnorderedAccess: - return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - case ResourceStates::Present: - case ResourceStates::Common: - case ResourceStates::VertexAndConstantBuffer: - case ResourceStates::IndexBuffer: - default: - return 0; - } -} - -VkPipelineStageFlags ToStageMask(ResourceStates state) { - switch (state) { - case ResourceStates::RenderTarget: - return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - case ResourceStates::DepthWrite: - case ResourceStates::DepthRead: - return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - case ResourceStates::CopySrc: - case ResourceStates::CopyDst: - return VK_PIPELINE_STAGE_TRANSFER_BIT; - case ResourceStates::PixelShaderResource: - return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - case ResourceStates::NonPixelShaderResource: - return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - case ResourceStates::GenericRead: - return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - case ResourceStates::UnorderedAccess: - return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - case ResourceStates::Present: - return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - case ResourceStates::Common: - case ResourceStates::VertexAndConstantBuffer: - case ResourceStates::IndexBuffer: - default: - return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } -} - -constexpr uint32_t kWriteRedComputeSpirv[] = { - 0x07230203, 0x00010000, 0x0008000B, 0x00000017, 0x00000000, 0x00020011, 0x00000001, 0x0006000B, - 0x00000001, 0x4C534C47, 0x6474732E, 0x3035342E, 0x00000000, 0x0003000E, 0x00000000, 0x00000001, - 0x0005000F, 0x00000005, 0x00000004, 0x6E69616D, 0x00000000, 0x00060010, 0x00000004, 0x00000011, - 0x00000001, 0x00000001, 0x00000001, 0x00030003, 0x00000002, 0x000001C2, 0x00040005, 0x00000004, - 0x6E69616D, 0x00000000, 0x00040005, 0x00000009, 0x616D4975, 0x00006567, 0x00030047, 0x00000009, - 0x00000019, 0x00040047, 0x00000009, 0x00000021, 0x00000000, 0x00040047, 0x00000009, 0x00000022, - 0x00000000, 0x00040047, 0x00000016, 0x0000000B, 0x00000019, 0x00020013, 0x00000002, 0x00030021, - 0x00000003, 0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00090019, 0x00000007, 0x00000006, - 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000004, 0x00040020, 0x00000008, - 0x00000000, 0x00000007, 0x0004003B, 0x00000008, 0x00000009, 0x00000000, 0x00040015, 0x0000000B, - 0x00000020, 0x00000001, 0x00040017, 0x0000000C, 0x0000000B, 0x00000002, 0x0004002B, 0x0000000B, - 0x0000000D, 0x00000000, 0x0005002C, 0x0000000C, 0x0000000E, 0x0000000D, 0x0000000D, 0x00040017, - 0x0000000F, 0x00000006, 0x00000004, 0x0004002B, 0x00000006, 0x00000010, 0x3F800000, 0x0004002B, - 0x00000006, 0x00000011, 0x00000000, 0x0007002C, 0x0000000F, 0x00000012, 0x00000010, 0x00000011, - 0x00000011, 0x00000010, 0x00040015, 0x00000013, 0x00000020, 0x00000000, 0x00040017, 0x00000014, - 0x00000013, 0x00000003, 0x0004002B, 0x00000013, 0x00000015, 0x00000001, 0x0006002C, 0x00000014, - 0x00000016, 0x00000015, 0x00000015, 0x00000015, 0x00050036, 0x00000002, 0x00000004, 0x00000000, - 0x00000003, 0x000200F8, 0x00000005, 0x0004003D, 0x00000007, 0x0000000A, 0x00000009, 0x00040063, - 0x0000000A, 0x0000000E, 0x00000012, 0x000100FD, 0x00010038 -}; - -class VulkanGraphicsFixture : public ::testing::Test { -protected: - void SetUp() override { - m_device = RHIFactory::CreateRHIDevice(RHIType::Vulkan); - ASSERT_NE(m_device, nullptr); - - RHIDeviceDesc deviceDesc = {}; - deviceDesc.enableDebugLayer = false; - ASSERT_TRUE(m_device->Initialize(deviceDesc)); - - CommandQueueDesc queueDesc = {}; - queueDesc.queueType = static_cast(CommandQueueType::Direct); - m_queue = m_device->CreateCommandQueue(queueDesc); - ASSERT_NE(m_queue, nullptr); - } - - void TearDown() override { - if (m_queue != nullptr) { - m_queue->WaitForIdle(); - m_queue->Shutdown(); - delete m_queue; - m_queue = nullptr; - } - - if (m_device != nullptr) { - m_device->Shutdown(); - delete m_device; - m_device = nullptr; - } - } - - TextureDesc CreateColorTextureDesc(uint32_t width, uint32_t height) const { - TextureDesc desc = {}; - desc.width = width; - desc.height = height; - desc.depth = 1; - desc.mipLevels = 1; - desc.arraySize = 1; - desc.format = static_cast(Format::R8G8B8A8_UNorm); - desc.textureType = static_cast(TextureType::Texture2D); - desc.sampleCount = 1; - return desc; - } - - TextureDesc CreateDepthTextureDesc(uint32_t width, uint32_t height) const { - TextureDesc desc = {}; - desc.width = width; - desc.height = height; - desc.depth = 1; - desc.mipLevels = 1; - desc.arraySize = 1; - desc.format = static_cast(Format::D24_UNorm_S8_UInt); - desc.textureType = static_cast(TextureType::Texture2D); - desc.sampleCount = 1; - return desc; - } - - RHICommandList* CreateCommandList() const { - CommandListDesc desc = {}; - desc.commandListType = static_cast(CommandQueueType::Direct); - return m_device->CreateCommandList(desc); - } - - RHIShader* CreateWriteRedComputeShader() const { - ShaderCompileDesc shaderDesc = {}; - shaderDesc.sourceLanguage = ShaderLanguage::SPIRV; - shaderDesc.profile = L"cs_6_0"; - shaderDesc.source.resize(sizeof(kWriteRedComputeSpirv)); - std::memcpy(shaderDesc.source.data(), kWriteRedComputeSpirv, sizeof(kWriteRedComputeSpirv)); - return m_device->CreateShader(shaderDesc); - } - - RHIShader* CreateWriteRedComputeShaderFromGlsl() const { - static const char* computeSource = R"(#version 450 -layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; -layout(set = 0, binding = 0, rgba8) uniform writeonly image2D uImage; -void main() { - imageStore(uImage, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0)); -} -)"; - - ShaderCompileDesc shaderDesc = {}; - shaderDesc.sourceLanguage = ShaderLanguage::GLSL; - shaderDesc.source.assign(computeSource, computeSource + std::strlen(computeSource)); - return m_device->CreateShader(shaderDesc); - } - - void SubmitAndWait(RHICommandList* commandList) { - ASSERT_NE(commandList, nullptr); - commandList->Close(); - void* commandLists[] = { commandList }; - m_queue->ExecuteCommandLists(1, commandLists); - m_queue->WaitForIdle(); - } - - std::vector ReadTextureRgba8(VulkanTexture* texture) { - EXPECT_NE(texture, nullptr); - EXPECT_EQ(texture->GetFormat(), Format::R8G8B8A8_UNorm); - - auto* device = static_cast(m_device); - const uint32_t width = texture->GetWidth(); - const uint32_t height = texture->GetHeight(); - const VkDeviceSize bufferSize = static_cast(width) * static_cast(height) * 4; - - VkBuffer stagingBuffer = VK_NULL_HANDLE; - VkDeviceMemory stagingMemory = VK_NULL_HANDLE; - VkCommandPool commandPool = VK_NULL_HANDLE; - VkCommandBuffer commandBuffer = VK_NULL_HANDLE; - - VkBufferCreateInfo bufferInfo = {}; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = bufferSize; - bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - EXPECT_EQ(vkCreateBuffer(device->GetDevice(), &bufferInfo, nullptr, &stagingBuffer), VK_SUCCESS); - - VkMemoryRequirements memoryRequirements = {}; - vkGetBufferMemoryRequirements(device->GetDevice(), stagingBuffer, &memoryRequirements); - - VkMemoryAllocateInfo allocateInfo = {}; - allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocateInfo.allocationSize = memoryRequirements.size; - allocateInfo.memoryTypeIndex = device->FindMemoryType( - memoryRequirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - EXPECT_NE(allocateInfo.memoryTypeIndex, UINT32_MAX); - EXPECT_EQ(vkAllocateMemory(device->GetDevice(), &allocateInfo, nullptr, &stagingMemory), VK_SUCCESS); - EXPECT_EQ(vkBindBufferMemory(device->GetDevice(), stagingBuffer, stagingMemory, 0), VK_SUCCESS); - - VkCommandPoolCreateInfo poolInfo = {}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.queueFamilyIndex = device->GetGraphicsQueueFamilyIndex(); - EXPECT_EQ(vkCreateCommandPool(device->GetDevice(), &poolInfo, nullptr, &commandPool), VK_SUCCESS); - - VkCommandBufferAllocateInfo commandBufferInfo = {}; - commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - commandBufferInfo.commandPool = commandPool; - commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandBufferInfo.commandBufferCount = 1; - EXPECT_EQ(vkAllocateCommandBuffers(device->GetDevice(), &commandBufferInfo, &commandBuffer), VK_SUCCESS); - - VkCommandBufferBeginInfo beginInfo = {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - EXPECT_EQ(vkBeginCommandBuffer(commandBuffer, &beginInfo), VK_SUCCESS); - - const ResourceStates previousState = texture->GetState(); - - VkImageMemoryBarrier toCopySource = {}; - toCopySource.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - toCopySource.oldLayout = ToImageLayout(previousState); - toCopySource.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - toCopySource.srcAccessMask = ToAccessMask(previousState); - toCopySource.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - toCopySource.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - toCopySource.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - toCopySource.image = texture->GetImage(); - toCopySource.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - toCopySource.subresourceRange.baseMipLevel = 0; - toCopySource.subresourceRange.levelCount = 1; - toCopySource.subresourceRange.baseArrayLayer = 0; - toCopySource.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier( - commandBuffer, - ToStageMask(previousState), - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, - 0, nullptr, - 0, nullptr, - 1, &toCopySource); - - VkBufferImageCopy copyRegion = {}; - copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copyRegion.imageSubresource.mipLevel = 0; - copyRegion.imageSubresource.baseArrayLayer = 0; - copyRegion.imageSubresource.layerCount = 1; - copyRegion.imageExtent.width = width; - copyRegion.imageExtent.height = height; - copyRegion.imageExtent.depth = 1; - vkCmdCopyImageToBuffer( - commandBuffer, - texture->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - stagingBuffer, - 1, - ©Region); - - VkImageMemoryBarrier restoreBarrier = toCopySource; - restoreBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - restoreBarrier.newLayout = ToImageLayout(previousState); - restoreBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - restoreBarrier.dstAccessMask = ToAccessMask(previousState); - vkCmdPipelineBarrier( - commandBuffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, - ToStageMask(previousState), - 0, - 0, nullptr, - 0, nullptr, - 1, &restoreBarrier); - - EXPECT_EQ(vkEndCommandBuffer(commandBuffer), VK_SUCCESS); - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - EXPECT_EQ(vkQueueSubmit(device->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE), VK_SUCCESS); - EXPECT_EQ(vkQueueWaitIdle(device->GetGraphicsQueue()), VK_SUCCESS); - - void* mappedData = nullptr; - EXPECT_EQ(vkMapMemory(device->GetDevice(), stagingMemory, 0, bufferSize, 0, &mappedData), VK_SUCCESS); - std::vector pixels(static_cast(bufferSize)); - std::copy_n(static_cast(mappedData), pixels.size(), pixels.data()); - vkUnmapMemory(device->GetDevice(), stagingMemory); - - vkDestroyCommandPool(device->GetDevice(), commandPool, nullptr); - vkFreeMemory(device->GetDevice(), stagingMemory, nullptr); - vkDestroyBuffer(device->GetDevice(), stagingBuffer, nullptr); - return pixels; - } - - RHIDevice* m_device = nullptr; - RHICommandQueue* m_queue = nullptr; -}; - TEST_F(VulkanGraphicsFixture, RenderPassFramebufferBeginRenderPassClearWritesColor) { AttachmentDesc colorAttachment = {}; colorAttachment.format = Format::R8G8B8A8_UNorm;