diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 8ee07b70e..1b19b88b1 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -6,6 +6,7 @@ #include "CmdBufferVulkan.h" #include "RenderToolsVulkan.h" #include "Engine/Core/Math/Color.h" +#include "Engine/Core/Math/Rectangle.h" #include "GPUBufferVulkan.h" #include "GPUShaderVulkan.h" #include "GPUSamplerVulkan.h" @@ -13,7 +14,8 @@ #include "Engine/Profiler/RenderStats.h" #include "GPUShaderProgramVulkan.h" #include "GPUTextureVulkan.h" -#include "Engine/Core/Math/Rectangle.h" +#include "Engine/Graphics/PixelFormatExtensions.h" +#include "Engine/Debug/Exceptions/NotImplementedException.h" // Ensure to match the indirect commands arguments layout static_assert(sizeof(GPUDispatchIndirectArgs) == sizeof(VkDispatchIndirectCommand), "Wrong size of GPUDrawIndirectArgs."); @@ -1554,15 +1556,127 @@ void GPUContextVulkan::CopyResource(GPUResource* dstResource, GPUResource* srcRe } else { - // TODO: implement it - MISSING_CODE("GPUContextVulkan::CopyResource"); + Log::NotImplementedException(TEXT("Cannot copy data between buffer and texture.")); } } void GPUContextVulkan::CopySubresource(GPUResource* dstResource, uint32 dstSubresource, GPUResource* srcResource, uint32 srcSubresource) { - // TODO: implement it - MISSING_CODE("GPUContextVulkan::CopySubresource"); + ASSERT(dstResource && srcResource); + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); + + // Ensure to end active render pass + if (cmdBuffer->IsInsideRenderPass()) + EndRenderPass(); + + auto dstTextureVulkan = static_cast(dstResource); + auto srcTextureVulkan = static_cast(srcResource); + + auto dstBufferVulkan = static_cast(dstResource); + auto srcBufferVulkan = static_cast(srcResource); + + auto srcType = srcResource->GetObjectType(); + auto dstType = dstResource->GetObjectType(); + + // Buffer -> Buffer + if (srcType == GPUResource::ObjectType::Buffer && dstType == GPUResource::ObjectType::Buffer) + { + ASSERT(dstSubresource == 0 && srcSubresource == 0); + + // Transition resources + AddBufferBarrier(dstBufferVulkan, VK_ACCESS_TRANSFER_WRITE_BIT); + AddBufferBarrier(srcBufferVulkan, VK_ACCESS_TRANSFER_READ_BIT); + FlushBarriers(); + + // Copy + VkBufferCopy bufferCopy; + bufferCopy.srcOffset = 0; + bufferCopy.dstOffset = 0; + bufferCopy.size = srcBufferVulkan->GetSize(); + ASSERT(bufferCopy.size == dstBufferVulkan->GetSize()); + vkCmdCopyBuffer(cmdBuffer->GetHandle(), srcBufferVulkan->GetHandle(), dstBufferVulkan->GetHandle(), 1, &bufferCopy); + } + // Texture -> Texture + else if (srcType == GPUResource::ObjectType::Texture && dstType == GPUResource::ObjectType::Texture) + { + const int32 dstMipMaps = dstTextureVulkan->MipLevels(); + const int32 dstMipIndex = dstSubresource % dstMipMaps; + const int32 dstArrayIndex = dstSubresource / dstMipMaps; + const int32 srcMipMaps = srcTextureVulkan->MipLevels(); + const int32 srcMipIndex = srcSubresource % srcMipMaps; + const int32 srcArrayIndex = srcSubresource / srcMipMaps; + + if (dstTextureVulkan->IsStaging()) + { + // Staging Texture -> Staging Texture + if (srcTextureVulkan->IsStaging()) + { + ASSERT(dstTextureVulkan->StagingBuffer && srcTextureVulkan->StagingBuffer); + CopyResource(dstTextureVulkan->StagingBuffer, srcTextureVulkan->StagingBuffer); + } + // Texture -> Staging Texture + else + { + // Transition resources + ASSERT(dstTextureVulkan->StagingBuffer); + AddBufferBarrier(dstTextureVulkan->StagingBuffer, VK_ACCESS_TRANSFER_WRITE_BIT); + AddImageBarrier(srcTextureVulkan, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + FlushBarriers(); + + // Copy + int32 copyOffset = 0; + uint32 subResourceCount = 0; + for (int32 arraySlice = 0; arraySlice < dstTextureVulkan->ArraySize() && subResourceCount < dstSubresource; arraySlice++) + { + for (int32 mipLevel = 0; mipLevel < dstMipMaps && subResourceCount < dstSubresource; mipLevel++) + { + // TODO: pitch/slice alignment on Vulkan? + copyOffset += dstTextureVulkan->ComputeSubresourceSize(mipLevel, 1, 1); + subResourceCount++; + } + } + VkBufferImageCopy region; + region.bufferOffset = copyOffset; + region.bufferRowLength = Math::Max(dstTextureVulkan->Width() >> dstMipIndex, 1); + region.bufferImageHeight = Math::Max(dstTextureVulkan->Height() >> dstMipIndex, 1); + region.imageOffset = { 0, 0, 0 }; + region.imageExtent = { Math::Max(srcTextureVulkan->Width() >> srcMipIndex, 1), Math::Max(srcTextureVulkan->Height() >> srcMipIndex, 1), Math::Max(srcTextureVulkan->Depth() >> srcMipIndex, 1) }; + region.imageSubresource.baseArrayLayer = srcArrayIndex; + region.imageSubresource.layerCount = 1; + region.imageSubresource.mipLevel = srcMipIndex; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdCopyImageToBuffer(cmdBuffer->GetHandle(), srcTextureVulkan->GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstTextureVulkan->StagingBuffer->GetHandle(), 1, ®ion); + } + } + else + { + // Transition resources + AddImageBarrier(dstTextureVulkan, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + AddImageBarrier(srcTextureVulkan, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + FlushBarriers(); + + // Copy + int32 mipWidth, mipHeight, mipDepth; + srcTextureVulkan->GetMipSize(srcMipIndex, mipWidth, mipHeight, mipDepth); + VkImageCopy region;; + region.extent = { Math::Max(mipWidth, 1), Math::Max(mipWidth, 1), Math::Max(mipDepth, 1) }; + region.srcOffset = { 0, 0, 0 }; + region.srcSubresource.baseArrayLayer = srcArrayIndex; + region.srcSubresource.layerCount = 1; + region.srcSubresource.mipLevel = srcMipIndex; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstOffset = { 0, 0, 0 }; + region.dstSubresource.baseArrayLayer = dstArrayIndex; + region.dstSubresource.layerCount = 1; + region.dstSubresource.mipLevel = dstMipIndex; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdCopyImage(cmdBuffer->GetHandle(), srcTextureVulkan->GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstTextureVulkan->GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + } + } + else + { + Log::NotImplementedException(TEXT("Cannot copy data between buffer and texture.")); + } } #endif