diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index c555728d..16c3f876 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -516,7 +516,7 @@ namespace dxvk { if (cPackedFormat == VK_FORMAT_UNDEFINED) { ctx->copyBufferToImage(cDstImage, cDstLayers, VkOffset3D { 0, 0, 0 }, cDstLevelExtent, - cSrcBuffer, 0, { 0u, 0u }); + cSrcBuffer, 0, 0); } else { ctx->copyPackedBufferToDepthStencilImage( cDstImage, cDstLayers, diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 242e6f3f..cd7d16a6 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -707,8 +707,7 @@ namespace dxvk { ] (DxvkContext* ctx) { ctx->copyBufferToImage( cDstImage, cDstLayers, cDstOffset, cCopyExtent, - cSrcSlice.buffer(), cSrcSlice.offset(), - VkExtent2D { 0, 0 }); + cSrcSlice.buffer(), cSrcSlice.offset(), 0); }); dstTextureInfo->SetWrittenByGPU(dst->GetSubresource(), true); @@ -801,8 +800,7 @@ namespace dxvk { ctx->copyBufferToImage( cDstImage, cDstLayers, cOffset, cExtent, - cSrcSlice.buffer(), cSrcSlice.offset(), - VkExtent2D { 0, 0 }); + cSrcSlice.buffer(), cSrcSlice.offset(), 0); }); dstTexInfo->SetWrittenByGPU(dstTexInfo->CalcSubresource(a, m), true); @@ -4359,8 +4357,7 @@ namespace dxvk { ctx->copyBufferToImage( cDstImage, cDstLayers, cOffset, cDstLevelExtent, - cSrcSlice.buffer(), cSrcSlice.offset(), - VkExtent2D { 0, 0 }); + cSrcSlice.buffer(), cSrcSlice.offset(), 0); }); } else { diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 9324e4e5..6d9361d8 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -704,14 +704,14 @@ namespace dxvk { VkExtent3D dstExtent, const Rc& srcBuffer, VkDeviceSize srcOffset, - VkExtent2D srcExtent) { + VkDeviceSize rowAlignment) { this->spillRenderPass(true); this->prepareImage(m_execBarriers, dstImage, vk::makeSubresourceRange(dstSubresource)); auto srcSlice = srcBuffer->getSliceHandle(srcOffset, 0); - // We may copy to only one aspect of a depth-stencil image, - // but pipeline barriers need to have all aspect bits set + // We may copy to only one aspect at a time, but pipeline + // barriers need to have all available aspect bits set auto dstFormatInfo = dstImage->formatInfo(); auto dstSubresourceRange = vk::makeSubresourceRange(dstSubresource); @@ -739,19 +739,10 @@ namespace dxvk { } m_execAcquires.recordCommands(m_cmd); - - VkBufferImageCopy copyRegion; - copyRegion.bufferOffset = srcSlice.offset; - copyRegion.bufferRowLength = srcExtent.width; - copyRegion.bufferImageHeight = srcExtent.height; - copyRegion.imageSubresource = dstSubresource; - copyRegion.imageOffset = dstOffset; - copyRegion.imageExtent = dstExtent; - - m_cmd->cmdCopyBufferToImage(DxvkCmdBuffer::ExecBuffer, - srcSlice.handle, dstImage->handle(), - dstImageLayoutTransfer, 1, ©Region); - + + this->copyImageBufferData(DxvkCmdBuffer::ExecBuffer, dstImage, dstSubresource, + dstOffset, dstExtent, dstImageLayoutTransfer, srcSlice, rowAlignment, 0); + m_execBarriers.accessImage( dstImage, dstSubresourceRange, dstImageLayoutTransfer, @@ -2754,6 +2745,91 @@ namespace dxvk { } + template + void DxvkContext::copyImageBufferData( + DxvkCmdBuffer cmd, + const Rc& image, + const VkImageSubresourceLayers& imageSubresource, + VkOffset3D imageOffset, + VkExtent3D imageExtent, + VkImageLayout imageLayout, + const DxvkBufferSliceHandle& bufferSlice, + VkDeviceSize bufferRowAlignment, + VkDeviceSize bufferSliceAlignment) { + auto formatInfo = image->formatInfo(); + auto layers = imageSubresource.layerCount; + + VkDeviceSize bufferOffset = bufferSlice.offset; + + // Do one copy region per layer in case the buffer memory layout is weird + if (bufferSliceAlignment || formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) + layers = 1; + + for (uint32_t i = 0; i < imageSubresource.layerCount; i += layers) { + auto aspectOffset = bufferOffset; + + for (auto aspects = imageSubresource.aspectMask; aspects; ) { + auto aspect = vk::getNextAspect(aspects); + auto elementSize = formatInfo->elementSize; + + VkBufferImageCopy copyRegion = { }; + copyRegion.imageSubresource.aspectMask = aspect; + copyRegion.imageSubresource.baseArrayLayer = imageSubresource.baseArrayLayer + i; + copyRegion.imageSubresource.layerCount = layers; + copyRegion.imageSubresource.mipLevel = imageSubresource.mipLevel; + copyRegion.imageOffset = imageOffset; + copyRegion.imageExtent = imageExtent; + + if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) { + auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)]; + copyRegion.imageOffset.x /= plane->blockSize.width; + copyRegion.imageOffset.y /= plane->blockSize.height; + copyRegion.imageExtent.width /= plane->blockSize.width; + copyRegion.imageExtent.height /= plane->blockSize.height; + elementSize = plane->elementSize; + } + + // Vulkan can't really express row pitch in the same way that client APIs + // may expect, so we'll need to do some heroics here and hope that it works + VkExtent3D blockCount = util::computeBlockCount(copyRegion.imageExtent, formatInfo->blockSize); + VkDeviceSize rowPitch = blockCount.width * elementSize; + + if (bufferRowAlignment > elementSize) + rowPitch = bufferRowAlignment > rowPitch ? bufferRowAlignment : align(rowPitch, bufferRowAlignment); + + VkDeviceSize slicePitch = blockCount.height * rowPitch; + + if (image->info().type == VK_IMAGE_TYPE_3D && bufferSliceAlignment > elementSize) + slicePitch = bufferSliceAlignment > slicePitch ? bufferSliceAlignment : align(slicePitch, bufferSliceAlignment); + + copyRegion.bufferOffset = aspectOffset; + copyRegion.bufferRowLength = formatInfo->blockSize.width * rowPitch / elementSize; + copyRegion.bufferImageHeight = formatInfo->blockSize.height * slicePitch / rowPitch; + + // Perform the actual copy + if constexpr (ToImage) { + m_cmd->cmdCopyBufferToImage(cmd, bufferSlice.handle, + image->handle(), imageLayout, 1, ©Region); + } else { + m_cmd->cmdCopyImageToBuffer(cmd, image->handle(), imageLayout, + bufferSlice.handle, 1, ©Region); + } + + aspectOffset += blockCount.depth * slicePitch; + } + + // Advance to next layer. This is non-trivial for multi-plane formats + // since plane data for each layer is expected to be packed. + VkDeviceSize layerPitch = aspectOffset - bufferOffset; + + if (bufferSliceAlignment) + layerPitch = bufferSliceAlignment > layerPitch ? bufferSliceAlignment : align(layerPitch, bufferSliceAlignment); + + bufferOffset += layerPitch; + } + } + + void DxvkContext::clearImageViewFb( const Rc& imageView, VkOffset3D offset, diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index efd9a705..0ec22889 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -341,13 +341,14 @@ namespace dxvk { /** * \brief Copies data from a buffer to an image * + * Source data must be packed, except for the row alignment. * \param [in] dstImage Destination image * \param [in] dstSubresource Destination subresource * \param [in] dstOffset Destination area offset * \param [in] dstExtent Destination area size * \param [in] srcBuffer Source buffer * \param [in] srcOffset Source offset, in bytes - * \param [in] srcExtent Source data extent + * \param [in] rowAlignment Row alignment, in bytes */ void copyBufferToImage( const Rc& dstImage, @@ -356,7 +357,7 @@ namespace dxvk { VkExtent3D dstExtent, const Rc& srcBuffer, VkDeviceSize srcOffset, - VkExtent2D srcExtent); + VkDeviceSize rowAlignment); /** * \brief Copies data from one image to another @@ -1061,6 +1062,18 @@ namespace dxvk { const VkImageBlit& region, VkFilter filter); + template + void copyImageBufferData( + DxvkCmdBuffer cmd, + const Rc& image, + const VkImageSubresourceLayers& imageSubresource, + VkOffset3D imageOffset, + VkExtent3D imageExtent, + VkImageLayout imageLayout, + const DxvkBufferSliceHandle& bufferSlice, + VkDeviceSize bufferRowAlignment, + VkDeviceSize bufferSliceAlignment); + void clearImageViewFb( const Rc& imageView, VkOffset3D offset,