[dxvk] Implemented buffer renaming

This commit is contained in:
Philip Rebohle 2017-12-16 13:21:11 +01:00
parent d9f38a7f42
commit d3b2174180
7 changed files with 189 additions and 72 deletions

View File

@ -78,47 +78,27 @@ namespace dxvk {
if (pMappedSubresource == nullptr)
return S_OK;
if (!buffer->isInUse()) {
// Simple case: The buffer is currently not being
// used by the device, we can return the pointer.
pMappedSubresource->pData = buffer->mapPtr(0);
pMappedSubresource->RowPitch = buffer->info().size;
pMappedSubresource->DepthPitch = buffer->info().size;
return S_OK;
} else {
if (buffer->isInUse()) {
// Don't wait if the application tells us not to
if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)
return DXGI_ERROR_WAS_STILL_DRAWING;
// TODO optimize this. In order to properly cover common use cases
// like frequent constant buffer updates, we must implement buffer
// renaming techniques. The current approach is inefficient as it
// leads to a lot of Flush() and Synchronize() calls.
//
// Possible solution:
// (1) Create buffers with a significantly larger size if they
// can be mapped by the host for writing. If mapping the
// buffer would stall on D3D11_MAP_WRITE_DISCARD, map the
// next slice. on D3D11_MAP_WRITE_NO_OVERWRITE, return the
// current slice. If the buffer is bound, update bindings.
// (2) If no more slices are available, create a new buffer.
// Limit the number of buffers to a small, fixed number.
// (3) If no more buffers are available, flush and synchronize.
// (4) When renaming the buffer internally, all active bindings
// need to be updated internally as well.
//
// In order to support deferred contexts, the immediate context
// must commit all changes to the initial buffer slice prior to
// executing a command list. When mapping on deferred contexts,
// the deferred context shall create local buffer objects.
pContext->Flush();
pContext->Synchronize();
pMappedSubresource->pData = buffer->mapPtr(0);
pMappedSubresource->RowPitch = buffer->info().size;
pMappedSubresource->DepthPitch = buffer->info().size;
return S_OK;
// Invalidate the buffer in order to avoid synchronization
// if the application does not need the buffer contents to
// 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) {
pContext->GetDXVKContext()->invalidateBuffer(m_buffer);
} else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {
pContext->Flush();
pContext->Synchronize();
}
}
pMappedSubresource->pData = buffer->mapPtr(0);
pMappedSubresource->RowPitch = buffer->info().size;
pMappedSubresource->DepthPitch = buffer->info().size;
return S_OK;
}

View File

