1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-11 19:24:11 +01:00

[dxvk] Implement dynamically growing chunk size

May reduce memory footprint for launchers.
This commit is contained in:
Philip Rebohle 2024-09-06 01:27:00 +02:00
parent 63506ee1ff
commit 619b9b12c2
2 changed files with 50 additions and 24 deletions

View File

@ -183,8 +183,7 @@ namespace dxvk {
DxvkMemoryAllocator::DxvkMemoryAllocator(DxvkDevice* device) DxvkMemoryAllocator::DxvkMemoryAllocator(DxvkDevice* device)
: m_device (device), : m_device (device),
m_memProps (device->adapter()->memoryProperties()), m_memProps (device->adapter()->memoryProperties()) {
m_maxChunkSize (determineMaxChunkSize(device)) {
for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) {
m_memHeaps[i].properties = m_memProps.memoryHeaps[i]; m_memHeaps[i].properties = m_memProps.memoryHeaps[i];
m_memHeaps[i].stats = DxvkMemoryStats { 0, 0 }; m_memHeaps[i].stats = DxvkMemoryStats { 0, 0 };
@ -195,6 +194,7 @@ namespace dxvk {
m_memTypes[i].heapId = m_memProps.memoryTypes[i].heapIndex; m_memTypes[i].heapId = m_memProps.memoryTypes[i].heapIndex;
m_memTypes[i].memType = m_memProps.memoryTypes[i]; m_memTypes[i].memType = m_memProps.memoryTypes[i];
m_memTypes[i].memTypeId = i; m_memTypes[i].memTypeId = i;
m_memTypes[i].chunkSize = MinChunkSize;
} }
if (device->features().core.features.sparseBinding) if (device->features().core.features.sparseBinding)
@ -313,7 +313,7 @@ namespace dxvk {
VkDeviceSize align, VkDeviceSize align,
const DxvkMemoryProperties& info, const DxvkMemoryProperties& info,
DxvkMemoryFlags hints) { DxvkMemoryFlags hints) {
VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, hints); VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, size, hints);
DxvkMemory memory; DxvkMemory memory;
@ -349,6 +349,8 @@ namespace dxvk {
memory = chunk->alloc(info.flags, size, align, hints); memory = chunk->alloc(info.flags, size, align, hints);
type->chunks.push_back(std::move(chunk)); type->chunks.push_back(std::move(chunk));
adjustChunkSize(type->memTypeId, devMem.memSize, hints);
} }
} }
} }
@ -489,12 +491,14 @@ namespace dxvk {
} }
VkDeviceSize DxvkMemoryAllocator::pickChunkSize(uint32_t memTypeId, DxvkMemoryFlags hints) const { VkDeviceSize DxvkMemoryAllocator::pickChunkSize(uint32_t memTypeId, VkDeviceSize requiredSize, DxvkMemoryFlags hints) const {
VkMemoryType type = m_memProps.memoryTypes[memTypeId]; VkMemoryType type = m_memProps.memoryTypes[memTypeId];
VkMemoryHeap heap = m_memProps.memoryHeaps[type.heapIndex]; VkMemoryHeap heap = m_memProps.memoryHeaps[type.heapIndex];
// Default to a chunk size of 256 MiB VkDeviceSize chunkSize = m_memTypes[memTypeId].chunkSize;
VkDeviceSize chunkSize = m_maxChunkSize;
while (chunkSize < requiredSize)
chunkSize <<= 1u;
if (hints.test(DxvkMemoryFlag::Small)) if (hints.test(DxvkMemoryFlag::Small))
chunkSize = std::min<VkDeviceSize>(chunkSize, 16 << 20); chunkSize = std::min<VkDeviceSize>(chunkSize, 16 << 20);
@ -513,6 +517,20 @@ namespace dxvk {
} }
void DxvkMemoryAllocator::adjustChunkSize(
uint32_t memTypeId,
VkDeviceSize allocatedSize,
DxvkMemoryFlags hints) {
VkDeviceSize chunkSize = m_memTypes[memTypeId].chunkSize;
// Don't bump chunk size if we reached the maximum or if
// we already were unable to allocate a full chunk.
if (chunkSize < MaxChunkSize && chunkSize <= allocatedSize
&& chunkSize <= m_memTypes[memTypeId].heap->stats.memoryAllocated)
m_memTypes[memTypeId].chunkSize <<= 1u;
}
bool DxvkMemoryAllocator::shouldFreeChunk( bool DxvkMemoryAllocator::shouldFreeChunk(
const DxvkMemoryType* type, const DxvkMemoryType* type,
const Rc<DxvkMemoryChunk>& chunk) const { const Rc<DxvkMemoryChunk>& chunk) const {
@ -520,6 +538,11 @@ namespace dxvk {
if (this->shouldFreeEmptyChunks(type->heap, 0)) if (this->shouldFreeEmptyChunks(type->heap, 0))
return true; return true;
// Free chunks that are below the current chunk size since it probably
// not going to be able to serve enough allocations to be useful.
if (chunk->size() < type->chunkSize)
return true;
// Only keep a small number of chunks of each type around to save memory. // Only keep a small number of chunks of each type around to save memory.
uint32_t numEmptyChunks = 0; uint32_t numEmptyChunks = 0;
@ -635,17 +658,6 @@ namespace dxvk {
} }
VkDeviceSize DxvkMemoryAllocator::determineMaxChunkSize(
DxvkDevice* device) const {
int32_t option = device->config().maxChunkSize;
if (option <= 0)
option = 256;
return VkDeviceSize(option) << 20;
}
void DxvkMemoryAllocator::logMemoryError(const VkMemoryRequirements& req) const { void DxvkMemoryAllocator::logMemoryError(const VkMemoryRequirements& req) const {
std::stringstream sstr; std::stringstream sstr;
sstr << "DxvkMemoryAllocator: Memory allocation failed" << std::endl sstr << "DxvkMemoryAllocator: Memory allocation failed" << std::endl

View File

@ -83,6 +83,8 @@ namespace dxvk {
VkMemoryType memType; VkMemoryType memType;
uint32_t memTypeId; uint32_t memTypeId;
VkDeviceSize chunkSize;
std::vector<Rc<DxvkMemoryChunk>> chunks; std::vector<Rc<DxvkMemoryChunk>> chunks;
}; };
@ -211,6 +213,14 @@ namespace dxvk {
~DxvkMemoryChunk(); ~DxvkMemoryChunk();
/**
* \brief Queries chunk size
* \returns Chunk size
*/
VkDeviceSize size() const {
return m_memory.memSize;
}
/** /**
* \brief Allocates memory from the chunk * \brief Allocates memory from the chunk
* *
@ -304,6 +314,9 @@ namespace dxvk {
friend class DxvkMemoryChunk; friend class DxvkMemoryChunk;
constexpr static VkDeviceSize SmallAllocationThreshold = 256 << 10; constexpr static VkDeviceSize SmallAllocationThreshold = 256 << 10;
constexpr static VkDeviceSize MinChunkSize = 4ull << 20;
constexpr static VkDeviceSize MaxChunkSize = 256ull << 20;
public: public:
DxvkMemoryAllocator(DxvkDevice* device); DxvkMemoryAllocator(DxvkDevice* device);
@ -348,10 +361,8 @@ namespace dxvk {
VkPhysicalDeviceMemoryProperties m_memProps; VkPhysicalDeviceMemoryProperties m_memProps;
dxvk::mutex m_mutex; dxvk::mutex m_mutex;
std::array<DxvkMemoryHeap, VK_MAX_MEMORY_HEAPS> m_memHeaps; std::array<DxvkMemoryHeap, VK_MAX_MEMORY_HEAPS> m_memHeaps = { };
std::array<DxvkMemoryType, VK_MAX_MEMORY_TYPES> m_memTypes; std::array<DxvkMemoryType, VK_MAX_MEMORY_TYPES> m_memTypes = { };
VkDeviceSize m_maxChunkSize;
uint32_t m_sparseMemoryTypes = 0u; uint32_t m_sparseMemoryTypes = 0u;
@ -388,8 +399,14 @@ namespace dxvk {
VkDeviceSize pickChunkSize( VkDeviceSize pickChunkSize(
uint32_t memTypeId, uint32_t memTypeId,
VkDeviceSize requiredSize,
DxvkMemoryFlags hints) const; DxvkMemoryFlags hints) const;
void adjustChunkSize(
uint32_t memTypeId,
VkDeviceSize allocatedSize,
DxvkMemoryFlags hints);
bool shouldFreeChunk( bool shouldFreeChunk(
const DxvkMemoryType* type, const DxvkMemoryType* type,
const Rc<DxvkMemoryChunk>& chunk) const; const Rc<DxvkMemoryChunk>& chunk) const;
@ -404,9 +421,6 @@ namespace dxvk {
uint32_t determineSparseMemoryTypes( uint32_t determineSparseMemoryTypes(
DxvkDevice* device) const; DxvkDevice* device) const;
VkDeviceSize determineMaxChunkSize(
DxvkDevice* device) const;
void logMemoryError( void logMemoryError(
const VkMemoryRequirements& req) const; const VkMemoryRequirements& req) const;