mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-07 07:54:15 +01:00
[d3d11] Use EmitCs for buffer mapping
This commit is contained in:
parent
f25b3c8b32
commit
aaf7b05625
@ -11,7 +11,8 @@ namespace dxvk {
|
|||||||
const D3D11_BUFFER_DESC* pDesc)
|
const D3D11_BUFFER_DESC* pDesc)
|
||||||
: m_device (pDevice),
|
: m_device (pDevice),
|
||||||
m_desc (*pDesc),
|
m_desc (*pDesc),
|
||||||
m_buffer (CreateBuffer(pDesc)) {
|
m_buffer (CreateBuffer(pDesc)),
|
||||||
|
m_bufferInfo{ m_buffer->slice() } {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,17 @@ namespace dxvk {
|
|||||||
class D3D11DeviceContext;
|
class D3D11DeviceContext;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Common buffer info
|
||||||
|
*
|
||||||
|
* Stores where the buffer was last
|
||||||
|
* mapped on the immediate context.
|
||||||
|
*/
|
||||||
|
struct D3D11BufferInfo {
|
||||||
|
DxvkPhysicalBufferSlice mappedSlice;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class D3D11Buffer : public D3D11DeviceChild<ID3D11Buffer> {
|
class D3D11Buffer : public D3D11DeviceChild<ID3D11Buffer> {
|
||||||
static constexpr VkDeviceSize BufferSliceAlignment = 64;
|
static constexpr VkDeviceSize BufferSliceAlignment = 64;
|
||||||
public:
|
public:
|
||||||
@ -49,12 +60,17 @@ namespace dxvk {
|
|||||||
return DxvkBufferSlice(m_buffer, offset, m_buffer->info().size - offset);
|
return DxvkBufferSlice(m_buffer, offset, m_buffer->info().size - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
D3D11BufferInfo* GetBufferInfo() {
|
||||||
|
return &m_bufferInfo;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
const Com<D3D11Device> m_device;
|
const Com<D3D11Device> m_device;
|
||||||
const D3D11_BUFFER_DESC m_desc;
|
const D3D11_BUFFER_DESC m_desc;
|
||||||
|
|
||||||
Rc<DxvkBuffer> m_buffer;
|
Rc<DxvkBuffer> m_buffer;
|
||||||
|
D3D11BufferInfo m_bufferInfo;
|
||||||
|
|
||||||
Rc<DxvkBuffer> CreateBuffer(
|
Rc<DxvkBuffer> CreateBuffer(
|
||||||
const D3D11_BUFFER_DESC* pDesc) const;
|
const D3D11_BUFFER_DESC* pDesc) const;
|
||||||
|
@ -77,8 +77,8 @@ namespace dxvk {
|
|||||||
pResource->GetType(&resourceDim);
|
pResource->GetType(&resourceDim);
|
||||||
|
|
||||||
if (resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) {
|
if (resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) {
|
||||||
const D3D11Buffer* resource = static_cast<D3D11Buffer*>(pResource);
|
D3D11Buffer* resource = static_cast<D3D11Buffer*>(pResource);
|
||||||
const Rc<DxvkBuffer> buffer = resource->GetBufferSlice().buffer();
|
Rc<DxvkBuffer> buffer = resource->GetBufferSlice().buffer();
|
||||||
|
|
||||||
if (!(buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
|
if (!(buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
|
||||||
Logger::err("D3D11: Cannot map a device-local buffer");
|
Logger::err("D3D11: Cannot map a device-local buffer");
|
||||||
@ -88,26 +88,42 @@ namespace dxvk {
|
|||||||
if (pMappedResource == nullptr)
|
if (pMappedResource == nullptr)
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
|
|
||||||
if (buffer->isInUse()) {
|
DxvkPhysicalBufferSlice physicalSlice;
|
||||||
// Don't wait if the application tells us not to
|
|
||||||
if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)
|
|
||||||
return DXGI_ERROR_WAS_STILL_DRAWING;
|
|
||||||
|
|
||||||
// Invalidate the buffer in order to avoid synchronization
|
if (MapType == D3D11_MAP_WRITE_DISCARD) {
|
||||||
// if the application does not need the buffer contents to
|
// Allocate a new backing slice for the buffer and set
|
||||||
// be preserved. The No Overwrite mode does not require any
|
// it as the 'new' mapped slice. This assumes that the
|
||||||
// sort of synchronization, but should be used with care.
|
// only way to invalidate a buffer is by mapping it.
|
||||||
if (MapType == D3D11_MAP_WRITE_DISCARD) {
|
physicalSlice = buffer->allocPhysicalSlice();
|
||||||
m_context->invalidateBuffer(buffer, buffer->allocPhysicalSlice());
|
resource->GetBufferInfo()->mappedSlice = physicalSlice;
|
||||||
} else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {
|
|
||||||
this->Flush();
|
EmitCs([
|
||||||
this->Synchronize();
|
cBuffer = buffer,
|
||||||
|
cPhysicalSlice = physicalSlice
|
||||||
|
] (DxvkContext* ctx) {
|
||||||
|
ctx->invalidateBuffer(cBuffer, cPhysicalSlice);
|
||||||
|
});
|
||||||
|
} else if (MapType == D3D11_MAP_WRITE_NO_OVERWRITE) {
|
||||||
|
// Use map pointer from previous map operation. This
|
||||||
|
// way we don't have to synchronize with the CS thread.
|
||||||
|
physicalSlice = resource->GetBufferInfo()->mappedSlice;
|
||||||
|
} else {
|
||||||
|
// Synchronize with CS thread so that we know whether
|
||||||
|
// the buffer is currently in use by the GPU or not
|
||||||
|
// TODO implement
|
||||||
|
|
||||||
|
if (buffer->isInUse()) {
|
||||||
|
if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)
|
||||||
|
return DXGI_ERROR_WAS_STILL_DRAWING;
|
||||||
|
|
||||||
|
Flush();
|
||||||
|
Synchronize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pMappedResource->pData = buffer->mapPtr(0);
|
pMappedResource->pData = physicalSlice.mapPtr(0);
|
||||||
pMappedResource->RowPitch = buffer->info().size;
|
pMappedResource->RowPitch = physicalSlice.length();
|
||||||
pMappedResource->DepthPitch = buffer->info().size;
|
pMappedResource->DepthPitch = physicalSlice.length();
|
||||||
return S_OK;
|
return S_OK;
|
||||||
} else {
|
} else {
|
||||||
// Mapping an image is sadly not as simple as mapping a buffer
|
// Mapping an image is sadly not as simple as mapping a buffer
|
||||||
@ -136,14 +152,23 @@ namespace dxvk {
|
|||||||
const VkExtent3D levelExtent = textureInfo->image
|
const VkExtent3D levelExtent = textureInfo->image
|
||||||
->mipLevelExtent(textureInfo->mappedSubresource.mipLevel);
|
->mipLevelExtent(textureInfo->mappedSubresource.mipLevel);
|
||||||
|
|
||||||
const VkExtent3D blockCount = {
|
const VkExtent3D blockCount = util::computeBlockCount(
|
||||||
levelExtent.width / formatInfo->blockSize.width,
|
levelExtent, formatInfo->blockSize);
|
||||||
levelExtent.height / formatInfo->blockSize.height,
|
|
||||||
levelExtent.depth / formatInfo->blockSize.depth };
|
DxvkPhysicalBufferSlice physicalSlice;
|
||||||
|
|
||||||
// When using any map mode which requires the image contents
|
// When using any map mode which requires the image contents
|
||||||
// to be preserved, copy the image's contents into the buffer.
|
// to be preserved, copy the image's contents into the buffer.
|
||||||
if (MapType != D3D11_MAP_WRITE_DISCARD) {
|
if (MapType == D3D11_MAP_WRITE_DISCARD) {
|
||||||
|
physicalSlice = textureInfo->imageBuffer->allocPhysicalSlice();
|
||||||
|
|
||||||
|
EmitCs([
|
||||||
|
cImageBuffer = textureInfo->imageBuffer,
|
||||||
|
cPhysicalSlice = physicalSlice
|
||||||
|
] (DxvkContext* ctx) {
|
||||||
|
ctx->invalidateBuffer(cImageBuffer, cPhysicalSlice);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
const VkImageSubresourceLayers subresourceLayers = {
|
const VkImageSubresourceLayers subresourceLayers = {
|
||||||
textureInfo->mappedSubresource.aspectMask,
|
textureInfo->mappedSubresource.aspectMask,
|
||||||
textureInfo->mappedSubresource.mipLevel,
|
textureInfo->mappedSubresource.mipLevel,
|
||||||
@ -156,31 +181,13 @@ namespace dxvk {
|
|||||||
cLevelExtent = levelExtent
|
cLevelExtent = levelExtent
|
||||||
] (DxvkContext* ctx) {
|
] (DxvkContext* ctx) {
|
||||||
ctx->copyImageToBuffer(
|
ctx->copyImageToBuffer(
|
||||||
cImageBuffer, 0, { 0u, 0u },
|
cImageBuffer, 0, VkExtent2D { 0u, 0u },
|
||||||
cImage, cSubresources,
|
cImage, cSubresources, VkOffset3D { 0, 0, 0 },
|
||||||
VkOffset3D { 0, 0, 0 },
|
|
||||||
cLevelExtent);
|
cLevelExtent);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
DxvkPhysicalBufferSlice physicalSlice;
|
Flush();
|
||||||
|
Synchronize();
|
||||||
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();
|
physicalSlice = textureInfo->imageBuffer->slice();
|
||||||
}
|
}
|
||||||
@ -229,7 +236,10 @@ namespace dxvk {
|
|||||||
|
|
||||||
|
|
||||||
void D3D11ImmediateContext::Synchronize() {
|
void D3D11ImmediateContext::Synchronize() {
|
||||||
// TODO sync with CS thread
|
// FIXME waiting until the device finished executing *all*
|
||||||
|
// pending commands is too pessimistic. Instead we should
|
||||||
|
// wait for individual command submissions to complete.
|
||||||
|
// This will require changes in the DxvkDevice class.
|
||||||
m_device->waitForIdle();
|
m_device->waitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,16 @@ namespace dxvk {
|
|||||||
DxvkCsChunk();
|
DxvkCsChunk();
|
||||||
~DxvkCsChunk();
|
~DxvkCsChunk();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Number of commands recorded to the chunk
|
||||||
|
*
|
||||||
|
* Can be used to check whether the chunk needs to
|
||||||
|
* be dispatched or just to keep track of statistics.
|
||||||
|
*/
|
||||||
|
size_t commandCount() const {
|
||||||
|
return m_commandCount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Tries to add a command to the chunk
|
* \brief Tries to add a command to the chunk
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user