diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 517ed3a58f..492540e60d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -449,9 +449,18 @@ bool BufferCache
::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
@@ -525,8 +534,17 @@ void BufferCache::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
@@ -1002,7 +1020,7 @@ void BufferCache::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
template
void BufferCache::BindHostGraphicsStorageBuffers(size_t stage) {
- boost::container::small_vector segment_sizes;
+ boost::container::small_vector 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::BindHostComputeUniformBuffers() {
template
void BufferCache::BindHostComputeStorageBuffers() {
- boost::container::small_vector segment_sizes;
+ boost::container::small_vector 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::UpdateComputeStorageBuffers() {
template
void BufferCache::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 device_addr = gpu_memory->GpuToCpuAddress(binding.gpu_addr);
if (device_addr) {
binding.segments.push_back(Binding{
@@ -1484,30 +1504,32 @@ void BufferCache::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 device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
- if (!device_addr || size > (std::numeric_limits::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 device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
+ if (!device_addr) {
+ break;
+ }
+ u32 segment_size = (std::numeric_limits::max)();
+ if (size < static_cast(segment_size)) {
+ segment_size = static_cast(size);
+ }
+ binding.segments.push_back(Binding{
+ .device_addr = *device_addr,
+ .size = segment_size,
+ .buffer_id = BufferId{},
+ });
}
- const u32 segment_size = static_cast(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);
}
}
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 62bfc2f1ab..3f42405c5b 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -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 segments;
};
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index b9b136f6b8..19a7d8d254 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -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(gpu_addr, size, page_stash);
for (const auto& [map_addr, map_size] : page_stash) {
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index f9fddd177a..82f0f1cddd 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -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 mapping_generation{1};
+
enum class EntryType : u64 {
Free = 0,
Reserved = 1,
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_buffer.h b/src/video_core/renderer_vulkan/vk_descriptor_buffer.h
index 6f96bcc326..5c1bd3a486 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_descriptor_buffer.h
@@ -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:
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 6d11d9a444..7df7bba61e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -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);
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 2e759eb781..45d5ab2c73 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -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(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;
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index 9f30e65dbd..b530734748 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -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_,