Add Vulkan render pass and copy coverage
This commit is contained in:
@@ -9,7 +9,9 @@ namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
class VulkanDevice;
|
||||
class VulkanFramebuffer;
|
||||
class VulkanPipelineState;
|
||||
class VulkanRenderPass;
|
||||
class VulkanTexture;
|
||||
|
||||
class VulkanCommandList : public RHICommandList {
|
||||
|
||||
@@ -51,6 +51,8 @@ public:
|
||||
VkImage GetImage() const { return m_image; }
|
||||
VkDeviceMemory GetMemory() const { return m_memory; }
|
||||
VkFormat GetVkFormat() const { return m_vkFormat; }
|
||||
bool OwnsImage() const { return m_ownsImage; }
|
||||
bool IsSwapChainImage() const { return !m_ownsImage; }
|
||||
|
||||
private:
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
#include "XCEngine/RHI/Vulkan/VulkanBuffer.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanFramebuffer.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanRenderPass.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -26,10 +29,18 @@ VkImageLayout ToVulkanImageLayout(ResourceStates state) {
|
||||
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::DepthWrite:
|
||||
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
case ResourceStates::DepthRead:
|
||||
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_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;
|
||||
}
|
||||
@@ -37,16 +48,27 @@ VkImageLayout ToVulkanImageLayout(ResourceStates state) {
|
||||
|
||||
VkAccessFlags ToVulkanAccessMask(ResourceStates state) {
|
||||
switch (state) {
|
||||
case ResourceStates::VertexAndConstantBuffer:
|
||||
return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT;
|
||||
case ResourceStates::IndexBuffer:
|
||||
return VK_ACCESS_INDEX_READ_BIT;
|
||||
case ResourceStates::RenderTarget:
|
||||
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
case ResourceStates::UnorderedAccess:
|
||||
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_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::DepthWrite:
|
||||
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
case ResourceStates::GenericRead:
|
||||
return VK_ACCESS_MEMORY_READ_BIT;
|
||||
case ResourceStates::Present:
|
||||
case ResourceStates::Common:
|
||||
default:
|
||||
@@ -56,15 +78,29 @@ VkAccessFlags ToVulkanAccessMask(ResourceStates state) {
|
||||
|
||||
VkPipelineStageFlags ToVulkanStageMask(ResourceStates state) {
|
||||
switch (state) {
|
||||
case ResourceStates::VertexAndConstantBuffer:
|
||||
return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
|
||||
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
case ResourceStates::IndexBuffer:
|
||||
return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
case ResourceStates::RenderTarget:
|
||||
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
case ResourceStates::UnorderedAccess:
|
||||
return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
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::DepthWrite:
|
||||
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
case ResourceStates::GenericRead:
|
||||
return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
case ResourceStates::Present:
|
||||
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
case ResourceStates::Common:
|
||||
@@ -85,6 +121,40 @@ VkImageAspectFlags ResolveClearAspectMask(Format format, uint32_t buffers) {
|
||||
return aspectMask;
|
||||
}
|
||||
|
||||
ResourceStates ResolvePostCopyState(ResourceStates previousState, ResourceStates fallbackState) {
|
||||
return previousState == ResourceStates::Common ? fallbackState : previousState;
|
||||
}
|
||||
|
||||
void TransitionBuffer(VkCommandBuffer commandBuffer, VulkanBuffer* buffer, ResourceStates newState) {
|
||||
if (commandBuffer == VK_NULL_HANDLE || buffer == nullptr || buffer->GetBuffer() == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
if (buffer->GetState() == newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
VkBufferMemoryBarrier barrier = {};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||
barrier.srcAccessMask = ToVulkanAccessMask(buffer->GetState());
|
||||
barrier.dstAccessMask = ToVulkanAccessMask(newState);
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.buffer = buffer->GetBuffer();
|
||||
barrier.offset = 0;
|
||||
barrier.size = buffer->GetSize();
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
ToVulkanStageMask(buffer->GetState()),
|
||||
ToVulkanStageMask(newState),
|
||||
0,
|
||||
0, nullptr,
|
||||
1, &barrier,
|
||||
0, nullptr);
|
||||
|
||||
buffer->SetState(newState);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VulkanCommandList::~VulkanCommandList() {
|
||||
@@ -138,9 +208,12 @@ void VulkanCommandList::Reset() {
|
||||
return;
|
||||
}
|
||||
|
||||
EndActiveRenderPass();
|
||||
vkResetCommandPool(m_device->GetDevice(), m_commandPool, 0);
|
||||
DestroyTransientFramebuffers();
|
||||
|
||||
m_currentColorTarget = nullptr;
|
||||
m_currentDepthTarget = nullptr;
|
||||
m_currentPipelineState = nullptr;
|
||||
m_currentPrimitiveTopology = PrimitiveTopology::TriangleList;
|
||||
m_renderPassActive = false;
|
||||
@@ -157,7 +230,9 @@ void VulkanCommandList::Close() {
|
||||
EndActiveRenderPass();
|
||||
|
||||
auto* colorView = static_cast<VulkanResourceView*>(m_currentColorTarget);
|
||||
if (colorView != nullptr && colorView->GetTexture() != nullptr) {
|
||||
if (colorView != nullptr &&
|
||||
colorView->GetTexture() != nullptr &&
|
||||
colorView->GetTexture()->IsSwapChainImage()) {
|
||||
TransitionTexture(colorView->GetTexture(), ResourceStates::Present);
|
||||
}
|
||||
|
||||
@@ -211,11 +286,96 @@ void VulkanCommandList::TransitionBarrier(RHIResourceView* resource, ResourceSta
|
||||
}
|
||||
|
||||
void VulkanCommandList::BeginRenderPass(class RHIRenderPass* renderPass, class RHIFramebuffer* framebuffer, const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) {
|
||||
(void)renderPass;
|
||||
(void)framebuffer;
|
||||
(void)renderArea;
|
||||
(void)clearValueCount;
|
||||
(void)clearValues;
|
||||
if (m_commandBuffer == VK_NULL_HANDLE || renderPass == nullptr || framebuffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* vulkanRenderPass = static_cast<VulkanRenderPass*>(renderPass);
|
||||
auto* vulkanFramebuffer = static_cast<VulkanFramebuffer*>(framebuffer);
|
||||
if (vulkanRenderPass == nullptr ||
|
||||
vulkanFramebuffer == nullptr ||
|
||||
vulkanRenderPass->GetRenderPass() == VK_NULL_HANDLE ||
|
||||
vulkanFramebuffer->GetFramebuffer() == VK_NULL_HANDLE ||
|
||||
vulkanFramebuffer->GetRenderPass() != vulkanRenderPass) {
|
||||
return;
|
||||
}
|
||||
|
||||
EndActiveRenderPass();
|
||||
|
||||
const uint32_t colorAttachmentCount = vulkanFramebuffer->GetColorAttachmentCount();
|
||||
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
|
||||
VulkanTexture* texture = vulkanFramebuffer->GetColorAttachmentTexture(i);
|
||||
if (texture != nullptr) {
|
||||
TransitionTexture(texture, ResourceStates::RenderTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (VulkanTexture* depthTexture = vulkanFramebuffer->GetDepthStencilTexture()) {
|
||||
TransitionTexture(depthTexture, ResourceStates::DepthWrite);
|
||||
}
|
||||
|
||||
std::vector<VkClearValue> vkClearValues(vulkanRenderPass->GetAttachmentCount());
|
||||
const AttachmentDesc* colorAttachments = vulkanRenderPass->GetColorAttachments();
|
||||
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
|
||||
if (clearValues != nullptr &&
|
||||
clearValueCount > i &&
|
||||
colorAttachments != nullptr &&
|
||||
colorAttachments[i].loadOp == LoadAction::Clear) {
|
||||
vkClearValues[i].color.float32[0] = clearValues[i].color.r;
|
||||
vkClearValues[i].color.float32[1] = clearValues[i].color.g;
|
||||
vkClearValues[i].color.float32[2] = clearValues[i].color.b;
|
||||
vkClearValues[i].color.float32[3] = clearValues[i].color.a;
|
||||
}
|
||||
}
|
||||
|
||||
if (vulkanRenderPass->HasDepthStencil()) {
|
||||
const uint32_t depthIndex = colorAttachmentCount;
|
||||
const AttachmentDesc* depthAttachment = vulkanRenderPass->GetDepthStencilAttachment();
|
||||
if (clearValues != nullptr && clearValueCount > depthIndex && depthAttachment != nullptr) {
|
||||
if (depthAttachment->loadOp == LoadAction::Clear) {
|
||||
vkClearValues[depthIndex].depthStencil.depth = clearValues[depthIndex].depth;
|
||||
}
|
||||
if (depthAttachment->stencilLoadOp == LoadAction::Clear) {
|
||||
vkClearValues[depthIndex].depthStencil.stencil = clearValues[depthIndex].stencil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t width = renderArea.right > renderArea.left
|
||||
? static_cast<uint32_t>(renderArea.right - renderArea.left)
|
||||
: vulkanFramebuffer->GetWidth();
|
||||
const uint32_t height = renderArea.bottom > renderArea.top
|
||||
? static_cast<uint32_t>(renderArea.bottom - renderArea.top)
|
||||
: vulkanFramebuffer->GetHeight();
|
||||
|
||||
VkRenderPassBeginInfo renderPassInfo = {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassInfo.renderPass = vulkanRenderPass->GetRenderPass();
|
||||
renderPassInfo.framebuffer = vulkanFramebuffer->GetFramebuffer();
|
||||
renderPassInfo.renderArea.offset = { renderArea.left, renderArea.top };
|
||||
renderPassInfo.renderArea.extent = { width, height };
|
||||
renderPassInfo.clearValueCount = static_cast<uint32_t>(vkClearValues.size());
|
||||
renderPassInfo.pClearValues = vkClearValues.empty() ? nullptr : vkClearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(m_commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
m_currentColorTarget = colorAttachmentCount > 0 ? vulkanFramebuffer->GetColorAttachmentView(0) : nullptr;
|
||||
m_currentDepthTarget = vulkanFramebuffer->GetDepthStencilView();
|
||||
m_renderPassActive = true;
|
||||
|
||||
m_viewport.x = static_cast<float>(renderArea.left);
|
||||
m_viewport.y = static_cast<float>(renderArea.top) + static_cast<float>(height);
|
||||
m_viewport.width = static_cast<float>(width);
|
||||
m_viewport.height = -static_cast<float>(height);
|
||||
m_viewport.minDepth = 0.0f;
|
||||
m_viewport.maxDepth = 1.0f;
|
||||
m_hasViewport = true;
|
||||
vkCmdSetViewport(m_commandBuffer, 0, 1, &m_viewport);
|
||||
|
||||
m_scissor.offset = { renderArea.left, renderArea.top };
|
||||
m_scissor.extent = { width, height };
|
||||
m_hasScissor = true;
|
||||
vkCmdSetScissor(m_commandBuffer, 0, 1, &m_scissor);
|
||||
}
|
||||
|
||||
void VulkanCommandList::EndRenderPass() {
|
||||
@@ -516,8 +676,89 @@ void VulkanCommandList::ClearDepthStencil(RHIResourceView* depthStencil, float d
|
||||
}
|
||||
|
||||
void VulkanCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) {
|
||||
(void)dst;
|
||||
(void)src;
|
||||
auto* dstView = static_cast<VulkanResourceView*>(dst);
|
||||
auto* srcView = static_cast<VulkanResourceView*>(src);
|
||||
if (m_commandBuffer == VK_NULL_HANDLE ||
|
||||
dstView == nullptr ||
|
||||
srcView == nullptr ||
|
||||
!dstView->IsValid() ||
|
||||
!srcView->IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EndActiveRenderPass();
|
||||
|
||||
VulkanTexture* dstTexture = dstView->GetTexture();
|
||||
VulkanTexture* srcTexture = srcView->GetTexture();
|
||||
if (dstTexture != nullptr && srcTexture != nullptr) {
|
||||
const VkImageAspectFlags srcAspectMask = GetImageAspectMask(srcTexture->GetFormat());
|
||||
const VkImageAspectFlags dstAspectMask = GetImageAspectMask(dstTexture->GetFormat());
|
||||
if (srcAspectMask != dstAspectMask) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ResourceStates previousSrcState = srcTexture->GetState();
|
||||
const ResourceStates previousDstState = dstTexture->GetState();
|
||||
|
||||
TransitionTexture(srcTexture, ResourceStates::CopySrc);
|
||||
TransitionTexture(dstTexture, ResourceStates::CopyDst);
|
||||
|
||||
VkImageCopy copyRegion = {};
|
||||
copyRegion.srcSubresource.aspectMask = srcAspectMask;
|
||||
copyRegion.srcSubresource.mipLevel = 0;
|
||||
copyRegion.srcSubresource.baseArrayLayer = 0;
|
||||
copyRegion.srcSubresource.layerCount = 1;
|
||||
copyRegion.dstSubresource.aspectMask = dstAspectMask;
|
||||
copyRegion.dstSubresource.mipLevel = 0;
|
||||
copyRegion.dstSubresource.baseArrayLayer = 0;
|
||||
copyRegion.dstSubresource.layerCount = 1;
|
||||
copyRegion.extent.width = (std::min)(srcTexture->GetWidth(), dstTexture->GetWidth());
|
||||
copyRegion.extent.height = (std::min)(srcTexture->GetHeight(), dstTexture->GetHeight());
|
||||
copyRegion.extent.depth = (std::max)(1u, (std::min)(srcTexture->GetDepth(), dstTexture->GetDepth()));
|
||||
|
||||
vkCmdCopyImage(
|
||||
m_commandBuffer,
|
||||
srcTexture->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
dstTexture->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
©Region);
|
||||
|
||||
TransitionTexture(srcTexture, ResolvePostCopyState(previousSrcState, ResourceStates::PixelShaderResource));
|
||||
TransitionTexture(dstTexture, ResolvePostCopyState(previousDstState, ResourceStates::PixelShaderResource));
|
||||
return;
|
||||
}
|
||||
|
||||
VulkanBuffer* dstBuffer = dstView->GetBufferResource();
|
||||
VulkanBuffer* srcBuffer = srcView->GetBufferResource();
|
||||
if (dstBuffer != nullptr && srcBuffer != nullptr) {
|
||||
const VkDeviceSize srcSize = srcView->GetBufferSize() > 0
|
||||
? static_cast<VkDeviceSize>(srcView->GetBufferSize())
|
||||
: static_cast<VkDeviceSize>(srcBuffer->GetSize());
|
||||
const VkDeviceSize dstSize = dstView->GetBufferSize() > 0
|
||||
? static_cast<VkDeviceSize>(dstView->GetBufferSize())
|
||||
: static_cast<VkDeviceSize>(dstBuffer->GetSize());
|
||||
const VkDeviceSize copySize = (std::min)(srcSize, dstSize);
|
||||
if (copySize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ResourceStates previousSrcState = srcBuffer->GetState();
|
||||
const ResourceStates previousDstState = dstBuffer->GetState();
|
||||
|
||||
TransitionBuffer(m_commandBuffer, srcBuffer, ResourceStates::CopySrc);
|
||||
TransitionBuffer(m_commandBuffer, dstBuffer, ResourceStates::CopyDst);
|
||||
|
||||
VkBufferCopy copyRegion = {};
|
||||
copyRegion.srcOffset = srcView->GetBufferOffset();
|
||||
copyRegion.dstOffset = dstView->GetBufferOffset();
|
||||
copyRegion.size = copySize;
|
||||
vkCmdCopyBuffer(m_commandBuffer, srcBuffer->GetBuffer(), dstBuffer->GetBuffer(), 1, ©Region);
|
||||
|
||||
TransitionBuffer(m_commandBuffer, srcBuffer, ResolvePostCopyState(previousSrcState, ResourceStates::GenericRead));
|
||||
TransitionBuffer(m_commandBuffer, dstBuffer, ResolvePostCopyState(previousDstState, ResourceStates::GenericRead));
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanCommandList::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
#include "XCEngine/RHI/Vulkan/VulkanDescriptorPool.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanFence.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanFramebuffer.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanRenderPass.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
|
||||
@@ -648,19 +650,22 @@ RHISampler* VulkanDevice::CreateSampler(const SamplerDesc& desc) {
|
||||
}
|
||||
|
||||
RHIRenderPass* VulkanDevice::CreateRenderPass(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments, const AttachmentDesc* depthStencilAttachment) {
|
||||
(void)colorAttachmentCount;
|
||||
(void)colorAttachments;
|
||||
(void)depthStencilAttachment;
|
||||
auto* renderPass = new VulkanRenderPass();
|
||||
if (renderPass->Initialize(m_device, colorAttachmentCount, colorAttachments, depthStencilAttachment)) {
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
delete renderPass;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RHIFramebuffer* VulkanDevice::CreateFramebuffer(class RHIRenderPass* renderPass, uint32_t width, uint32_t height, uint32_t colorAttachmentCount, RHIResourceView** colorAttachments, RHIResourceView* depthStencilAttachment) {
|
||||
(void)renderPass;
|
||||
(void)width;
|
||||
(void)height;
|
||||
(void)colorAttachmentCount;
|
||||
(void)colorAttachments;
|
||||
(void)depthStencilAttachment;
|
||||
auto* framebuffer = new VulkanFramebuffer();
|
||||
if (framebuffer->Initialize(m_device, renderPass, width, height, colorAttachmentCount, colorAttachments, depthStencilAttachment)) {
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
delete framebuffer;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,15 @@ bool VulkanFramebuffer::Initialize(VkDevice device, RHIRenderPass* renderPass, u
|
||||
m_renderPass = vulkanRenderPass;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
if (colorAttachmentCount != vulkanRenderPass->GetColorAttachmentCount()) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
if ((vulkanRenderPass->HasDepthStencil() && depthStencilAttachment == nullptr) ||
|
||||
(!vulkanRenderPass->HasDepthStencil() && depthStencilAttachment != nullptr)) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
m_colorAttachmentViews.reserve(colorAttachmentCount);
|
||||
|
||||
std::vector<VkImageView> attachments;
|
||||
|
||||
@@ -51,7 +51,9 @@ bool VulkanRenderPass::Initialize(VkDevice device, uint32_t colorAttachmentCount
|
||||
|
||||
m_device = device;
|
||||
m_colorAttachmentCount = colorAttachmentCount;
|
||||
m_colorAttachments.assign(colorAttachments, colorAttachments + colorAttachmentCount);
|
||||
if (colorAttachmentCount > 0) {
|
||||
m_colorAttachments.assign(colorAttachments, colorAttachments + colorAttachmentCount);
|
||||
}
|
||||
if (depthStencilAttachment != nullptr) {
|
||||
m_depthStencilAttachment = *depthStencilAttachment;
|
||||
m_hasDepthStencil = true;
|
||||
|
||||
@@ -26,6 +26,7 @@ set(TEST_SOURCES
|
||||
test_capabilities.cpp
|
||||
test_views.cpp
|
||||
test_screenshot.cpp
|
||||
test_vulkan_graphics.cpp
|
||||
${CMAKE_SOURCE_DIR}/tests/opengl/package/src/glad.c
|
||||
)
|
||||
|
||||
|
||||
422
tests/RHI/unit/test_vulkan_graphics.cpp
Normal file
422
tests/RHI/unit/test_vulkan_graphics.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
#if defined(XCENGINE_SUPPORT_VULKAN)
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "XCEngine/RHI/RHICommandList.h"
|
||||
#include "XCEngine/RHI/RHICommandQueue.h"
|
||||
#include "XCEngine/RHI/RHIDevice.h"
|
||||
#include "XCEngine/RHI/RHIFramebuffer.h"
|
||||
#include "XCEngine/RHI/RHIFactory.h"
|
||||
#include "XCEngine/RHI/RHIRenderPass.h"
|
||||
#include "XCEngine/RHI/RHIResourceView.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 {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
AttachmentDesc colorAttachment = {};
|
||||
colorAttachment.format = Format::R8G8B8A8_UNorm;
|
||||
colorAttachment.loadOp = LoadAction::Clear;
|
||||
colorAttachment.storeOp = StoreAction::Store;
|
||||
|
||||
AttachmentDesc depthAttachment = {};
|
||||
depthAttachment.format = Format::D24_UNorm_S8_UInt;
|
||||
depthAttachment.loadOp = LoadAction::Clear;
|
||||
depthAttachment.storeOp = StoreAction::Store;
|
||||
depthAttachment.stencilLoadOp = LoadAction::Clear;
|
||||
depthAttachment.stencilStoreOp = StoreAction::Store;
|
||||
|
||||
RHIRenderPass* renderPass = m_device->CreateRenderPass(1, &colorAttachment, &depthAttachment);
|
||||
ASSERT_NE(renderPass, nullptr);
|
||||
|
||||
RHITexture* colorTexture = m_device->CreateTexture(CreateColorTextureDesc(64, 64));
|
||||
RHITexture* depthTexture = m_device->CreateTexture(CreateDepthTextureDesc(64, 64));
|
||||
ASSERT_NE(colorTexture, nullptr);
|
||||
ASSERT_NE(depthTexture, nullptr);
|
||||
|
||||
RHIResourceView* colorView = m_device->CreateRenderTargetView(colorTexture, {});
|
||||
RHIResourceView* depthView = m_device->CreateDepthStencilView(depthTexture, {});
|
||||
ASSERT_NE(colorView, nullptr);
|
||||
ASSERT_NE(depthView, nullptr);
|
||||
|
||||
RHIFramebuffer* framebuffer = m_device->CreateFramebuffer(renderPass, 64, 64, 1, &colorView, depthView);
|
||||
ASSERT_NE(framebuffer, nullptr);
|
||||
|
||||
RHICommandList* commandList = CreateCommandList();
|
||||
ASSERT_NE(commandList, nullptr);
|
||||
|
||||
ClearValue clearValues[2] = {};
|
||||
clearValues[0].color = { 0.25f, 0.5f, 0.75f, 1.0f };
|
||||
clearValues[1].depth = 1.0f;
|
||||
clearValues[1].stencil = 0;
|
||||
|
||||
commandList->Reset();
|
||||
commandList->BeginRenderPass(renderPass, framebuffer, Rect{0, 0, 64, 64}, 2, clearValues);
|
||||
commandList->EndRenderPass();
|
||||
SubmitAndWait(commandList);
|
||||
|
||||
const std::vector<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(colorTexture));
|
||||
ASSERT_GE(pixels.size(), 4u);
|
||||
EXPECT_NEAR(static_cast<int>(pixels[0]), 64, 1);
|
||||
EXPECT_NEAR(static_cast<int>(pixels[1]), 128, 1);
|
||||
EXPECT_NEAR(static_cast<int>(pixels[2]), 191, 1);
|
||||
EXPECT_EQ(pixels[3], 255u);
|
||||
|
||||
commandList->Shutdown();
|
||||
delete commandList;
|
||||
framebuffer->Shutdown();
|
||||
delete framebuffer;
|
||||
delete depthView;
|
||||
delete colorView;
|
||||
depthTexture->Shutdown();
|
||||
delete depthTexture;
|
||||
colorTexture->Shutdown();
|
||||
delete colorTexture;
|
||||
renderPass->Shutdown();
|
||||
delete renderPass;
|
||||
}
|
||||
|
||||
TEST_F(VulkanGraphicsFixture, CopyResourceCopiesTexturePixels) {
|
||||
constexpr uint32_t kWidth = 32;
|
||||
constexpr uint32_t kHeight = 16;
|
||||
|
||||
std::vector<uint8_t> sourcePixels(kWidth * kHeight * 4);
|
||||
for (uint32_t y = 0; y < kHeight; ++y) {
|
||||
for (uint32_t x = 0; x < kWidth; ++x) {
|
||||
const size_t index = static_cast<size_t>((y * kWidth + x) * 4);
|
||||
sourcePixels[index + 0] = static_cast<uint8_t>((x * 13) & 0xFF);
|
||||
sourcePixels[index + 1] = static_cast<uint8_t>((y * 29) & 0xFF);
|
||||
sourcePixels[index + 2] = static_cast<uint8_t>(((x + y) * 17) & 0xFF);
|
||||
sourcePixels[index + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
TextureDesc textureDesc = CreateColorTextureDesc(kWidth, kHeight);
|
||||
RHITexture* sourceTexture = m_device->CreateTexture(textureDesc, sourcePixels.data(), sourcePixels.size(), kWidth * 4);
|
||||
RHITexture* destinationTexture = m_device->CreateTexture(textureDesc);
|
||||
ASSERT_NE(sourceTexture, nullptr);
|
||||
ASSERT_NE(destinationTexture, nullptr);
|
||||
|
||||
RHIResourceView* sourceView = m_device->CreateShaderResourceView(sourceTexture, {});
|
||||
RHIResourceView* destinationView = m_device->CreateShaderResourceView(destinationTexture, {});
|
||||
ASSERT_NE(sourceView, nullptr);
|
||||
ASSERT_NE(destinationView, nullptr);
|
||||
|
||||
RHICommandList* commandList = CreateCommandList();
|
||||
ASSERT_NE(commandList, nullptr);
|
||||
|
||||
commandList->Reset();
|
||||
commandList->CopyResource(destinationView, sourceView);
|
||||
SubmitAndWait(commandList);
|
||||
|
||||
const std::vector<uint8_t> copiedPixels = ReadTextureRgba8(static_cast<VulkanTexture*>(destinationTexture));
|
||||
EXPECT_EQ(copiedPixels, sourcePixels);
|
||||
|
||||
commandList->Shutdown();
|
||||
delete commandList;
|
||||
delete destinationView;
|
||||
delete sourceView;
|
||||
destinationTexture->Shutdown();
|
||||
delete destinationTexture;
|
||||
sourceTexture->Shutdown();
|
||||
delete sourceTexture;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user