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>
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 (binding.gpu_addr == 0 || binding.size == 0) {
return false;
return;
}
if (is_written && !runtime.PrefersSparseSources()) {
return false;
return;
}
const VirtualSegments* segments =
const VirtualSegments* found =
virtual_ranges.Query(*gpu_memory, binding.gpu_addr, binding.size);
if (!segments || segments->size() < 2) {
return false;
if (!found || found->size() < 2) {
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();
runtime.ResetMultiRange();
for (const VirtualSegment& segment : *segments) {
for (const VirtualSegment& segment : segments) {
const BufferId buffer_id =
FindBuffer(segment.device_addr, segment.size, prefer_sparse);
if (!buffer_id) {
return false;
pool.resize(first);
return;
}
Buffer& buffer = slot_buffers[buffer_id];
TouchBuffer(buffer, buffer_id);
pool.push_back(MultiRangeSegment{
.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)) {
runtime.InvalidateMultiRange(key);
}
const u32 offset = buffer.Offset(segment.device_addr);
buffer.MarkUsage(offset, segment.size);
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);
}
@@ -1050,7 +1079,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
const Binding& binding = channel_state->storage_buffers[stage][index];
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;
}
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 bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
if (BindMultiRangeStorage(binding, is_written)) {
if (BindMultiRangeStorage(binding, is_written, compute_segments)) {
return;
}
Buffer& buffer = slot_buffers[binding.buffer_id];
@@ -1245,6 +1274,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
template <class P>
void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
graphics_segments.clear();
BufferOperations([&]() {
if (is_indexed) {
UpdateIndexBuffer();
@@ -1264,6 +1294,7 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
template <class P>
void BufferCache<P>::DoUpdateComputeBuffers() {
compute_segments.clear();
BufferOperations([&]() {
UpdateComputeUniformBuffers();
UpdateComputeStorageBuffers();
@@ -1406,6 +1437,8 @@ void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
Binding& binding = channel_state->storage_buffers[stage][index];
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size);
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
Binding& binding = channel_state->compute_storage_buffers[index];
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{};
BufferId buffer_id;
GPUVAddr gpu_addr{};
u32 segment_first{};
u32 segment_count{};
};
struct MultiRangeSegment {
BufferId buffer_id;
DAddr device_addr{};
u32 size{};
};
struct TextureBufferBinding : Binding {
@@ -217,7 +225,11 @@ public:
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);
@@ -523,6 +535,8 @@ private:
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
u64 frame_tick = 0;
VirtualRangeCache virtual_ranges;
std::vector<MultiRangeSegment> graphics_segments;
std::vector<MultiRangeSegment> compute_segments;
u64 total_used_memory = 0;
u64 minimum_memory = 0;
u64 critical_memory = 0;
@@ -43,20 +43,31 @@ public:
entry.gpu_addr = gpu_addr;
entry.size = size;
const auto ranges = memory.GetSubmappedRange(gpu_addr, size);
GPUVAddr expected = gpu_addr;
bool contiguous = true;
for (const auto& [range_addr, range_size] : ranges) {
const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr);
if (!device_addr) {
if (range_addr != expected || range_size == 0) {
contiguous = false;
break;
}
u32 segment_size = (std::numeric_limits<u32>::max)();
if (range_size < static_cast<size_t>(segment_size)) {
segment_size = static_cast<u32>(range_size);
const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr);
if (!device_addr || *device_addr == 0) {
contiguous = false;
break;
}
if (range_size > static_cast<size_t>((std::numeric_limits<u32>::max)())) {
contiguous = false;
break;
}
entry.segments.push_back(VirtualSegment{
.gpu_addr = range_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));
return &result.first->second.segments;
@@ -192,6 +192,7 @@ public:
.memory_offset = location.offset,
.offset = offset,
.size = size,
.memory_type = location.memory_type,
});
multi_range_total += size;
}
@@ -21,11 +21,13 @@ MultiRangeBufferCache::MultiRangeBufferCache(const Device& device_,
if (!device.IsSparseBindingSupported()) {
return;
}
const VkDeviceSize queried = QueryBlockSize();
if (queried == 0) {
u32 memory_type_bits = 0;
const VkDeviceSize queried = QueryBlockSize(memory_type_bits);
if (queried == 0 || memory_type_bits == 0) {
return;
}
block_size = queried;
sparse_memory_type_bits = memory_type_bits;
use_sparse = true;
}
@@ -44,7 +46,7 @@ MultiRangeBufferCache::~MultiRangeBufferCache() {
retired.clear();
}
VkDeviceSize MultiRangeBufferCache::QueryBlockSize() const {
VkDeviceSize MultiRangeBufferCache::QueryBlockSize(u32& memory_type_bits) const {
const VkDevice logical = *device.GetLogical();
const auto& dld = device.GetDispatchLoader();
const VkBufferCreateInfo probe_ci{
@@ -73,6 +75,7 @@ VkDeviceSize MultiRangeBufferCache::QueryBlockSize() const {
};
dld.vkGetBufferMemoryRequirements2(logical, &reqs_info, &reqs2);
dld.vkDestroyBuffer(logical, probe, nullptr);
memory_type_bits = reqs2.memoryRequirements.memoryTypeBits;
return reqs2.memoryRequirements.alignment;
}
@@ -98,6 +101,12 @@ bool MultiRangeBufferCache::CanBindSparse(std::span<const MultiRangeSource> sour
if (source.memory == VK_NULL_HANDLE) {
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;
if ((memory_offset % block_size) != 0) {
return false;
@@ -22,6 +22,7 @@ struct MultiRangeSource {
VkDeviceSize memory_offset{};
VkDeviceSize offset{};
VkDeviceSize size{};
u32 memory_type{};
};
struct MultiRangeRef {
@@ -81,7 +82,7 @@ private:
[[nodiscard]] VkBuffer CreateSparse(std::span<const MultiRangeSource> sources,
VkDeviceSize total);
[[nodiscard]] VkDeviceSize QueryBlockSize() const;
[[nodiscard]] VkDeviceSize QueryBlockSize(u32& memory_type_bits) const;
void DestroySparse(VkBuffer handle);
@@ -92,6 +93,7 @@ private:
Scheduler& scheduler;
bool use_sparse{};
VkDeviceSize block_size{DEFAULT_BLOCK_SIZE};
u32 sparse_memory_type_bits{};
VkBufferUsageFlags sparse_usage{};
std::unordered_map<u64, Entry> entries;
std::vector<Retired> retired;
@@ -549,6 +549,7 @@ MemoryLocation Buffer::Location() const noexcept {
return MemoryLocation{
.memory = info.deviceMemory,
.offset = info.offset,
.memory_type = info.memoryType,
};
}
@@ -744,6 +744,7 @@ private:
struct MemoryLocation {
VkDeviceMemory memory{};
VkDeviceSize offset{};
u32 memory_type{};
};
class Buffer {