@ -1,13 +1,14 @@
#include "dxvk_buffer.h"
#include "dxvk_device.h"
namespace dxvk {
DxvkBuffer::DxvkBuffer(
DxvkBufferResource::DxvkBufferResource(
const Rc<vk::DeviceFn>& vkd,
const DxvkBufferCreateInfo& createInfo,
DxvkMemoryAllocator& memAlloc,
VkMemoryPropertyFlags memFlags)
: m_vkd(vkd), m_info(createInfo) {
: m_vkd(vkd) {
VkBufferCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@ -34,12 +35,28 @@ namespace dxvk {
}
DxvkBuffer::~DxvkBuffer() {
DxvkBufferResource::~DxvkBufferResource() {
if (m_buffer != VK_NULL_HANDLE)
m_vkd->vkDestroyBuffer(m_vkd->device(), m_buffer, nullptr);
}
DxvkBuffer::DxvkBuffer(
DxvkDevice* device,
const DxvkBufferCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType)
: m_device (device),
m_info (createInfo),
m_memFlags(memoryType) {
this->allocateResource();
}
void DxvkBuffer::allocateResource() {
m_resource = m_device->allocBufferResource(m_info, m_memFlags);
}
DxvkBufferView::DxvkBufferView(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkBuffer>& buffer,

View File

@ -47,30 +47,54 @@ namespace dxvk {
/**
* \brief Buffer resource
* \brief Physical buffer resource
*
*
*/
class DxvkBufferResource : public DxvkResource {
public:
DxvkBufferResource(
const Rc<vk::DeviceFn>& vkd,
const DxvkBufferCreateInfo& createInfo,
DxvkMemoryAllocator& memAlloc,
VkMemoryPropertyFlags memFlags);
~DxvkBufferResource();
VkBuffer handle() const {
return m_buffer;
}
void* mapPtr(VkDeviceSize offset) const {
return m_memory.mapPtr(offset);
}
private:
Rc<vk::DeviceFn> m_vkd;
DxvkMemory m_memory;
VkBuffer m_buffer;
};
/**
* \brief Virtual buffer resource
*
* A simple buffer resource that stores linear,
* unformatted data. Can be accessed by the host
* if allocated on an appropriate memory type.
*/
class DxvkBuffer : public DxvkResource {
class DxvkBuffer : public RcObject {
public:
DxvkBuffer(
const Rc<vk::DeviceFn>& vkd,
DxvkDevice* device,
const DxvkBufferCreateInfo& createInfo,
DxvkMemoryAllocator& memAlloc,
VkMemoryPropertyFlags memFlags);
~DxvkBuffer();
/**
* \brief Buffer handle
* \returns Buffer handle
*/
VkBuffer handle() const {
return m_buffer;
}
VkMemoryPropertyFlags memoryType);
/**
* \brief Buffer properties
@ -80,6 +104,14 @@ namespace dxvk {
return m_info;
}
/**
* \brief Buffer handle
* \returns Buffer handle
*/
VkBuffer handle() const {
return m_resource->handle();;
}
/**
* \brief Map pointer
*
@ -90,15 +122,49 @@ namespace dxvk {
* \returns Pointer to mapped memory region
*/
void* mapPtr(VkDeviceSize offset) const {
return m_memory.mapPtr(offset);
return m_resource->mapPtr(offset);
}
/**
* \brief Checks whether the buffer is in use
*
* Returns \c true if the underlying buffer resource
* is in use. If it is, it should not be accessed by
* the host for reading or writing, but reallocating
* the buffer is a valid strategy to overcome this.
* \returns \c true if the buffer is in use
*/
bool isInUse() const {
return m_resource->isInUse();
}
/**
* \brief Underlying buffer resource
*
* Use this for lifetime tracking.
* \returns The resource object
*/
Rc<DxvkResource> resource() const {
return m_resource;
}
/**
* \brief Allocates new backing resource
*
* Replaces the underlying buffer and implicitly marks
* any buffer views using this resource as dirty. Do
* not call this directly as this is called implicitly
* by the context's \c invalidateBuffer method.
*/
void allocateResource();
private:
Rc<vk::DeviceFn> m_vkd;
DxvkBufferCreateInfo m_info;
DxvkMemory m_memory;
VkBuffer m_buffer = VK_NULL_HANDLE;
DxvkDevice* m_device;
DxvkBufferCreateInfo m_info;
VkMemoryPropertyFlags m_memFlags;
Rc<DxvkBufferResource> m_resource;
};

View File

@ -310,8 +310,8 @@ namespace dxvk {
m_barriers.recordCommands(m_cmd);
m_cmd->trackResource(dstBuffer);
m_cmd->trackResource(srcBuffer);
m_cmd->trackResource(dstBuffer->resource());
m_cmd->trackResource(srcBuffer->resource());
}
}
@ -368,6 +368,29 @@ namespace dxvk {
}
void DxvkContext::invalidateBuffer(const Rc<DxvkBuffer>& buffer) {
// Allocate new backing resource
buffer->allocateResource();
// We also need to update all bindings that the buffer
// may be bound to either directly or through views.
const VkBufferUsageFlags usage = buffer->info().usage;
if (usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT)
m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer);
if (usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers);
if (usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT))
m_flags.set(DxvkContextFlag::GpDirtyResources,
DxvkContextFlag::CpDirtyResources);
}
void DxvkContext::resolveImage(
const Rc<DxvkImage>& dstImage,
const VkImageSubresourceLayers& dstSubresources,
@ -471,7 +494,7 @@ namespace dxvk {
buffer->info().access);
m_barriers.recordCommands(m_cmd);
m_cmd->trackResource(buffer);
m_cmd->trackResource(buffer->resource());
}
}
@ -785,7 +808,6 @@ namespace dxvk {
gpState.rsDepthBiasSlope = m_state.rs.depthBiasSlope;
gpState.rsViewportCount = m_state.vp.viewportCount;
// TODO implement multisampling support properly
gpState.msSampleCount = m_state.om.framebuffer->sampleCount();
gpState.msSampleMask = m_state.ms.sampleMask;
gpState.msEnableAlphaToCoverage = m_state.ms.enableAlphaToCoverage;
@ -837,6 +859,8 @@ namespace dxvk {
auto layout = m_state.cp.pipeline->layout();
// TODO refcount used resources
m_cmd->bindResourceDescriptors(
VK_PIPELINE_BIND_POINT_COMPUTE,
layout->pipelineLayout(),
@ -904,7 +928,7 @@ namespace dxvk {
m_state.vi.indexBuffer.offset(),
m_state.vi.indexType);
m_cmd->trackResource(
m_state.vi.indexBuffer.buffer());
m_state.vi.indexBuffer.buffer()->resource());
}
}
}
@ -922,7 +946,7 @@ namespace dxvk {
if (handle != VK_NULL_HANDLE) {
m_cmd->cmdBindVertexBuffers(i, 1, &handle, &offset);
m_cmd->trackResource(vbo.buffer());
m_cmd->trackResource(vbo.buffer()->resource());
}
}
}

View File

@ -244,8 +244,23 @@ namespace dxvk {
* \param [in] subresources Image subresources
*/
void initImage(
const Rc<DxvkImage>& image,
const VkImageSubresourceRange& subresources);
const Rc<DxvkImage>& image,
const VkImageSubresourceRange& subresources);
/**
* \brief Invalidates a buffer's contents
*
* Discards a buffer's contents by allocating a new
* backing resource. This allows the host to access
* the buffer while the GPU is still accessing the
* original backing resource.
*
* \warning If the buffer is used by another context,
* invalidating it will result in undefined behaviour.
* \param [in] buffer The buffer to invalidate
*/
void invalidateBuffer(
const Rc<DxvkBuffer>& buffer);
/**
* \brief Resolves a multisampled image resource

View File

@ -29,6 +29,14 @@ namespace dxvk {
}
Rc<DxvkBufferResource> DxvkDevice::allocBufferResource(
const DxvkBufferCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType) {
return new DxvkBufferResource(m_vkd,
createInfo, *m_memory, memoryType);
}
Rc<DxvkStagingBuffer> DxvkDevice::allocStagingBuffer(VkDeviceSize size) {
// In case we need a standard-size staging buffer, try
// to recycle an old one that has been returned earlier
@ -102,9 +110,7 @@ namespace dxvk {
const DxvkBufferCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType) {
m_statCounters.increment(DxvkStat::ResBufferCreations, 1);
return new DxvkBuffer(m_vkd,
createInfo, *m_memory, memoryType);
return new DxvkBuffer(this, createInfo, memoryType);
}
@ -119,9 +125,7 @@ namespace dxvk {
const DxvkImageCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType) {
m_statCounters.increment(DxvkStat::ResImageCreations, 1);
return new DxvkImage(m_vkd,
createInfo, *m_memory, memoryType);
return new DxvkImage(m_vkd, createInfo, *m_memory, memoryType);
}

View File

@ -75,6 +75,17 @@ namespace dxvk {
return m_features;
}
/**
* \brief Allocates buffer resource
*
* \param [in] createInfo Buffer create info
* \param [in] memoryType Memory property flags
* \returns The buffer resource object
*/
Rc<DxvkBufferResource> allocBufferResource(
const DxvkBufferCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType);
/**
* \brief Allocates a staging buffer
*