rsx/vk: Implement bulk aligned allocator

- Avoids wasting space and allows use of natural arrays in shaders
This commit is contained in:
kd-11
2026-04-29 00:04:26 +03:00
committed by kd-11
parent 8b02f46e67
commit 3b1abec405
3 changed files with 195 additions and 155 deletions
+40 -7
View File
@@ -3,7 +3,9 @@
#include "Utilities/StrFmt.h" #include "Utilities/StrFmt.h"
#include "util/asm.hpp" #include "util/asm.hpp"
/** namespace rsx
{
/**
* Ring buffer memory helper : * Ring buffer memory helper :
* There are 2 "pointers" (offset inside a memory buffer to be provided by class derivative) * There are 2 "pointers" (offset inside a memory buffer to be provided by class derivative)
* PUT pointer "points" to the start of allocatable space. * PUT pointer "points" to the start of allocatable space.
@@ -11,9 +13,9 @@
* Space between GET and PUT is used by the GPU ; this structure check that this memory is not overwritten. * Space between GET and PUT is used by the GPU ; this structure check that this memory is not overwritten.
* User has to update the GET pointer when synchronisation happens. * User has to update the GET pointer when synchronisation happens.
*/ */
class data_heap class data_heap
{ {
protected: protected:
/** /**
* Internal implementation of allocation test * Internal implementation of allocation test
* Does alloc cross get position? * Does alloc cross get position?
@@ -70,13 +72,13 @@ protected:
usz m_min_guard_size; // If an allocation touches the guard region, reset the heap to avoid going over budget usz m_min_guard_size; // If an allocation touches the guard region, reset the heap to avoid going over budget
char* m_name; char* m_name;
public: public:
data_heap() = default; data_heap() = default;
~data_heap() = default; ~data_heap() = default;
data_heap(const data_heap&) = delete; data_heap(const data_heap&) = delete;
data_heap(data_heap&&) = delete; data_heap(data_heap&&) = delete;
void init(usz heap_size, const char* buffer_name = nullptr, usz min_guard_size=0x10000) void init(usz heap_size, const char* buffer_name = nullptr, usz min_guard_size = 0x10000)
{ {
m_name = const_cast<char*>(buffer_name ? buffer_name : "<unnamed>"); m_name = const_cast<char*>(buffer_name ? buffer_name : "<unnamed>");
@@ -166,4 +168,35 @@ public:
{ {
return m_size; return m_size;
} }
};
// Bulk static allocator. Allows to allocate one large block and subdivide
// [ 0, 1, 2, 3 ] <pad> [ 4, 5, 6, 7 ] ...
template <usz Alignment, usz ElementSize = Alignment>
struct bulk_allocator
{
bulk_allocator(data_heap& container, u32 batch_size = 1)
: m_container(container)
, m_batch_size(batch_size)
{}
usz alloc()
{
if (!m_capacity)
{
m_address = m_container.alloc<Alignment>(ElementSize * m_batch_size);
m_capacity = m_batch_size;
}
m_capacity--;
return std::exchange(m_address, m_address + ElementSize);
}
private:
data_heap& m_container;
usz m_address = 0;
u32 m_capacity = 0;
u32 m_batch_size = 1;
};
};
}
+3 -3
View File
@@ -15,7 +15,7 @@ namespace vk
void data_heap::create(VkBufferUsageFlags usage, usz size, rsx::flags32_t flags, const char* name, usz guard, VkBool32 notify) void data_heap::create(VkBufferUsageFlags usage, usz size, rsx::flags32_t flags, const char* name, usz guard, VkBool32 notify)
{ {
::data_heap::init(size, name, guard); rsx::data_heap::init(size, name, guard);
const auto& memory_map = g_render_device->get_memory_mapping(); const auto& memory_map = g_render_device->get_memory_mapping();
@@ -135,7 +135,7 @@ namespace vk
auto memory_index = m_prefer_writethrough ? memory_map.device_bar : memory_map.host_visible_coherent; auto memory_index = m_prefer_writethrough ? memory_map.device_bar : memory_map.host_visible_coherent;
// Update heap information and reset the allocator // Update heap information and reset the allocator
::data_heap::init(aligned_new_size, m_name, m_min_guard_size); rsx::data_heap::init(aligned_new_size, m_name, m_min_guard_size);
// Discard old heap and create a new one. Old heap will be garbage collected when no longer needed // Discard old heap and create a new one. Old heap will be garbage collected when no longer needed
auto gc = get_resource_manager(); auto gc = get_resource_manager();
@@ -188,7 +188,7 @@ namespace vk
return after_usage < limit; return after_usage < limit;
} }
void* data_heap::map(usz offset, usz size) void* data_heap::map_impl(usz offset, usz size)
{ {
if (!_ptr) if (!_ptr)
{ {
+11 -4
View File
@@ -20,7 +20,7 @@ namespace vk
heap_pool_force_vram_shadow = (1 << 2), heap_pool_force_vram_shadow = (1 << 2),
}; };
class data_heap : public ::data_heap class data_heap : public rsx::data_heap
{ {
private: private:
usz initial_size = 0; usz initial_size = 0;
@@ -41,6 +41,8 @@ namespace vk
bool grow(usz size) override; bool grow(usz size) override;
bool can_allocate_heap(const vk::memory_type_info& target_heap, usz size, int max_usage_percent); bool can_allocate_heap(const vk::memory_type_info& target_heap, usz size, int max_usage_percent);
void* map_impl(usz offset, usz size);
public: public:
std::unique_ptr<buffer> heap; std::unique_ptr<buffer> heap;
@@ -51,9 +53,16 @@ namespace vk
void create(VkBufferUsageFlags usage, usz size, rsx::flags32_t flags, const char* name, usz guard = 0x10000, VkBool32 notify = VK_FALSE); void create(VkBufferUsageFlags usage, usz size, rsx::flags32_t flags, const char* name, usz guard = 0x10000, VkBool32 notify = VK_FALSE);
void destroy(); void destroy();
void* map(usz offset, usz size); template <typename T = void>
T* map(usz offset, usz size)
{
return reinterpret_cast<T*>(map_impl(offset, size));
}
void unmap(bool force = false); void unmap(bool force = false);
void sync(const vk::command_buffer& cmd);
template<int Alignment, typename T = char> template<int Alignment, typename T = char>
requires std::is_trivially_destructible_v<T> requires std::is_trivially_destructible_v<T>
std::pair<usz, T*> alloc_and_map(usz count) std::pair<usz, T*> alloc_and_map(usz count)
@@ -63,8 +72,6 @@ namespace vk
return { addr, reinterpret_cast<T*>(map(addr, size_bytes)) }; return { addr, reinterpret_cast<T*>(map(addr, size_bytes)) };
} }
void sync(const vk::command_buffer& cmd);
template <usz Alignment> template <usz Alignment>
VkDescriptorBufferInfoEx window(usz offset, usz range, u64 window_size) const VkDescriptorBufferInfoEx window(usz offset, usz range, u64 window_size) const
{ {