Adjustments based on previous handlings for descriptors usage

This commit is contained in:
CamilleLaVey
2026-09-05 13:23:21 -04:00
parent c246f02697
commit 4e33c74895
8 changed files with 69 additions and 43 deletions
+53 -31
View File
@@ -449,9 +449,18 @@ bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
const auto& cbufs = maxwell3d->state.shader_stages[stage];
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
channel_state->storage_buffers[stage][ssbo_index] =
StorageBufferBindingInfo& slot = channel_state->storage_buffers[stage][ssbo_index];
const StorageBufferBindingInfo binding =
StorageBufferBinding(ssbo_addr, cbuf_index, is_written, descriptor_count);
return channel_state->storage_buffers[stage][ssbo_index].gpu_addr != 0;
if (slot.gpu_addr != binding.gpu_addr || slot.size != binding.size ||
slot.descriptor_count != binding.descriptor_count) {
slot.gpu_addr = binding.gpu_addr;
slot.size = binding.size;
slot.descriptor_count = binding.descriptor_count;
slot.mapping_generation = 0;
slot.segments.clear();
}
return slot.gpu_addr != 0;
}
template <class P>
@@ -525,8 +534,17 @@ void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index,
const auto& cbufs = launch_desc.const_buffer_config;
const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset;
channel_state->compute_storage_buffers[ssbo_index] =
StorageBufferBindingInfo& slot = channel_state->compute_storage_buffers[ssbo_index];
const StorageBufferBindingInfo binding =
StorageBufferBinding(ssbo_addr, cbuf_index, is_written, descriptor_count);
if (slot.gpu_addr != binding.gpu_addr || slot.size != binding.size ||
slot.descriptor_count != binding.descriptor_count) {
slot.gpu_addr = binding.gpu_addr;
slot.size = binding.size;
slot.descriptor_count = binding.descriptor_count;
slot.mapping_generation = 0;
slot.segments.clear();
}
}
template <class P>
@@ -1002,7 +1020,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
template <class P>
void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
boost::container::small_vector<u32, NUM_STORAGE_BUFFERS> segment_sizes;
boost::container::small_vector<u32, NUM_STORAGE_BUFFERS * NUM_STORAGE_BUFFER_SEGMENTS> segment_sizes;
bool uses_mapping{};
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
const StorageBufferBindingInfo& binding = channel_state->storage_buffers[stage][index];
@@ -1163,7 +1181,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
template <class P>
void BufferCache<P>::BindHostComputeStorageBuffers() {
boost::container::small_vector<u32, NUM_STORAGE_BUFFERS> segment_sizes;
boost::container::small_vector<u32, NUM_STORAGE_BUFFERS * NUM_STORAGE_BUFFER_SEGMENTS> segment_sizes;
bool uses_mapping{};
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
const StorageBufferBindingInfo& binding = channel_state->compute_storage_buffers[index];
@@ -1469,10 +1487,12 @@ void BufferCache<P>::UpdateComputeStorageBuffers() {
template <class P>
void BufferCache<P>::UpdateStorageBuffer(StorageBufferBindingInfo& binding) {
binding.segments.clear();
if (binding.gpu_addr == 0 || binding.size == 0) { return;}
if (binding.gpu_addr == 0 || binding.size == 0) {
binding.segments.clear();
return;
}
if (binding.descriptor_count == 1) {
// for safety gotta preserve the legacy path on possible hosts without storage-buffer descriptor indexing.
binding.segments.clear();
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(binding.gpu_addr);
if (device_addr) {
binding.segments.push_back(Binding{
@@ -1484,30 +1504,32 @@ void BufferCache<P>::UpdateStorageBuffer(StorageBufferBindingInfo& binding) {
return;
}
const auto ranges = gpu_memory->GetSubmappedRange(binding.gpu_addr, binding.size);
const size_t mapped_size =
std::accumulate(ranges.begin(), ranges.end(), size_t{}, [](size_t total, const auto& range) { return total + range.second; });
if (mapped_size != binding.size) {
LOG_ERROR(HW_GPU, "Storage buffer range {:#x}+{:#x} is not fully mapped", binding.gpu_addr, binding.size);
return;
}
if (ranges.size() > binding.descriptor_count) {
LOG_ERROR(HW_GPU, "Storage buffer range {:#x}+{:#x} has {} physical segments, exceeding host capacity {}",
binding.gpu_addr, binding.size, ranges.size(), binding.descriptor_count);
return;
}
for (const auto& [gpu_addr, size] : ranges) {
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!device_addr || size > (std::numeric_limits<u32>::max)()) {
binding.segments.clear();
return;
const u64 generation = gpu_memory->MappingGeneration();
if (binding.mapping_generation != generation || binding.segments.empty()) {
binding.mapping_generation = generation;
binding.segments.clear();
const auto ranges = gpu_memory->GetSubmappedRange(binding.gpu_addr, binding.size);
for (const auto& [gpu_addr, size] : ranges) {
if (binding.segments.size() >= binding.descriptor_count) {
break;
}
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!device_addr) {
break;
}
u32 segment_size = (std::numeric_limits<u32>::max)();
if (size < static_cast<size_t>(segment_size)) {
segment_size = static_cast<u32>(size);
}
binding.segments.push_back(Binding{
.device_addr = *device_addr,
.size = segment_size,
.buffer_id = BufferId{},
});
}
const u32 segment_size = static_cast<u32>(size);
binding.segments.push_back(Binding{
.device_addr = *device_addr,
.size = segment_size,
.buffer_id = FindBuffer(*device_addr, segment_size),
});
}
for (Binding& segment : binding.segments) {
segment.buffer_id = FindBuffer(segment.device_addr, segment.size);
}
}
@@ -54,7 +54,8 @@ constexpr u32 NUM_VERTEX_BUFFERS = 32;
constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
constexpr u32 NUM_STORAGE_BUFFERS = 16;
constexpr u32 NUM_STORAGE_BUFFERS = 32;
constexpr u32 NUM_STORAGE_BUFFER_SEGMENTS = 8;
constexpr u32 NUM_TEXTURE_BUFFERS = 32;
constexpr u32 NUM_STAGES = 5;
@@ -90,11 +91,10 @@ struct TextureBufferBinding : Binding {
};
struct StorageBufferBindingInfo {
// another good one: guest SSBO is a virtual interval and may span discontiguous device-memory ranges.
// exact case of missing character frames (high sample lane)
GPUVAddr gpu_addr{};
u32 size{};
u32 descriptor_count{1};
u64 mapping_generation{};
boost::container::small_vector<Binding, 1> segments;
};
+3
View File
@@ -176,12 +176,14 @@ void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
}
GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, DAddr dev_addr, std::size_t size, PTEKind kind, bool is_big_pages) {
mapping_generation.fetch_add(1, std::memory_order_release);
if (is_big_pages)
return BigPageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped);
return PageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped);
}
GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) {
mapping_generation.fetch_add(1, std::memory_order_release);
if (is_big_pages)
return BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved);
return PageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved);
@@ -191,6 +193,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
if (size == 0) {
return;
}
mapping_generation.fetch_add(1, std::memory_order_release);
GetSubmappedRangeImpl<false>(gpu_addr, size, page_stash);
for (const auto& [map_addr, map_size] : page_stash) {
+6
View File
@@ -145,6 +145,10 @@ public:
return gpu_addr < address_space_size;
}
u64 MappingGeneration() const noexcept {
return mapping_generation.load(std::memory_order_acquire);
}
PTEKind GetPageKind(GPUVAddr gpu_addr) const;
size_t GetMemoryLayoutSize(GPUVAddr gpu_addr,
@@ -197,6 +201,8 @@ private:
VideoCore::RasterizerInterface* rasterizer = nullptr;
std::atomic<u64> mapping_generation{1};
enum class EntryType : u64 {
Free = 0,
Reserved = 1,
@@ -18,7 +18,7 @@ class Scheduler;
class DescriptorBufferRing final {
static constexpr size_t FRAMES_IN_FLIGHT = 8;
static constexpr VkDeviceSize TILER_FRAME_SIZE = 2 * 1024 * 1024;
static constexpr VkDeviceSize TILER_FRAME_SIZE = 8 * 1024 * 1024;
static constexpr VkDeviceSize DESKTOP_FRAME_SIZE = 4 * 1024 * 1024;
public:
@@ -315,7 +315,7 @@ GraphicsPipeline::GraphicsPipeline(
try {
MakePipeline(render_pass);
} catch (const vk::Exception& exception) {
LOG_CRITICAL(Render_Vulkan, "Graphics pipeline build failed: {}", exception.what());
LOG_DEBUG(Render_Vulkan, "Graphics pipeline build failed: {}", exception.what());
std::scoped_lock lock{build_mutex};
is_built = true;
build_condvar.notify_one();
@@ -581,7 +581,6 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
const DescriptorBufferRing::Allocation alloc{
descriptor_buffer_ring.Allocate(scheduler, descriptor_buffer_layout.size)};
if (!alloc.host) {
LOG_DEBUG(Render_Vulkan, "Failed to reserve descriptor memory, skipping draw");
return false;
}
WriteDescriptorBuffer(device, descriptor_buffer_layout, entries, alloc.host);
@@ -62,11 +62,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
// SPIR-V descriptor arrays require a fixed pipeline-layout count.
// Exploration ceiling; buffer-cache telemetry records the actual physical-range demand.
// Keep this modest because every mapped SSBO binds the full fixed array on each update.
const u32 MAX_MAPPED_STORAGE_BUFFER_DESCRIPTORS =
(std::max)(6u, static_cast<u32>(Settings::values.debug_knobs.GetValue()));
constexpr u32 MAX_MAPPED_STORAGE_BUFFER_DESCRIPTORS = 8;
constexpr u32 CACHE_VERSION = 19;
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
@@ -40,7 +40,7 @@ class UpdateDescriptorQueue final {
static constexpr size_t FRAMES_IN_FLIGHT = 8;
public:
static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x80000;
static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x40000;
static constexpr size_t COMPUTE_FRAME_PAYLOAD_SIZE = 0x20000;
explicit UpdateDescriptorQueue(const Device& device_, size_t frame_payload_size_,