From f25b3c8b320866bc8d5d21b321eb2f6f8051ef4b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 20 Jan 2018 21:42:11 +0100 Subject: [PATCH] [d3d11] Use EmitCs for resource updates --- src/d3d11/d3d11_context.cpp | 61 +++++++++++++++++++++++++++------ src/d3d11/d3d11_context_imm.cpp | 59 ++++++++++++++++++++++--------- src/dxvk/dxvk_buffer.h | 12 +++++++ src/dxvk/dxvk_context.cpp | 52 +++++++--------------------- src/dxvk/dxvk_context.h | 6 ++-- src/dxvk/dxvk_util.cpp | 34 ++++++++++++++++++ src/dxvk/dxvk_util.h | 18 ++++++++++ 7 files changed, 173 insertions(+), 69 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 260e1416..adab2440 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -547,8 +547,8 @@ namespace dxvk { const auto bufferResource = static_cast(pDstResource); const auto bufferSlice = bufferResource->GetBufferSlice(); - VkDeviceSize offset = 0; - VkDeviceSize size = bufferSlice.length(); + VkDeviceSize offset = bufferSlice.offset(); + VkDeviceSize size = bufferSlice.length(); if (pDstBox != nullptr) { offset = pDstBox->left; @@ -565,13 +565,26 @@ namespace dxvk { if (((size == bufferSlice.length()) && (bufferSlice.buffer()->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) { - m_context->invalidateBuffer(bufferSlice.buffer()); - std::memcpy(bufferSlice.mapPtr(0), pSrcData, size); + auto physicalSlice = bufferSlice.buffer()->allocPhysicalSlice(); + std::memcpy(physicalSlice.mapPtr(0), pSrcData, size); + + EmitCs([ + cDstBuffer = bufferSlice.buffer(), + cPhysicalSlice = std::move(physicalSlice) + ] (DxvkContext* ctx) { + ctx->invalidateBuffer(cDstBuffer, cPhysicalSlice); + }); } else { - m_context->updateBuffer( - bufferSlice.buffer(), - bufferSlice.offset() + offset, - size, pSrcData); + EmitCs([ + cDataBuffer = Rc(new DxvkDataBuffer(pSrcData, size)), + cBufferSlice = bufferSlice.subSlice(offset, size) + ] (DxvkContext* ctx) { + ctx->updateBuffer( + cBufferSlice.buffer(), + cBufferSlice.offset(), + cBufferSlice.length(), + cDataBuffer->data()); + }); } } else { const D3D11TextureInfo* textureInfo @@ -604,10 +617,36 @@ namespace dxvk { subresource.mipLevel, subresource.arrayLayer, 1 }; - m_context->updateImage( - textureInfo->image, layers, - offset, extent, pSrcData, + auto formatInfo = imageFormatInfo( + textureInfo->image->info().format); + + const VkExtent3D regionExtent = util::computeBlockCount(extent, formatInfo->blockSize); + + const VkDeviceSize bytesPerRow = regionExtent.width * formatInfo->elementSize; + const VkDeviceSize bytesPerLayer = regionExtent.height * bytesPerRow; + const VkDeviceSize bytesTotal = regionExtent.depth * bytesPerLayer; + + Rc imageDataBuffer = new DxvkDataBuffer(bytesTotal); + + util::packImageData( + reinterpret_cast(imageDataBuffer->data()), + reinterpret_cast(pSrcData), + regionExtent, formatInfo->elementSize, SrcRowPitch, SrcDepthPitch); + + EmitCs([ + cDstImage = textureInfo->image, + cDstLayers = layers, + cDstOffset = offset, + cDstExtent = extent, + cSrcData = std::move(imageDataBuffer), + cSrcBytesPerRow = bytesPerRow, + cSrcBytesPerLayer = bytesPerLayer + ] (DxvkContext* ctx) { + ctx->updateImage(cDstImage, cDstLayers, + cDstOffset, cDstExtent, cSrcData->data(), + cSrcBytesPerRow, cSrcBytesPerLayer); + }); } } diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index 506f98f7..80d77577 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -86,7 +86,7 @@ namespace dxvk { } if (pMappedResource == nullptr) - return S_OK; + return S_FALSE; if (buffer->isInUse()) { // Don't wait if the application tells us not to @@ -98,7 +98,7 @@ namespace dxvk { // be preserved. The No Overwrite mode does not require any // sort of synchronization, but should be used with care. if (MapType == D3D11_MAP_WRITE_DISCARD) { - m_context->invalidateBuffer(buffer); + m_context->invalidateBuffer(buffer, buffer->allocPhysicalSlice()); } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) { this->Flush(); this->Synchronize(); @@ -142,30 +142,51 @@ namespace dxvk { levelExtent.depth / formatInfo->blockSize.depth }; // When using any map mode which requires the image contents - // to be preserved, copy image contents into the buffer. + // to be preserved, copy the image's contents into the buffer. if (MapType != D3D11_MAP_WRITE_DISCARD) { const VkImageSubresourceLayers subresourceLayers = { textureInfo->mappedSubresource.aspectMask, textureInfo->mappedSubresource.mipLevel, textureInfo->mappedSubresource.arrayLayer, 1 }; - m_context->copyImageToBuffer( - textureInfo->imageBuffer, 0, { 0u, 0u }, - textureInfo->image, subresourceLayers, - VkOffset3D { 0, 0, 0 }, levelExtent); + EmitCs([ + cImageBuffer = textureInfo->imageBuffer, + cImage = textureInfo->image, + cSubresources = subresourceLayers, + cLevelExtent = levelExtent + ] (DxvkContext* ctx) { + ctx->copyImageToBuffer( + cImageBuffer, 0, { 0u, 0u }, + cImage, cSubresources, + VkOffset3D { 0, 0, 0 }, + cLevelExtent); + }); } - if (textureInfo->imageBuffer->isInUse()) { - if (MapType == D3D11_MAP_WRITE_DISCARD) { - m_context->invalidateBuffer(textureInfo->imageBuffer); - } else { + DxvkPhysicalBufferSlice physicalSlice; + + if (MapType == D3D11_MAP_WRITE_DISCARD) { + physicalSlice = textureInfo->imageBuffer->allocPhysicalSlice(); + + EmitCs([ + cImageBuffer = textureInfo->imageBuffer, + cPhysicalSlice = physicalSlice + ] (DxvkContext* ctx) { + ctx->invalidateBuffer(cImageBuffer, cPhysicalSlice); + }); + } else { + // TODO sync with CS thread here + + if (textureInfo->image->isInUse()) { this->Flush(); this->Synchronize(); } + + physicalSlice = textureInfo->imageBuffer->slice(); } // Set up map pointer. Data is tightly packed within the mapped buffer. - pMappedResource->pData = textureInfo->imageBuffer->mapPtr(0); + pMappedResource->pData = physicalSlice.mapPtr(0); pMappedResource->RowPitch = formatInfo->elementSize * blockCount.width; pMappedResource->DepthPitch = formatInfo->elementSize * blockCount.width * blockCount.height; return S_OK; @@ -193,10 +214,16 @@ namespace dxvk { textureInfo->mappedSubresource.mipLevel, textureInfo->mappedSubresource.arrayLayer, 1 }; - m_context->copyBufferToImage( - textureInfo->image, subresourceLayers, - VkOffset3D { 0, 0, 0 }, levelExtent, - textureInfo->imageBuffer, 0, { 0u, 0u }); + EmitCs([ + cSrcBuffer = textureInfo->imageBuffer, + cDstImage = textureInfo->image, + cDstLayers = subresourceLayers, + cDstLevelExtent = levelExtent + ] (DxvkContext* ctx) { + ctx->copyBufferToImage(cDstImage, cDstLayers, + VkOffset3D { 0, 0, 0 }, cDstLevelExtent, + cSrcBuffer, 0, { 0u, 0u }); + }); } } diff --git a/src/dxvk/dxvk_buffer.h b/src/dxvk/dxvk_buffer.h index 40b9b983..9e892387 100644 --- a/src/dxvk/dxvk_buffer.h +++ b/src/dxvk/dxvk_buffer.h @@ -262,6 +262,18 @@ namespace dxvk { return m_buffer->info(); } + /** + * \brief Buffer sub slice + * + * Takes a sub slice from this slice. + * \param [in] offset Sub slice offset + * \param [in] length Sub slice length + * \returns The sub slice object + */ + DxvkBufferSlice subSlice(VkDeviceSize offset, VkDeviceSize length) const { + return DxvkBufferSlice(m_buffer, offset, length); + } + /** * \brief Checks whether the slice is valid * diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 0929d3bb..202a4ff2 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -729,9 +729,11 @@ namespace dxvk { } - void DxvkContext::invalidateBuffer(const Rc& buffer) { + void DxvkContext::invalidateBuffer( + const Rc& buffer, + const DxvkPhysicalBufferSlice& slice) { // Allocate new backing resource - buffer->rename(buffer->allocPhysicalSlice()); + buffer->rename(slice); // We also need to update all bindings that the buffer // may be bound to either directly or through views. @@ -884,53 +886,23 @@ namespace dxvk { const DxvkFormatInfo* formatInfo = imageFormatInfo(image->info().format); - VkExtent3D elementCount = imageExtent; - elementCount.depth *= subresources.layerCount; - // Align image extent to a full block. This is necessary in // case the image size is not a multiple of the block size. - elementCount.width += formatInfo->blockSize.width - 1; - elementCount.height += formatInfo->blockSize.height - 1; - elementCount.depth += formatInfo->blockSize.depth - 1; - - elementCount.width /= formatInfo->blockSize.width; - elementCount.height /= formatInfo->blockSize.height; - elementCount.depth /= formatInfo->blockSize.depth; - - VkDeviceSize bytesPerRow = elementCount.width * formatInfo->elementSize; - VkDeviceSize bytesPerLayer = elementCount.height * bytesPerRow; - VkDeviceSize bytesTotal = elementCount.depth * bytesPerLayer; + VkExtent3D elementCount = util::computeBlockCount( + imageExtent, formatInfo->blockSize); + elementCount.depth *= subresources.layerCount; // Allocate staging buffer memory for the image data. The // pixels or blocks will be tightly packed within the buffer. - DxvkStagingBufferSlice slice = m_cmd->stagedAlloc(bytesTotal); + const DxvkStagingBufferSlice slice = m_cmd->stagedAlloc( + formatInfo->elementSize * util::flattenImageExtent(elementCount)); auto dstData = reinterpret_cast(slice.mapPtr); auto srcData = reinterpret_cast(data); - // If the application provides tightly packed data as well, - // we can minimize the number of memcpy calls in order to - // improve performance. - bool useDirectCopy = true; - - useDirectCopy &= (pitchPerLayer == bytesPerLayer) || (elementCount.depth == 1); - useDirectCopy &= (pitchPerRow == bytesPerRow) || (elementCount.height == 1); - - if (useDirectCopy) { - std::memcpy(dstData, srcData, bytesTotal); - } else { - for (uint32_t i = 0; i < elementCount.depth; i++) { - for (uint32_t j = 0; j < elementCount.height; j++) { - std::memcpy( - dstData + j * bytesPerRow, - srcData + j * pitchPerRow, - bytesPerRow); - } - - srcData += pitchPerLayer; - dstData += bytesPerLayer; - } - } + util::packImageData(dstData, srcData, + elementCount, formatInfo->elementSize, + pitchPerRow, pitchPerLayer); // Prepare the image layout. If the given extent covers // the entire image, we may discard its previous contents. diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index d188d616..32fa541f 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -352,7 +352,7 @@ namespace dxvk { /** * \brief Invalidates a buffer's contents * - * Discards a buffer's contents by allocating a new + * Discards a buffer's contents by replacing the * backing resource. This allows the host to access * the buffer while the GPU is still accessing the * original backing resource. @@ -360,9 +360,11 @@ namespace dxvk { * \warning If the buffer is used by another context, * invalidating it will result in undefined behaviour. * \param [in] buffer The buffer to invalidate + * \param [in] slice New physical buffer slice */ void invalidateBuffer( - const Rc& buffer); + const Rc& buffer, + const DxvkPhysicalBufferSlice& slice); /** * \brief Resolves a multisampled image resource diff --git a/src/dxvk/dxvk_util.cpp b/src/dxvk/dxvk_util.cpp index e18b20c2..e6c7f204 100644 --- a/src/dxvk/dxvk_util.cpp +++ b/src/dxvk/dxvk_util.cpp @@ -1,3 +1,5 @@ +#include + #include "dxvk_util.h" namespace dxvk::util { @@ -34,4 +36,36 @@ namespace dxvk::util { return mipCnt; } + + void packImageData( + char* dstData, + const char* srcData, + VkExtent3D blockCount, + VkDeviceSize blockSize, + VkDeviceSize pitchPerRow, + VkDeviceSize pitchPerLayer) { + const VkDeviceSize bytesPerRow = blockCount.width * blockSize; + const VkDeviceSize bytesPerLayer = blockCount.height * bytesPerRow; + const VkDeviceSize bytesTotal = blockCount.depth * bytesPerLayer; + + const bool directCopy = (bytesPerRow == pitchPerRow ) || (blockCount.height == 1) + && (bytesPerLayer == pitchPerLayer) || (blockCount.depth == 1); + + if (directCopy) { + std::memcpy(dstData, srcData, bytesTotal); + } else { + for (uint32_t i = 0; i < blockCount.depth; i++) { + for (uint32_t j = 0; j < blockCount.height; j++) { + std::memcpy( + dstData + j * bytesPerRow, + srcData + j * pitchPerRow, + bytesPerRow); + } + + srcData += pitchPerLayer; + dstData += bytesPerLayer; + } + } + } + } diff --git a/src/dxvk/dxvk_util.h b/src/dxvk/dxvk_util.h index 58b7bb71..ed0221e1 100644 --- a/src/dxvk/dxvk_util.h +++ b/src/dxvk/dxvk_util.h @@ -21,6 +21,24 @@ namespace dxvk::util { */ uint32_t computeMipLevelCount(VkExtent3D imageSize); + /** + * \brief Writes tightly packed image data to a buffer + * + * \param [in] dstData Destination buffer pointer + * \param [in] srcData Pointer to source data + * \param [in] blockCount Number of blocks to copy + * \param [in] blockSize Number of bytes per block + * \param [in] pitchPerRow Number of bytes between rows + * \param [in] pitchPerLayer Number of bytes between layers + */ + void packImageData( + char* dstData, + const char* srcData, + VkExtent3D blockCount, + VkDeviceSize blockSize, + VkDeviceSize pitchPerRow, + VkDeviceSize pitchPerLayer); + /** * \brief Computes block count for compressed images *