[d3d11] Sub-allocate from larger update buffers for UpdateSubresources

Reduces the allocation overhead when applications frequently
call UpdateSubresources to update small buffers and textures.
This commit is contained in:
Philip Rebohle 2018-01-23 16:43:55 +01:00
parent 226afa96c9
commit e198bd2d55
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
5 changed files with 103 additions and 32 deletions

View File

@ -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<DxvkDataBuffer>(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<DxvkDataBuffer> imageDataBuffer = new DxvkDataBuffer(bytesTotal);
DxvkDataSlice imageDataBuffer = AllocUpdateBufferSlice(bytesTotal);
util::packImageData(
reinterpret_cast<char*>(imageDataBuffer->data()),
reinterpret_cast<char*>(imageDataBuffer.ptr()),
reinterpret_cast<const char*>(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<DxvkDataBuffer> 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;
}
}
}

View File

@ -520,6 +520,7 @@ namespace dxvk {
Rc<DxvkDevice> m_device;
Rc<DxvkCsChunk> m_csChunk;
Rc<DxvkSampler> m_defaultSampler;
Rc<DxvkDataBuffer> m_updateBuffer;
Com<D3D11BlendState> m_defaultBlendState;
Com<D3D11DepthStencilState> m_defaultDepthStencilState;
@ -565,6 +566,8 @@ namespace dxvk {
Rc<DxvkSampler> CreateDefaultSampler();
DxvkDataSlice AllocUpdateBufferSlice(size_t Size);
template<typename Cmd>
void EmitCs(Cmd&& command) {
if (!m_csChunk->push(command)) {
@ -584,6 +587,7 @@ namespace dxvk {
virtual void EmitCsChunk(Rc<DxvkCsChunk>&& chunk) = 0;
};
}

View File

@ -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();
}
}

View File

@ -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<char> 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<DxvkDataBuffer>& 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<DxvkDataBuffer> m_buffer;
size_t m_offset = 0;
size_t m_length = 0;
};
}

View File

@ -2,6 +2,8 @@
namespace dxvk {
constexpr size_t CACHE_LINE_SIZE = 64;
template<typename T>
T clamp(T n, T lo, T hi) {
if (n < lo) return lo;