mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-27 04:54:15 +01:00
[dxvk] Rework buffer slice allocation
Temporary solution that hits the allocator on every single invalidation, which isn't great but will do for now.
This commit is contained in:
parent
ec2f43e5e3
commit
14990dbb49
@ -9,63 +9,15 @@ namespace dxvk {
|
||||
DxvkBuffer::DxvkBuffer(
|
||||
DxvkDevice* device,
|
||||
const DxvkBufferCreateInfo& createInfo,
|
||||
DxvkMemoryAllocator& memAlloc,
|
||||
DxvkMemoryAllocator& allocator,
|
||||
VkMemoryPropertyFlags memFlags)
|
||||
: m_vkd (device->vkd()),
|
||||
m_info (createInfo),
|
||||
m_memAlloc (&memAlloc),
|
||||
m_memFlags (memFlags),
|
||||
m_shaderStages (util::shaderStages(createInfo.stages)) {
|
||||
if (!(m_info.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)) {
|
||||
// Align slices so that we don't violate any alignment
|
||||
// requirements imposed by the Vulkan device/driver
|
||||
VkDeviceSize sliceAlignment = computeSliceAlignment(device);
|
||||
m_physSliceLength = createInfo.size;
|
||||
m_physSliceStride = align(createInfo.size, sliceAlignment);
|
||||
|
||||
// Determine optimal allocation size for the buffer. If the buffer
|
||||
// is small enough to hit the pool allocator, round its size up to
|
||||
// a power of two to minimize the amount of internal fragmentation.
|
||||
VkDeviceSize sliceSize = align(createInfo.size, sliceAlignment);
|
||||
m_allocationSize = std::max(MinAllocationSize, sliceSize);
|
||||
|
||||
if (m_allocationSize <= DxvkPoolAllocator::MaxSize)
|
||||
m_allocationSize = (VkDeviceSize(-1) >> bit::lzcnt(m_allocationSize - 1u)) + 1u;
|
||||
|
||||
m_maxAllocationSize = MaxSlicesPerAllocation * sliceSize;
|
||||
m_maxAllocationSize = std::max(m_maxAllocationSize, MinAllocationSizeLimit);
|
||||
m_maxAllocationSize = std::min(m_maxAllocationSize, MaxAllocationSize);
|
||||
|
||||
// Allocate the initial set of buffer slices. Only clear
|
||||
// buffer memory if there is more than one slice, since
|
||||
// we expect the client api to initialize the first slice.
|
||||
bool hasMultipleSlices = m_allocationSize >= sliceSize + sliceSize;
|
||||
|
||||
m_buffer = allocBuffer(m_allocationSize, hasMultipleSlices);
|
||||
|
||||
m_physSlice.handle = m_buffer.buffer;
|
||||
m_physSlice.offset = m_buffer.getBaseOffset();
|
||||
m_physSlice.length = m_physSliceLength;
|
||||
m_physSlice.mapPtr = m_buffer.memory.mapPtr(0);
|
||||
|
||||
m_lazyAlloc = hasMultipleSlices;
|
||||
} else {
|
||||
m_physSliceLength = createInfo.size;
|
||||
m_physSliceStride = createInfo.size;
|
||||
|
||||
m_allocationSize = createInfo.size;
|
||||
|
||||
m_buffer = createSparseBuffer();
|
||||
|
||||
m_physSlice.handle = m_buffer.buffer;
|
||||
m_physSlice.offset = 0;
|
||||
m_physSlice.length = createInfo.size;
|
||||
m_physSlice.mapPtr = nullptr;
|
||||
|
||||
m_lazyAlloc = false;
|
||||
|
||||
m_sparsePageTable = DxvkSparsePageTable(device, this);
|
||||
}
|
||||
m_allocator (&allocator),
|
||||
m_properties (memFlags),
|
||||
m_shaderStages (util::shaderStages(createInfo.stages)),
|
||||
m_info (createInfo) {
|
||||
// Create and assign actual buffer resource
|
||||
assignSlice(allocateSlice());
|
||||
}
|
||||
|
||||
|
||||
@ -75,155 +27,16 @@ namespace dxvk {
|
||||
const DxvkBufferImportInfo& importInfo,
|
||||
VkMemoryPropertyFlags memFlags)
|
||||
: m_vkd (device->vkd()),
|
||||
m_properties (memFlags),
|
||||
m_shaderStages (util::shaderStages(createInfo.stages)),
|
||||
m_info (createInfo),
|
||||
m_import (importInfo),
|
||||
m_memAlloc (nullptr),
|
||||
m_memFlags (memFlags),
|
||||
m_shaderStages (util::shaderStages(createInfo.stages)) {
|
||||
m_physSliceLength = createInfo.size;
|
||||
m_physSliceStride = createInfo.size;
|
||||
m_import (importInfo) {
|
||||
|
||||
m_physSlice.handle = importInfo.buffer;
|
||||
m_physSlice.offset = importInfo.offset;
|
||||
m_physSlice.length = createInfo.size;
|
||||
m_physSlice.mapPtr = importInfo.mapPtr;
|
||||
|
||||
m_allocationSize = createInfo.size;
|
||||
|
||||
m_lazyAlloc = false;
|
||||
}
|
||||
|
||||
|
||||
DxvkBuffer::~DxvkBuffer() {
|
||||
for (const auto& buffer : m_buffers) {
|
||||
if (buffer.buffer != buffer.memory.buffer())
|
||||
m_vkd->vkDestroyBuffer(m_vkd->device(), buffer.buffer, nullptr);
|
||||
}
|
||||
|
||||
if (m_buffer.buffer != m_buffer.memory.buffer())
|
||||
m_vkd->vkDestroyBuffer(m_vkd->device(), m_buffer.buffer, nullptr);
|
||||
}
|
||||
|
||||
|
||||
DxvkBufferHandle DxvkBuffer::allocBuffer(VkDeviceSize allocationSize, bool clear) const {
|
||||
VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
info.flags = m_info.flags;
|
||||
info.size = allocationSize;
|
||||
info.usage = m_info.usage;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
DxvkBufferHandle handle;
|
||||
|
||||
// Query memory requirements and whether to use a dedicated allocation
|
||||
DxvkMemoryRequirements memoryRequirements = { };
|
||||
memoryRequirements.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };
|
||||
memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated };
|
||||
|
||||
m_memAlloc->getBufferMemoryRequirements(info, memoryRequirements.core);
|
||||
|
||||
// Fill in desired memory properties
|
||||
DxvkMemoryProperties memoryProperties = { };
|
||||
memoryProperties.flags = m_memFlags;
|
||||
|
||||
if (memoryRequirements.dedicated.prefersDedicatedAllocation) {
|
||||
handle.buffer = createBuffer(info);
|
||||
|
||||
memoryProperties.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };
|
||||
memoryProperties.dedicated.buffer = handle.buffer;
|
||||
}
|
||||
|
||||
handle.memory = m_memAlloc->alloc(memoryRequirements, memoryProperties);
|
||||
|
||||
if (!handle.buffer && (!handle.memory.buffer() || (handle.memory.getBufferUsage() & info.usage) != info.usage))
|
||||
handle.buffer = createBuffer(info);
|
||||
|
||||
if (handle.buffer) {
|
||||
if (m_vkd->vkBindBufferMemory(m_vkd->device(), handle.buffer,
|
||||
handle.memory.memory(), handle.memory.offset()) != VK_SUCCESS)
|
||||
throw DxvkError("DxvkBuffer: Failed to bind device memory");
|
||||
} else {
|
||||
handle.buffer = handle.memory.buffer();
|
||||
}
|
||||
|
||||
if (clear && (m_memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
|
||||
std::memset(handle.memory.mapPtr(0), 0, info.size);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
DxvkBufferHandle DxvkBuffer::createSparseBuffer() const {
|
||||
VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
info.flags = m_info.flags;
|
||||
info.size = m_info.size;
|
||||
info.usage = m_info.usage;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
DxvkBufferHandle handle = { };
|
||||
|
||||
if (m_vkd->vkCreateBuffer(m_vkd->device(),
|
||||
&info, nullptr, &handle.buffer) != VK_SUCCESS) {
|
||||
throw DxvkError(str::format(
|
||||
"DxvkBuffer: Failed to create buffer:"
|
||||
"\n flags: ", std::hex, info.flags,
|
||||
"\n size: ", std::dec, info.size,
|
||||
"\n usage: ", std::hex, info.usage));
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
VkBuffer DxvkBuffer::createBuffer(const VkBufferCreateInfo& info) const {
|
||||
VkBuffer buffer = VK_NULL_HANDLE;
|
||||
|
||||
if (m_vkd->vkCreateBuffer(m_vkd->device(), &info, nullptr, &buffer)) {
|
||||
throw DxvkError(str::format(
|
||||
"DxvkBuffer: Failed to create buffer:"
|
||||
"\n flags: ", std::hex, info.flags,
|
||||
"\n size: ", std::dec, info.size,
|
||||
"\n usage: ", std::hex, info.usage));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
VkDeviceSize DxvkBuffer::computeSliceAlignment(DxvkDevice* device) const {
|
||||
const auto& devInfo = device->properties();
|
||||
|
||||
VkDeviceSize result = sizeof(uint32_t);
|
||||
|
||||
if (m_info.usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
|
||||
result = std::max(result, devInfo.core.properties.limits.minUniformBufferOffsetAlignment);
|
||||
result = std::max(result, devInfo.extRobustness2.robustUniformBufferAccessSizeAlignment);
|
||||
}
|
||||
|
||||
if (m_info.usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
|
||||
result = std::max(result, devInfo.core.properties.limits.minStorageBufferOffsetAlignment);
|
||||
result = std::max(result, devInfo.extRobustness2.robustStorageBufferAccessSizeAlignment);
|
||||
}
|
||||
|
||||
if (m_info.usage & (VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT)) {
|
||||
result = std::max(result, devInfo.core.properties.limits.minTexelBufferOffsetAlignment);
|
||||
result = std::max(result, VkDeviceSize(16));
|
||||
}
|
||||
|
||||
if (m_info.usage & (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
|
||||
&& m_info.size > (devInfo.core.properties.limits.optimalBufferCopyOffsetAlignment / 2))
|
||||
result = std::max(result, devInfo.core.properties.limits.optimalBufferCopyOffsetAlignment);
|
||||
|
||||
// For some reason, Warhammer Chaosbane breaks otherwise
|
||||
if (m_info.usage & (VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT))
|
||||
result = std::max(result, VkDeviceSize(256));
|
||||
|
||||
if (m_memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
|
||||
result = std::max(result, devInfo.core.properties.limits.nonCoherentAtomSize);
|
||||
result = std::max(result, VkDeviceSize(64));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -304,21 +117,4 @@ namespace dxvk {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxvkBufferTracker:: DxvkBufferTracker() { }
|
||||
DxvkBufferTracker::~DxvkBufferTracker() { }
|
||||
|
||||
|
||||
void DxvkBufferTracker::reset() {
|
||||
std::sort(m_entries.begin(), m_entries.end(),
|
||||
[] (const Entry& a, const Entry& b) {
|
||||
return a.slice.handle < b.slice.handle;
|
||||
});
|
||||
|
||||
for (const auto& e : m_entries)
|
||||
e.buffer->freeSlice(e.slice);
|
||||
|
||||
m_entries.clear();
|
||||
}
|
||||
|
||||
}
|
@ -45,16 +45,16 @@ namespace dxvk {
|
||||
*/
|
||||
struct DxvkBufferViewCreateInfo {
|
||||
/// Buffer data format, like image data
|
||||
VkFormat format;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
|
||||
/// Offset of the buffer region to include in the view
|
||||
VkDeviceSize rangeOffset;
|
||||
VkDeviceSize rangeOffset = 0u;
|
||||
|
||||
/// Size of the buffer region to include in the view
|
||||
VkDeviceSize rangeLength;
|
||||
VkDeviceSize rangeLength = 0u;
|
||||
|
||||
/// Buffer view usage flags
|
||||
VkBufferUsageFlags usage;
|
||||
VkBufferUsageFlags usage = 0u;
|
||||
};
|
||||
|
||||
|
||||
@ -129,36 +129,20 @@ namespace dxvk {
|
||||
*/
|
||||
class DxvkBufferAllocation {
|
||||
friend class DxvkBuffer;
|
||||
friend class DxvkContext; /* TODO remove */
|
||||
public:
|
||||
|
||||
DxvkBufferAllocation() = default;
|
||||
|
||||
explicit DxvkBufferAllocation(DxvkBufferSliceHandle slice)
|
||||
: m_slice(slice) { }
|
||||
|
||||
DxvkBufferAllocation(const DxvkBufferAllocation&) = default;
|
||||
DxvkBufferAllocation& operator = (const DxvkBufferAllocation&) = default;
|
||||
|
||||
DxvkBufferAllocation(DxvkBufferAllocation&& other)
|
||||
: m_slice(other.m_slice) {
|
||||
other.m_slice = DxvkBufferSliceHandle();
|
||||
}
|
||||
|
||||
DxvkBufferAllocation& operator = (DxvkBufferAllocation&& other) {
|
||||
m_slice = other.m_slice;
|
||||
other.m_slice = DxvkBufferSliceHandle();
|
||||
return *this;
|
||||
}
|
||||
|
||||
~DxvkBufferAllocation() = default;
|
||||
DxvkBufferAllocation(
|
||||
Rc<DxvkResourceAllocation> allocation)
|
||||
: m_allocation(std::move(allocation)) { }
|
||||
|
||||
/**
|
||||
* \brief Retrieves CPU pointer
|
||||
* \returns Pointer to the mapped buffer slice
|
||||
*/
|
||||
void* mapPtr() const {
|
||||
return m_slice.mapPtr;
|
||||
return m_allocation->getBufferInfo().mapPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,12 +150,20 @@ namespace dxvk {
|
||||
* \returns \c true if the slice is valid
|
||||
*/
|
||||
explicit operator bool () const {
|
||||
return m_slice.handle != VK_NULL_HANDLE;
|
||||
return bool(m_allocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Extracts resource allocation
|
||||
* \returns Underlying resource allocation
|
||||
*/
|
||||
Rc<DxvkResourceAllocation> extract() {
|
||||
return std::move(m_allocation);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DxvkBufferSliceHandle m_slice = { };
|
||||
Rc<DxvkResourceAllocation> m_allocation;
|
||||
|
||||
};
|
||||
|
||||
@ -189,9 +181,8 @@ namespace dxvk {
|
||||
constexpr static VkDeviceSize MaxAllocationSize = DxvkPageAllocator::PageSize;
|
||||
constexpr static VkDeviceSize MinAllocationSize = DxvkPoolAllocator::MinSize;
|
||||
|
||||
constexpr static VkDeviceSize MinAllocationSizeLimit = MaxAllocationSize / 32u;
|
||||
|
||||
constexpr static uint32_t MaxSlicesPerAllocation = 64u;
|
||||
constexpr static VkDeviceSize MinMappedAllocationSize = DxvkPageAllocator::PageSize / 32u;
|
||||
constexpr static VkDeviceSize MinMappedSlicesPerAllocation = 3u;
|
||||
public:
|
||||
|
||||
DxvkBuffer(
|
||||
@ -224,7 +215,7 @@ namespace dxvk {
|
||||
* \returns Vulkan memory flags
|
||||
*/
|
||||
VkMemoryPropertyFlags memFlags() const {
|
||||
return m_memFlags;
|
||||
return m_properties;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,7 +228,9 @@ namespace dxvk {
|
||||
* \returns Pointer to mapped memory region
|
||||
*/
|
||||
void* mapPtr(VkDeviceSize offset) const {
|
||||
return reinterpret_cast<char*>(m_physSlice.mapPtr) + offset;
|
||||
return m_bufferInfo.mapPtr
|
||||
? reinterpret_cast<char*>(m_bufferInfo.mapPtr) + offset
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,7 +248,12 @@ namespace dxvk {
|
||||
* \returns Buffer slice handle
|
||||
*/
|
||||
DxvkBufferSliceHandle getSliceHandle() const {
|
||||
return m_physSlice;
|
||||
DxvkBufferSliceHandle result = { };
|
||||
result.handle = m_bufferInfo.buffer;
|
||||
result.offset = m_bufferInfo.offset;
|
||||
result.length = m_info.size;
|
||||
result.mapPtr = mapPtr(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,9 +264,9 @@ namespace dxvk {
|
||||
* \returns Buffer slice handle
|
||||
*/
|
||||
DxvkBufferSliceHandle getSliceHandle(VkDeviceSize offset, VkDeviceSize length) const {
|
||||
DxvkBufferSliceHandle result;
|
||||
result.handle = m_physSlice.handle;
|
||||
result.offset = m_physSlice.offset + offset;
|
||||
DxvkBufferSliceHandle result = { };
|
||||
result.handle = m_bufferInfo.buffer;
|
||||
result.offset = m_bufferInfo.offset + offset;
|
||||
result.length = length;
|
||||
result.mapPtr = mapPtr(offset);
|
||||
return result;
|
||||
@ -282,10 +280,10 @@ namespace dxvk {
|
||||
* \returns Buffer slice descriptor
|
||||
*/
|
||||
DxvkDescriptorInfo getDescriptor(VkDeviceSize offset, VkDeviceSize length) const {
|
||||
DxvkDescriptorInfo result;
|
||||
result.buffer.buffer = m_physSlice.handle;
|
||||
result.buffer.offset = m_physSlice.offset + offset;
|
||||
result.buffer.range = length;
|
||||
DxvkDescriptorInfo result = { };
|
||||
result.buffer.buffer = m_bufferInfo.buffer;
|
||||
result.buffer.offset = m_bufferInfo.offset + offset;
|
||||
result.buffer.range = length;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -296,7 +294,7 @@ namespace dxvk {
|
||||
* \returns The current xfb vertex stride
|
||||
*/
|
||||
uint32_t getXfbVertexStride() const {
|
||||
return m_vertexStride;
|
||||
return m_xfbStride;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -308,7 +306,7 @@ namespace dxvk {
|
||||
* \param [in] stride Vertex stride
|
||||
*/
|
||||
void setXfbVertexStride(uint32_t stride) {
|
||||
m_vertexStride = stride;
|
||||
m_xfbStride = stride;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -316,39 +314,13 @@ namespace dxvk {
|
||||
* \returns The new buffer slice
|
||||
*/
|
||||
DxvkBufferAllocation allocateSlice() {
|
||||
std::unique_lock<sync::Spinlock> freeLock(m_freeMutex);
|
||||
|
||||
// If no slices are available, swap the two free lists.
|
||||
if (unlikely(m_freeSlices.empty())) {
|
||||
std::unique_lock<sync::Spinlock> swapLock(m_swapMutex);
|
||||
std::swap(m_freeSlices, m_nextSlices);
|
||||
}
|
||||
VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
info.flags = m_info.flags;
|
||||
info.usage = m_info.usage;
|
||||
info.size = m_info.size;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
// If there are still no slices available, create a new
|
||||
// backing buffer and add all slices to the free list.
|
||||
if (unlikely(m_freeSlices.empty())) {
|
||||
if (likely(!m_lazyAlloc)) {
|
||||
DxvkBufferHandle handle = allocBuffer(m_allocationSize, true);
|
||||
|
||||
for (uint32_t i = 0; (i + 1) * m_physSliceStride <= m_allocationSize; i++)
|
||||
pushSlice(handle, i);
|
||||
|
||||
m_buffers.push_back(std::move(handle));
|
||||
|
||||
if (2u * m_allocationSize <= m_maxAllocationSize)
|
||||
m_allocationSize *= 2u;
|
||||
} else {
|
||||
for (uint32_t i = 1; (i + 1) * m_physSliceStride <= m_allocationSize; i++)
|
||||
pushSlice(m_buffer, i);
|
||||
|
||||
m_lazyAlloc = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Take the first slice from the queue
|
||||
DxvkBufferAllocation result(m_freeSlices.back());
|
||||
m_freeSlices.pop_back();
|
||||
return result;
|
||||
return DxvkBufferAllocation(m_allocator->createBufferResource(info, m_properties));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,12 +331,13 @@ namespace dxvk {
|
||||
* not call this directly as this is called implicitly
|
||||
* by the context's \c invalidateBuffer method.
|
||||
* \param [in] slice The new backing resource
|
||||
* \returns Previous buffer slice
|
||||
* \returns Previous buffer allocation
|
||||
*/
|
||||
DxvkBufferAllocation assignSlice(DxvkBufferAllocation&& slice) {
|
||||
DxvkBufferAllocation result(m_physSlice);
|
||||
m_physSlice = slice.m_slice;
|
||||
slice.m_slice = DxvkBufferSliceHandle();
|
||||
Rc<DxvkResourceAllocation> assignSlice(DxvkBufferAllocation&& slice) {
|
||||
Rc<DxvkResourceAllocation> result = std::move(m_storage);
|
||||
|
||||
m_storage = std::move(slice.m_allocation);
|
||||
m_bufferInfo = m_storage->getBufferInfo();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -373,21 +346,7 @@ namespace dxvk {
|
||||
* \returns Current buffer allocation
|
||||
*/
|
||||
DxvkBufferAllocation getAllocation() const {
|
||||
return DxvkBufferAllocation(m_physSlice);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Frees a buffer slice
|
||||
*
|
||||
* Marks the slice as free so that it can be used for
|
||||
* subsequent allocations. Called automatically when
|
||||
* the slice is no longer needed by the GPU.
|
||||
* \param [in] slice The buffer slice to free
|
||||
*/
|
||||
void freeSlice(const DxvkBufferSliceHandle& slice) {
|
||||
// Add slice to a separate free list to reduce lock contention.
|
||||
std::unique_lock<sync::Spinlock> swapLock(m_swapMutex);
|
||||
m_nextSlices.emplace_back(slice);
|
||||
return DxvkBufferAllocation(m_storage);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,53 +359,19 @@ namespace dxvk {
|
||||
|
||||
private:
|
||||
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
DxvkBufferCreateInfo m_info;
|
||||
DxvkBufferImportInfo m_import;
|
||||
DxvkMemoryAllocator* m_memAlloc;
|
||||
VkMemoryPropertyFlags m_memFlags;
|
||||
VkShaderStageFlags m_shaderStages;
|
||||
|
||||
DxvkBufferHandle m_buffer;
|
||||
DxvkBufferSliceHandle m_physSlice;
|
||||
uint32_t m_vertexStride = 0;
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
DxvkMemoryAllocator* m_allocator = nullptr;
|
||||
VkMemoryPropertyFlags m_properties = 0u;
|
||||
VkShaderStageFlags m_shaderStages = 0u;
|
||||
|
||||
alignas(CACHE_LINE_SIZE)
|
||||
sync::Spinlock m_freeMutex;
|
||||
DxvkBufferCreateInfo m_info = { };
|
||||
DxvkBufferImportInfo m_import = { };
|
||||
|
||||
uint32_t m_lazyAlloc = false;
|
||||
VkDeviceSize m_physSliceLength = 0;
|
||||
VkDeviceSize m_physSliceStride = 0;
|
||||
VkDeviceSize m_xfbStride = 0u;
|
||||
|
||||
VkDeviceSize m_allocationSize = 0;
|
||||
VkDeviceSize m_maxAllocationSize = 0;
|
||||
DxvkResourceBufferInfo m_bufferInfo = { };
|
||||
|
||||
std::vector<DxvkBufferHandle> m_buffers;
|
||||
std::vector<DxvkBufferAllocation> m_freeSlices;
|
||||
|
||||
alignas(CACHE_LINE_SIZE)
|
||||
sync::Spinlock m_swapMutex;
|
||||
std::vector<DxvkBufferAllocation> m_nextSlices;
|
||||
|
||||
void pushSlice(const DxvkBufferHandle& handle, uint32_t index) {
|
||||
DxvkBufferSliceHandle slice;
|
||||
slice.handle = handle.buffer;
|
||||
slice.offset = handle.getBaseOffset() + m_physSliceStride * index;
|
||||
slice.length = m_physSliceLength;
|
||||
slice.mapPtr = handle.memory.mapPtr(m_physSliceStride * index);
|
||||
m_freeSlices.emplace_back(slice);
|
||||
}
|
||||
|
||||
DxvkBufferHandle allocBuffer(
|
||||
VkDeviceSize allocationSize,
|
||||
bool clear) const;
|
||||
|
||||
DxvkBufferHandle createSparseBuffer() const;
|
||||
|
||||
VkBuffer createBuffer(const VkBufferCreateInfo& info) const;
|
||||
|
||||
VkDeviceSize computeSliceAlignment(
|
||||
DxvkDevice* device) const;
|
||||
Rc<DxvkResourceAllocation> m_storage;
|
||||
|
||||
};
|
||||
|
||||
@ -772,48 +697,5 @@ namespace dxvk {
|
||||
const DxvkBufferSliceHandle& slice);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Buffer slice tracker
|
||||
*
|
||||
* Stores a list of buffer slices that can be
|
||||
* freed. Useful when buffers have been renamed
|
||||
* and the original slice is no longer needed.
|
||||
*/
|
||||
class DxvkBufferTracker {
|
||||
|
||||
public:
|
||||
|
||||
DxvkBufferTracker();
|
||||
~DxvkBufferTracker();
|
||||
|
||||
/**
|
||||
* \brief Add buffer slice for tracking
|
||||
*
|
||||
* The slice will be returned to the
|
||||
* buffer on the next call to \c reset.
|
||||
* \param [in] buffer The parent buffer
|
||||
* \param [in] slice The buffer slice
|
||||
*/
|
||||
void freeBufferSlice(const Rc<DxvkBuffer>& buffer, const DxvkBufferSliceHandle& slice) {
|
||||
m_entries.push_back({ buffer, slice });
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns tracked buffer slices
|
||||
*/
|
||||
void reset();
|
||||
|
||||
private:
|
||||
|
||||
struct Entry {
|
||||
Rc<DxvkBuffer> buffer;
|
||||
DxvkBufferSliceHandle slice;
|
||||
};
|
||||
|
||||
std::vector<Entry> m_entries;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -368,9 +368,6 @@ namespace dxvk {
|
||||
// that are no longer in use
|
||||
m_resources.reset();
|
||||
|
||||
// Return buffer memory slices
|
||||
m_bufferTracker.reset();
|
||||
|
||||
// Return query and event handles
|
||||
m_gpuQueryTracker.reset();
|
||||
m_gpuEventTracker.reset();
|
||||
|
@ -248,21 +248,6 @@ namespace dxvk {
|
||||
*/
|
||||
void next();
|
||||
|
||||
/**
|
||||
* \brief Frees buffer slice
|
||||
*
|
||||
* After the command buffer execution has finished,
|
||||
* the given buffer slice will be released to the
|
||||
* virtual buffer object so that it can be reused.
|
||||
* \param [in] buffer The virtual buffer object
|
||||
* \param [in] slice The buffer slice handle
|
||||
*/
|
||||
void freeBufferSlice(
|
||||
const Rc<DxvkBuffer>& buffer,
|
||||
const DxvkBufferSliceHandle& slice) {
|
||||
m_bufferTracker.freeBufferSlice(buffer, slice);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a resource to track
|
||||
*
|
||||
@ -323,7 +308,7 @@ namespace dxvk {
|
||||
* \brief Notifies resources and signals
|
||||
*/
|
||||
void notifyObjects() {
|
||||
m_resources.notify();
|
||||
m_resources.reset();
|
||||
m_signalTracker.notify();
|
||||
}
|
||||
|
||||
@ -1048,7 +1033,6 @@ namespace dxvk {
|
||||
DxvkSignalTracker m_signalTracker;
|
||||
DxvkGpuEventTracker m_gpuEventTracker;
|
||||
DxvkGpuQueryTracker m_gpuQueryTracker;
|
||||
DxvkBufferTracker m_bufferTracker;
|
||||
DxvkStatCounters m_statCounters;
|
||||
|
||||
DxvkCommandSubmission m_commandSubmission;
|
||||
|
@ -1830,9 +1830,9 @@ namespace dxvk {
|
||||
const Rc<DxvkBuffer>& buffer,
|
||||
DxvkBufferAllocation&& slice) {
|
||||
// Allocate new backing resource
|
||||
DxvkBufferAllocation prevSlice = buffer->assignSlice(std::move(slice));
|
||||
m_cmd->freeBufferSlice(buffer, prevSlice.m_slice);
|
||||
|
||||
Rc<DxvkResourceAllocation> prevAllocation = buffer->assignSlice(std::move(slice));
|
||||
m_cmd->trackResource<DxvkAccess::None>(prevAllocation);
|
||||
|
||||
// We also need to update all bindings that the buffer
|
||||
// may be bound to either directly or through views.
|
||||
VkBufferUsageFlags usage = buffer->info().usage &
|
||||
|
@ -6,13 +6,9 @@ namespace dxvk {
|
||||
DxvkLifetimeTracker::~DxvkLifetimeTracker() { }
|
||||
|
||||
|
||||
void DxvkLifetimeTracker::notify() {
|
||||
m_resources.clear();
|
||||
}
|
||||
|
||||
|
||||
void DxvkLifetimeTracker::reset() {
|
||||
m_resources.clear();
|
||||
m_allocations.clear();
|
||||
}
|
||||
|
||||
}
|
@ -11,39 +11,38 @@ namespace dxvk {
|
||||
*
|
||||
* Keeps a resource alive and stores access information.
|
||||
*/
|
||||
template<typename T>
|
||||
class DxvkLifetime {
|
||||
static constexpr uintptr_t AccessMask = 0x3u;
|
||||
static constexpr uintptr_t PointerMask = ~AccessMask;
|
||||
|
||||
static_assert(alignof(T) > AccessMask);
|
||||
public:
|
||||
|
||||
DxvkLifetime()
|
||||
: m_resource(nullptr), m_access(DxvkAccess::None) { }
|
||||
DxvkLifetime() = default;
|
||||
|
||||
DxvkLifetime(
|
||||
DxvkResource* resource,
|
||||
T* resource,
|
||||
DxvkAccess access)
|
||||
: m_resource(resource), m_access(access) {
|
||||
: m_ptr(reinterpret_cast<uintptr_t>(resource) | uintptr_t(access)) {
|
||||
acquire();
|
||||
}
|
||||
|
||||
DxvkLifetime(DxvkLifetime&& other)
|
||||
: m_resource(other.m_resource), m_access(other.m_access) {
|
||||
other.m_resource = nullptr;
|
||||
other.m_access = DxvkAccess::None;
|
||||
: m_ptr(other.m_ptr) {
|
||||
other.m_ptr = 0u;
|
||||
}
|
||||
|
||||
DxvkLifetime(const DxvkLifetime& other)
|
||||
: m_resource(other.m_resource), m_access(other.m_access) {
|
||||
: m_ptr(other.m_ptr) {
|
||||
acquire();
|
||||
}
|
||||
|
||||
DxvkLifetime& operator = (DxvkLifetime&& other) {
|
||||
release();
|
||||
|
||||
m_resource = other.m_resource;
|
||||
m_access = other.m_access;
|
||||
|
||||
other.m_resource = nullptr;
|
||||
other.m_access = DxvkAccess::None;
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = 0u;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -51,8 +50,7 @@ namespace dxvk {
|
||||
other.acquire();
|
||||
release();
|
||||
|
||||
m_resource = other.m_resource;
|
||||
m_access = other.m_access;
|
||||
m_ptr = other.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -62,17 +60,24 @@ namespace dxvk {
|
||||
|
||||
private:
|
||||
|
||||
DxvkResource* m_resource;
|
||||
DxvkAccess m_access;
|
||||
uintptr_t m_ptr = 0u;
|
||||
|
||||
T* ptr() const {
|
||||
return reinterpret_cast<T*>(m_ptr & PointerMask);
|
||||
}
|
||||
|
||||
DxvkAccess access() const {
|
||||
return DxvkAccess(m_ptr & AccessMask);
|
||||
}
|
||||
|
||||
void acquire() const {
|
||||
if (m_resource)
|
||||
m_resource->acquire(m_access);
|
||||
if (m_ptr)
|
||||
ptr()->acquire(access());
|
||||
}
|
||||
|
||||
void release() const {
|
||||
if (m_resource)
|
||||
m_resource->release(m_access);
|
||||
if (m_ptr)
|
||||
ptr()->release(access());
|
||||
}
|
||||
|
||||
};
|
||||
@ -102,6 +107,15 @@ namespace dxvk {
|
||||
m_resources.emplace_back(rc, Access);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a resource allocation to track
|
||||
* \param [in] rc The allocation to track
|
||||
*/
|
||||
template<DxvkAccess Access>
|
||||
void trackResource(DxvkResourceAllocation* rc) {
|
||||
m_allocations.emplace_back(rc, Access);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Releases resources
|
||||
*
|
||||
@ -119,7 +133,8 @@ namespace dxvk {
|
||||
|
||||
private:
|
||||
|
||||
std::vector<DxvkLifetime> m_resources;
|
||||
std::vector<DxvkLifetime<DxvkResource>> m_resources;
|
||||
std::vector<DxvkLifetime<DxvkResourceAllocation>> m_allocations;
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user