From ba9d6701574a467ad57f0531c8e3a82dacab67c2 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 23 Jun 2021 16:02:13 +0200 Subject: [PATCH] [dxvk] Introduce copyPackedBufferImage --- src/dxvk/dxvk_context.cpp | 165 ++++++++++++++++++++++++++++++++++++++ src/dxvk/dxvk_context.h | 28 +++++++ 2 files changed, 193 insertions(+) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index c26f6d363..b59bfe3c4 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -1064,6 +1064,171 @@ namespace dxvk { } + void DxvkContext::copyPackedBufferImage( + const Rc& dstBuffer, + VkDeviceSize dstBufferOffset, + VkOffset3D dstOffset, + VkExtent3D dstSize, + const Rc& srcBuffer, + VkDeviceSize srcBufferOffset, + VkOffset3D srcOffset, + VkExtent3D srcSize, + VkExtent3D extent, + VkDeviceSize elementSize) { + this->spillRenderPass(true); + this->unbindComputePipeline(); + + auto dstBufferSlice = dstBuffer->getSliceHandle(dstBufferOffset, elementSize * util::flattenImageExtent(dstSize)); + auto srcBufferSlice = srcBuffer->getSliceHandle(srcBufferOffset, elementSize * util::flattenImageExtent(srcSize)); + + if (m_execBarriers.isBufferDirty(dstBufferSlice, DxvkAccess::Write) + || m_execBarriers.isBufferDirty(srcBufferSlice, DxvkAccess::Read)) + m_execBarriers.recordCommands(m_cmd); + + // We'll use texel buffer views with an appropriately + // sized integer format to perform the copy + VkFormat format = VK_FORMAT_UNDEFINED; + + switch (elementSize) { + case 1: format = VK_FORMAT_R8_UINT; break; + case 2: format = VK_FORMAT_R16_UINT; break; + case 4: format = VK_FORMAT_R32_UINT; break; + case 8: format = VK_FORMAT_R32G32_UINT; break; + case 12: format = VK_FORMAT_R32G32B32_UINT; break; + case 16: format = VK_FORMAT_R32G32B32A32_UINT; break; + } + + if (!format) { + Logger::err(str::format("DxvkContext: copyPackedBufferImage: Unsupported element size ", elementSize)); + return; + } + + DxvkBufferViewCreateInfo viewInfo; + viewInfo.format = format; + viewInfo.rangeOffset = dstBufferOffset; + viewInfo.rangeLength = dstBufferSlice.length; + Rc dstView = m_device->createBufferView(dstBuffer, viewInfo); + + viewInfo.rangeOffset = srcBufferOffset; + viewInfo.rangeLength = srcBufferSlice.length; + Rc srcView; + + if (srcBuffer == dstBuffer + && srcBufferSlice.offset < dstBufferSlice.offset + dstBufferSlice.length + && srcBufferSlice.offset + srcBufferSlice.length > dstBufferSlice.offset) { + // Create temporary copy in case of overlapping regions + DxvkBufferCreateInfo bufferInfo; + bufferInfo.size = srcBufferSlice.length; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT + | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + bufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT + | VK_ACCESS_SHADER_READ_BIT; + Rc tmpBuffer = m_device->createBuffer(bufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + auto tmpBufferSlice = tmpBuffer->getSliceHandle(); + + VkBufferCopy copyRegion; + copyRegion.srcOffset = srcBufferSlice.offset; + copyRegion.dstOffset = tmpBufferSlice.offset; + copyRegion.size = tmpBufferSlice.length; + + m_cmd->cmdCopyBuffer(DxvkCmdBuffer::ExecBuffer, + srcBufferSlice.handle, tmpBufferSlice.handle, + 1, ©Region); + + emitMemoryBarrier(0, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT); + + viewInfo.rangeOffset = 0; + srcView = m_device->createBufferView(tmpBuffer, viewInfo); + + m_cmd->trackResource(tmpBuffer); + } else { + srcView = m_device->createBufferView(srcBuffer, viewInfo); + } + + auto pipeInfo = m_common->metaCopy().getCopyBufferImagePipeline(); + VkDescriptorSet descriptorSet = allocateDescriptorSet(pipeInfo.dsetLayout); + + std::array descriptorWrites; + + std::array, 2> descriptorInfos = {{ + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, dstView->handle() }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, srcView->handle() }, + }}; + + for (uint32_t i = 0; i < descriptorWrites.size(); i++) { + auto write = &descriptorWrites[i]; + auto info = &descriptorInfos[i]; + + write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write->pNext = nullptr; + write->dstSet = descriptorSet; + write->dstBinding = i; + write->dstArrayElement = 0; + write->descriptorCount = 1; + write->descriptorType = info->first; + write->pImageInfo = nullptr; + write->pBufferInfo = nullptr; + write->pTexelBufferView = &info->second; + } + + m_cmd->updateDescriptorSets(descriptorWrites.size(), descriptorWrites.data()); + + DxvkCopyBufferImageArgs args = { }; + args.dstOffset = dstOffset; + args.srcOffset = srcOffset; + args.extent = extent; + args.dstSize = { dstSize.width, dstSize.height }; + args.srcSize = { srcSize.width, srcSize.height }; + + m_cmd->cmdBindPipeline( + VK_PIPELINE_BIND_POINT_COMPUTE, + pipeInfo.pipeHandle); + + m_cmd->cmdBindDescriptorSet( + VK_PIPELINE_BIND_POINT_COMPUTE, + pipeInfo.pipeLayout, descriptorSet, + 0, nullptr); + + m_cmd->cmdPushConstants( + pipeInfo.pipeLayout, + VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(args), &args); + + m_cmd->cmdDispatch( + (extent.width + 7) / 8, + (extent.height + 7) / 8, + extent.depth); + + m_execBarriers.accessBuffer( + dstView->getSliceHandle(), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + dstBuffer->info().stages, + dstBuffer->info().access); + + m_execBarriers.accessBuffer( + srcView->getSliceHandle(), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + srcBuffer->info().stages, + srcBuffer->info().access); + + // Track all involved resources + m_cmd->trackResource(dstBuffer); + m_cmd->trackResource(srcBuffer); + + m_cmd->trackResource(dstView); + m_cmd->trackResource(srcView); + } + + void DxvkContext::copyPackedBufferToDepthStencilImage( const Rc& dstImage, VkImageSubresourceLayers dstSubresource, diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 21456ae17..f17e7dcfd 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -444,6 +444,34 @@ namespace dxvk { VkExtent2D srcExtent, VkFormat format); + /** + * \brief Copies image data stored in a linear buffer to another + * + * The source and destination regions may overlap, in which case + * a temporary copy of the source buffer will be created. + * \param [in] dstBuffer Destination buffer + * \param [in] dstBufferOffset Destination subresource offset + * \param [in] dstOffset Destination image offset + * \param [in] dstSize Total size of the destination image + * \param [in] srcBuffer Source buffer + * \param [in] srcBufferOffset Source subresource offset + * \param [in] srcOffset Source image offset + * \param [in] srcSize Total size of the source image + * \param [in] extent Number of pixels to copy + * \param [in] elementSize Pixel size, in bytes + */ + void copyPackedBufferImage( + const Rc& dstBuffer, + VkDeviceSize dstBufferOffset, + VkOffset3D dstOffset, + VkExtent3D dstSize, + const Rc& srcBuffer, + VkDeviceSize srcBufferOffset, + VkOffset3D srcOffset, + VkExtent3D srcSize, + VkExtent3D extent, + VkDeviceSize elementSize); + /** * \brief Unpacks buffer data to a depth-stencil image *