Extract Vulkan backend test fixture
This commit is contained in:
@@ -9,6 +9,7 @@ if(NOT TARGET Vulkan::Vulkan)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
|
fixtures/VulkanTestFixture.cpp
|
||||||
test_backend_specific.cpp
|
test_backend_specific.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
363
tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.cpp
Normal file
363
tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.cpp
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
#include "VulkanTestFixture.h"
|
||||||
|
|
||||||
|
#if defined(XCENGINE_SUPPORT_VULKAN)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#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<uint32_t>(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<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
desc.textureType = static_cast<uint32_t>(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<uint32_t>(Format::D24_UNorm_S8_UInt);
|
||||||
|
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||||
|
desc.sampleCount = 1;
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHICommandList* VulkanGraphicsFixture::CreateCommandList() const {
|
||||||
|
CommandListDesc desc = {};
|
||||||
|
desc.commandListType = static_cast<uint32_t>(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<uint8_t> VulkanGraphicsFixture::ReadTextureRgba8(VulkanTexture* texture) {
|
||||||
|
EXPECT_NE(texture, nullptr);
|
||||||
|
EXPECT_EQ(texture->GetFormat(), Format::R8G8B8A8_UNorm);
|
||||||
|
|
||||||
|
auto* device = static_cast<VulkanDevice*>(m_device);
|
||||||
|
const uint32_t width = texture->GetWidth();
|
||||||
|
const uint32_t height = texture->GetHeight();
|
||||||
|
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(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<uint8_t> pixels(static_cast<size_t>(bufferSize));
|
||||||
|
std::copy_n(static_cast<const uint8_t*>(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
|
||||||
43
tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.h
Normal file
43
tests/RHI/Vulkan/unit/fixtures/VulkanTestFixture.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(XCENGINE_SUPPORT_VULKAN)
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<uint8_t> ReadTextureRgba8(VulkanTexture* texture);
|
||||||
|
|
||||||
|
RHIDevice* m_device = nullptr;
|
||||||
|
RHICommandQueue* m_queue = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace RHI
|
||||||
|
} // namespace XCEngine
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -10,372 +10,20 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "XCEngine/RHI/RHICommandList.h"
|
#include "fixtures/VulkanTestFixture.h"
|
||||||
#include "XCEngine/RHI/RHICommandQueue.h"
|
|
||||||
#include "XCEngine/RHI/RHIDescriptorPool.h"
|
#include "XCEngine/RHI/RHIDescriptorPool.h"
|
||||||
#include "XCEngine/RHI/RHIDescriptorSet.h"
|
#include "XCEngine/RHI/RHIDescriptorSet.h"
|
||||||
#include "XCEngine/RHI/RHIDevice.h"
|
|
||||||
#include "XCEngine/RHI/RHIFramebuffer.h"
|
#include "XCEngine/RHI/RHIFramebuffer.h"
|
||||||
#include "XCEngine/RHI/RHIFactory.h"
|
|
||||||
#include "XCEngine/RHI/RHIPipelineLayout.h"
|
#include "XCEngine/RHI/RHIPipelineLayout.h"
|
||||||
#include "XCEngine/RHI/RHIPipelineState.h"
|
#include "XCEngine/RHI/RHIPipelineState.h"
|
||||||
#include "XCEngine/RHI/RHIRenderPass.h"
|
#include "XCEngine/RHI/RHIRenderPass.h"
|
||||||
#include "XCEngine/RHI/RHIResourceView.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"
|
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
|
||||||
|
|
||||||
using namespace XCEngine::RHI;
|
using namespace XCEngine::RHI;
|
||||||
|
|
||||||
namespace {
|
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<uint32_t>(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<uint32_t>(Format::R8G8B8A8_UNorm);
|
|
||||||
desc.textureType = static_cast<uint32_t>(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<uint32_t>(Format::D24_UNorm_S8_UInt);
|
|
||||||
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
|
||||||
desc.sampleCount = 1;
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
RHICommandList* CreateCommandList() const {
|
|
||||||
CommandListDesc desc = {};
|
|
||||||
desc.commandListType = static_cast<uint32_t>(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<uint8_t> ReadTextureRgba8(VulkanTexture* texture) {
|
|
||||||
EXPECT_NE(texture, nullptr);
|
|
||||||
EXPECT_EQ(texture->GetFormat(), Format::R8G8B8A8_UNorm);
|
|
||||||
|
|
||||||
auto* device = static_cast<VulkanDevice*>(m_device);
|
|
||||||
const uint32_t width = texture->GetWidth();
|
|
||||||
const uint32_t height = texture->GetHeight();
|
|
||||||
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(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<uint8_t> pixels(static_cast<size_t>(bufferSize));
|
|
||||||
std::copy_n(static_cast<const uint8_t*>(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) {
|
TEST_F(VulkanGraphicsFixture, RenderPassFramebufferBeginRenderPassClearWritesColor) {
|
||||||
AttachmentDesc colorAttachment = {};
|
AttachmentDesc colorAttachment = {};
|
||||||
colorAttachment.format = Format::R8G8B8A8_UNorm;
|
colorAttachment.format = Format::R8G8B8A8_UNorm;
|
||||||
|
|||||||
Reference in New Issue
Block a user