mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-20 19:54:19 +01:00
[dxvk] Implemented buffer renaming
This commit is contained in:
parent
d9f38a7f42
commit
d3b2174180
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user