diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index e93c6c534..1bcea1689 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -569,15 +569,18 @@ namespace dxvk { std::memcpy(mappedSr.pData, pSrcData, size); Unmap(pDstResource, 0); } else { + DxvkDataSlice dataSlice = AllocUpdateBufferSlice(size); + std::memcpy(dataSlice.ptr(), pSrcData, size); + EmitCs([ - cDataBuffer = Rc(new DxvkDataBuffer(pSrcData, size)), + cDataBuffer = std::move(dataSlice), cBufferSlice = bufferSlice.subSlice(offset, size) ] (DxvkContext* ctx) { ctx->updateBuffer( cBufferSlice.buffer(), cBufferSlice.offset(), cBufferSlice.length(), - cDataBuffer->data()); + cDataBuffer.ptr()); }); } } else { @@ -620,10 +623,10 @@ namespace dxvk { const VkDeviceSize bytesPerLayer = regionExtent.height * bytesPerRow; const VkDeviceSize bytesTotal = regionExtent.depth * bytesPerLayer; - Rc imageDataBuffer = new DxvkDataBuffer(bytesTotal); + DxvkDataSlice imageDataBuffer = AllocUpdateBufferSlice(bytesTotal); util::packImageData( - reinterpret_cast(imageDataBuffer->data()), + reinterpret_cast(imageDataBuffer.ptr()), reinterpret_cast(pSrcData), regionExtent, formatInfo->elementSize, SrcRowPitch, SrcDepthPitch); @@ -638,7 +641,7 @@ namespace dxvk { cSrcBytesPerLayer = bytesPerLayer ] (DxvkContext* ctx) { ctx->updateImage(cDstImage, cDstLayers, - cDstOffset, cDstExtent, cSrcData->data(), + cDstOffset, cDstExtent, cSrcData.ptr(), cSrcBytesPerRow, cSrcBytesPerLayer); }); } @@ -2113,4 +2116,27 @@ namespace dxvk { return m_device->createSampler(info); } + + DxvkDataSlice D3D11DeviceContext::AllocUpdateBufferSlice(size_t Size) { + constexpr size_t UpdateBufferSize = 4 * 1024 * 1024; + + if (Size >= UpdateBufferSize) { + Rc buffer = new DxvkDataBuffer(Size); + return buffer->alloc(Size); + } else { + if (m_updateBuffer == nullptr) + m_updateBuffer = new DxvkDataBuffer(Size); + + DxvkDataSlice slice = m_updateBuffer->alloc(Size); + + if (slice.ptr() == nullptr) { + m_updateBuffer = new DxvkDataBuffer(Size); + slice = m_updateBuffer->alloc(Size); + } + + return slice; + } + } + + } diff --git a/src/d3d11/d3d11_context.h b/src/d3d11/d3d11_context.h index 1247c656c..bbe522603 100644 --- a/src/d3d11/d3d11_context.h +++ b/src/d3d11/d3d11_context.h @@ -520,6 +520,7 @@ namespace dxvk { Rc m_device; Rc m_csChunk; Rc m_defaultSampler; + Rc m_updateBuffer; Com m_defaultBlendState; Com m_defaultDepthStencilState; @@ -565,6 +566,8 @@ namespace dxvk { Rc CreateDefaultSampler(); + DxvkDataSlice AllocUpdateBufferSlice(size_t Size); + template void EmitCs(Cmd&& command) { if (!m_csChunk->push(command)) { @@ -584,6 +587,7 @@ namespace dxvk { virtual void EmitCsChunk(Rc&& chunk) = 0; + }; } diff --git a/src/dxvk/dxvk_data.cpp b/src/dxvk/dxvk_data.cpp index eb407b8f4..ebcc06b1d 100644 --- a/src/dxvk/dxvk_data.cpp +++ b/src/dxvk/dxvk_data.cpp @@ -7,16 +7,18 @@ namespace dxvk { DxvkDataBuffer:: DxvkDataBuffer() { } DxvkDataBuffer::~DxvkDataBuffer() { } - DxvkDataBuffer::DxvkDataBuffer( - size_t size) { + DxvkDataBuffer::DxvkDataBuffer(size_t size) { m_data.resize(size); } - DxvkDataBuffer::DxvkDataBuffer( - const void* data, - size_t size) { - m_data.resize(size); - std::memcpy(m_data.data(), data, size); + + DxvkDataSlice DxvkDataBuffer::alloc(size_t n) { + const size_t offset = m_offset; + + if (offset + n <= m_data.size()) { + m_offset += align(n, CACHE_LINE_SIZE); + return DxvkDataSlice(this, offset, n); + } return DxvkDataSlice(); } } \ No newline at end of file diff --git a/src/dxvk/dxvk_data.h b/src/dxvk/dxvk_data.h index ee205d73b..097f10ad0 100644 --- a/src/dxvk/dxvk_data.h +++ b/src/dxvk/dxvk_data.h @@ -4,41 +4,78 @@ namespace dxvk { + class DxvkDataSlice; + /** * \brief Data buffer * - * Stores immutable data. Used for temporary - * copies of data that can be transferred to - * or from DXVK resources. + * Provides a fixed-size buffer with a linear memory + * allocator for arbitrary data. Can be used to copy + * data to or from resources. Note that allocations + * will be aligned to a cache line boundary. */ class DxvkDataBuffer : public RcObject { - + friend class DxvkDataSlice; public: DxvkDataBuffer(); - DxvkDataBuffer( - size_t size); - DxvkDataBuffer( - const void* data, - size_t size); + DxvkDataBuffer(size_t size); ~DxvkDataBuffer(); - size_t size() const { - return m_data.size(); - } - - void* data() { - return m_data.data(); - } - - const void* data() const { - return m_data.data(); - } + /** + * \brief Allocates a slice + * + * If the desired slice length is larger than the + * number of bytes left in the buffer, this will + * fail and the returned slice points to \c nullptr. + * \param [in] n Number of bytes to allocate + * \returns The slice, or an empty slice on failure + */ + DxvkDataSlice alloc(size_t n); private: std::vector m_data; + size_t m_offset = 0; }; + + /** + * \brief Data buffer slice + * + * A slice of a \ref DxvkDataBuffer which stores + * a strong reference to the backing buffer object. + */ + class DxvkDataSlice { + + public: + + DxvkDataSlice() { } + DxvkDataSlice( + const Rc& buffer, + size_t offset, + size_t length) + : m_buffer(buffer), + m_offset(offset), + m_length(length) { } + + void* ptr() const { + return m_buffer != nullptr + ? m_buffer->m_data.data() + m_offset + : nullptr; + } + + size_t offset() const { return m_offset; } + size_t length() const { return m_length; } + + private: + + Rc m_buffer; + size_t m_offset = 0; + size_t m_length = 0; + + }; + + } \ No newline at end of file diff --git a/src/util/util_math.h b/src/util/util_math.h index 5705bf7f0..e2ad44a59 100644 --- a/src/util/util_math.h +++ b/src/util/util_math.h @@ -2,6 +2,8 @@ namespace dxvk { + constexpr size_t CACHE_LINE_SIZE = 64; + template T clamp(T n, T lo, T hi) { if (n < lo) return lo;