New resolution on the multirange resolve

This commit is contained in:
CamilleLaVey
2026-09-06 00:10:14 -04:00
parent 49e96351b1
commit cf4e6ea901
8 changed files with 101 additions and 26 deletions
+51 -15
View File
@@ -1004,37 +1004,66 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
} }
template <class P> template <class P>
bool BufferCache<P>::BindMultiRangeStorage(const Binding& binding, bool is_written) { void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding, bool is_written,
std::vector<MultiRangeSegment>& pool) {
binding.segment_first = 0;
binding.segment_count = 0;
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}); }) { if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}); }) {
if (binding.gpu_addr == 0 || binding.size == 0) { if (binding.gpu_addr == 0 || binding.size == 0) {
return false; return;
} }
if (is_written && !runtime.PrefersSparseSources()) { if (is_written && !runtime.PrefersSparseSources()) {
return false; return;
} }
const VirtualSegments* segments = const VirtualSegments* found =
virtual_ranges.Query(*gpu_memory, binding.gpu_addr, binding.size); virtual_ranges.Query(*gpu_memory, binding.gpu_addr, binding.size);
if (!segments || segments->size() < 2) { if (!found || found->size() < 2) {
return false; return;
} }
const u64 key = (static_cast<u64>(gpu_memory->GetID()) << 48) ^ binding.gpu_addr; const VirtualSegments segments = *found;
const u32 first = static_cast<u32>(pool.size());
const bool prefer_sparse = runtime.PrefersSparseSources(); const bool prefer_sparse = runtime.PrefersSparseSources();
runtime.ResetMultiRange(); for (const VirtualSegment& segment : segments) {
for (const VirtualSegment& segment : *segments) {
const BufferId buffer_id = const BufferId buffer_id =
FindBuffer(segment.device_addr, segment.size, prefer_sparse); FindBuffer(segment.device_addr, segment.size, prefer_sparse);
if (!buffer_id) { if (!buffer_id) {
return false; pool.resize(first);
return;
} }
Buffer& buffer = slot_buffers[buffer_id]; pool.push_back(MultiRangeSegment{
TouchBuffer(buffer, buffer_id); .buffer_id = buffer_id,
.device_addr = segment.device_addr,
.size = segment.size,
});
}
binding.segment_first = first;
binding.segment_count = static_cast<u32>(segments.size());
}
}
template <class P>
bool BufferCache<P>::BindMultiRangeStorage(const Binding& binding, bool is_written,
std::span<const MultiRangeSegment> pool) {
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}); }) {
if (binding.segment_count < 2) {
return false;
}
if (binding.segment_first + binding.segment_count > pool.size()) {
return false;
}
const u64 key = (static_cast<u64>(gpu_memory->GetID()) << 48) ^ binding.gpu_addr;
runtime.ResetMultiRange();
for (u32 index = 0; index < binding.segment_count; ++index) {
const MultiRangeSegment& segment = pool[binding.segment_first + index];
Buffer& buffer = slot_buffers[segment.buffer_id];
TouchBuffer(buffer, segment.buffer_id);
if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) { if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) {
runtime.InvalidateMultiRange(key); runtime.InvalidateMultiRange(key);
} }
const u32 offset = buffer.Offset(segment.device_addr); const u32 offset = buffer.Offset(segment.device_addr);
buffer.MarkUsage(offset, segment.size); buffer.MarkUsage(offset, segment.size);
if (is_written) { if (is_written) {
MarkWrittenBuffer(buffer_id, segment.device_addr, segment.size); MarkWrittenBuffer(segment.buffer_id, segment.device_addr, segment.size);
} }
runtime.PushMultiRangeSource(buffer, offset, segment.size); runtime.PushMultiRangeSource(buffer, offset, segment.size);
} }
@@ -1050,7 +1079,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
const Binding& binding = channel_state->storage_buffers[stage][index]; const Binding& binding = channel_state->storage_buffers[stage][index];
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
if (BindMultiRangeStorage(binding, is_written)) { if (BindMultiRangeStorage(binding, is_written, graphics_segments)) {
return; return;
} }
Buffer& buffer = slot_buffers[binding.buffer_id]; Buffer& buffer = slot_buffers[binding.buffer_id];
@@ -1190,7 +1219,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
const Binding& binding = channel_state->compute_storage_buffers[index]; const Binding& binding = channel_state->compute_storage_buffers[index];
const bool is_written = const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0; ((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
if (BindMultiRangeStorage(binding, is_written)) { if (BindMultiRangeStorage(binding, is_written, compute_segments)) {
return; return;
} }
Buffer& buffer = slot_buffers[binding.buffer_id]; Buffer& buffer = slot_buffers[binding.buffer_id];
@@ -1245,6 +1274,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
template <class P> template <class P>
void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
graphics_segments.clear();
BufferOperations([&]() { BufferOperations([&]() {
if (is_indexed) { if (is_indexed) {
UpdateIndexBuffer(); UpdateIndexBuffer();
@@ -1264,6 +1294,7 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
template <class P> template <class P>
void BufferCache<P>::DoUpdateComputeBuffers() { void BufferCache<P>::DoUpdateComputeBuffers() {
compute_segments.clear();
BufferOperations([&]() { BufferOperations([&]() {
UpdateComputeUniformBuffers(); UpdateComputeUniformBuffers();
UpdateComputeStorageBuffers(); UpdateComputeStorageBuffers();
@@ -1406,6 +1437,8 @@ void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
Binding& binding = channel_state->storage_buffers[stage][index]; Binding& binding = channel_state->storage_buffers[stage][index];
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size); const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size);
binding.buffer_id = buffer_id; binding.buffer_id = buffer_id;
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
ResolveMultiRangeStorage(binding, is_written, graphics_segments);
}); });
} }
@@ -1469,6 +1502,9 @@ void BufferCache<P>::UpdateComputeStorageBuffers() {
// Resolve buffer // Resolve buffer
Binding& binding = channel_state->compute_storage_buffers[index]; Binding& binding = channel_state->compute_storage_buffers[index];
binding.buffer_id = FindBuffer(binding.device_addr, binding.size); binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
ResolveMultiRangeStorage(binding, is_written, compute_segments);
}); });
} }
@@ -85,6 +85,14 @@ struct Binding {
u32 size{}; u32 size{};
BufferId buffer_id; BufferId buffer_id;
GPUVAddr gpu_addr{}; GPUVAddr gpu_addr{};
u32 segment_first{};
u32 segment_count{};
};
struct MultiRangeSegment {
BufferId buffer_id;
DAddr device_addr{};
u32 size{};
}; };
struct TextureBufferBinding : Binding { struct TextureBufferBinding : Binding {
@@ -217,7 +225,11 @@ public:
void TickFrame(); void TickFrame();
bool BindMultiRangeStorage(const Binding& binding, bool is_written); bool BindMultiRangeStorage(const Binding& binding, bool is_written,
std::span<const MultiRangeSegment> pool);
void ResolveMultiRangeStorage(Binding& binding, bool is_written,
std::vector<MultiRangeSegment>& pool);
void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
@@ -523,6 +535,8 @@ private:
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
u64 frame_tick = 0; u64 frame_tick = 0;
VirtualRangeCache virtual_ranges; VirtualRangeCache virtual_ranges;
std::vector<MultiRangeSegment> graphics_segments;
std::vector<MultiRangeSegment> compute_segments;
u64 total_used_memory = 0; u64 total_used_memory = 0;
u64 minimum_memory = 0; u64 minimum_memory = 0;
u64 critical_memory = 0; u64 critical_memory = 0;
@@ -43,20 +43,31 @@ public:
entry.gpu_addr = gpu_addr; entry.gpu_addr = gpu_addr;
entry.size = size; entry.size = size;
const auto ranges = memory.GetSubmappedRange(gpu_addr, size); const auto ranges = memory.GetSubmappedRange(gpu_addr, size);
GPUVAddr expected = gpu_addr;
bool contiguous = true;
for (const auto& [range_addr, range_size] : ranges) { for (const auto& [range_addr, range_size] : ranges) {
const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr); if (range_addr != expected || range_size == 0) {
if (!device_addr) { contiguous = false;
break; break;
} }
u32 segment_size = (std::numeric_limits<u32>::max)(); const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr);
if (range_size < static_cast<size_t>(segment_size)) { if (!device_addr || *device_addr == 0) {
segment_size = static_cast<u32>(range_size); contiguous = false;
break;
}
if (range_size > static_cast<size_t>((std::numeric_limits<u32>::max)())) {
contiguous = false;
break;
} }
entry.segments.push_back(VirtualSegment{ entry.segments.push_back(VirtualSegment{
.gpu_addr = range_addr, .gpu_addr = range_addr,
.device_addr = *device_addr, .device_addr = *device_addr,
.size = segment_size, .size = static_cast<u32>(range_size),
}); });
expected += range_size;
}
if (!contiguous || expected != gpu_addr + size) {
entry.segments.clear();
} }
const auto result = entries.insert_or_assign(key, std::move(entry)); const auto result = entries.insert_or_assign(key, std::move(entry));
return &result.first->second.segments; return &result.first->second.segments;
@@ -192,6 +192,7 @@ public:
.memory_offset = location.offset, .memory_offset = location.offset,
.offset = offset, .offset = offset,
.size = size, .size = size,
.memory_type = location.memory_type,
}); });
multi_range_total += size; multi_range_total += size;
} }
@@ -21,11 +21,13 @@ MultiRangeBufferCache::MultiRangeBufferCache(const Device& device_,
if (!device.IsSparseBindingSupported()) { if (!device.IsSparseBindingSupported()) {
return; return;
} }
const VkDeviceSize queried = QueryBlockSize(); u32 memory_type_bits = 0;
if (queried == 0) { const VkDeviceSize queried = QueryBlockSize(memory_type_bits);
if (queried == 0 || memory_type_bits == 0) {
return; return;
} }
block_size = queried; block_size = queried;
sparse_memory_type_bits = memory_type_bits;
use_sparse = true; use_sparse = true;
} }
@@ -44,7 +46,7 @@ MultiRangeBufferCache::~MultiRangeBufferCache() {
retired.clear(); retired.clear();
} }
VkDeviceSize MultiRangeBufferCache::QueryBlockSize() const { VkDeviceSize MultiRangeBufferCache::QueryBlockSize(u32& memory_type_bits) const {
const VkDevice logical = *device.GetLogical(); const VkDevice logical = *device.GetLogical();
const auto& dld = device.GetDispatchLoader(); const auto& dld = device.GetDispatchLoader();
const VkBufferCreateInfo probe_ci{ const VkBufferCreateInfo probe_ci{
@@ -73,6 +75,7 @@ VkDeviceSize MultiRangeBufferCache::QueryBlockSize() const {
}; };
dld.vkGetBufferMemoryRequirements2(logical, &reqs_info, &reqs2); dld.vkGetBufferMemoryRequirements2(logical, &reqs_info, &reqs2);
dld.vkDestroyBuffer(logical, probe, nullptr); dld.vkDestroyBuffer(logical, probe, nullptr);
memory_type_bits = reqs2.memoryRequirements.memoryTypeBits;
return reqs2.memoryRequirements.alignment; return reqs2.memoryRequirements.alignment;
} }
@@ -98,6 +101,12 @@ bool MultiRangeBufferCache::CanBindSparse(std::span<const MultiRangeSource> sour
if (source.memory == VK_NULL_HANDLE) { if (source.memory == VK_NULL_HANDLE) {
return false; return false;
} }
if (source.memory_type >= 32) {
return false;
}
if (((sparse_memory_type_bits >> source.memory_type) & 1) == 0) {
return false;
}
const VkDeviceSize memory_offset = source.memory_offset + source.offset; const VkDeviceSize memory_offset = source.memory_offset + source.offset;
if ((memory_offset % block_size) != 0) { if ((memory_offset % block_size) != 0) {
return false; return false;
@@ -22,6 +22,7 @@ struct MultiRangeSource {
VkDeviceSize memory_offset{}; VkDeviceSize memory_offset{};
VkDeviceSize offset{}; VkDeviceSize offset{};
VkDeviceSize size{}; VkDeviceSize size{};
u32 memory_type{};
}; };
struct MultiRangeRef { struct MultiRangeRef {
@@ -81,7 +82,7 @@ private:
[[nodiscard]] VkBuffer CreateSparse(std::span<const MultiRangeSource> sources, [[nodiscard]] VkBuffer CreateSparse(std::span<const MultiRangeSource> sources,
VkDeviceSize total); VkDeviceSize total);
[[nodiscard]] VkDeviceSize QueryBlockSize() const; [[nodiscard]] VkDeviceSize QueryBlockSize(u32& memory_type_bits) const;
void DestroySparse(VkBuffer handle); void DestroySparse(VkBuffer handle);
@@ -92,6 +93,7 @@ private:
Scheduler& scheduler; Scheduler& scheduler;
bool use_sparse{}; bool use_sparse{};
VkDeviceSize block_size{DEFAULT_BLOCK_SIZE}; VkDeviceSize block_size{DEFAULT_BLOCK_SIZE};
u32 sparse_memory_type_bits{};
VkBufferUsageFlags sparse_usage{}; VkBufferUsageFlags sparse_usage{};
std::unordered_map<u64, Entry> entries; std::unordered_map<u64, Entry> entries;
std::vector<Retired> retired; std::vector<Retired> retired;
@@ -549,6 +549,7 @@ MemoryLocation Buffer::Location() const noexcept {
return MemoryLocation{ return MemoryLocation{
.memory = info.deviceMemory, .memory = info.deviceMemory,
.offset = info.offset, .offset = info.offset,
.memory_type = info.memoryType,
}; };
} }
@@ -744,6 +744,7 @@ private:
struct MemoryLocation { struct MemoryLocation {
VkDeviceMemory memory{}; VkDeviceMemory memory{};
VkDeviceSize offset{}; VkDeviceSize offset{};
u32 memory_type{};
}; };
class Buffer { class Buffer {