From c47f6615d3bda7b3c7d305701ec8af38333aeebf Mon Sep 17 00:00:00 2001 From: Ghost <> Date: Sun, 13 Jul 2025 19:27:39 +0200 Subject: [PATCH] [vk, opengl] Prevent GPU draw call if CBUF binding fails (cbuf0 error handling) (#2) Add defensive checks to cancel draw calls early if any graphics storage buffer (CBUF) fails to bind properly. - Modified BindGraphicsStorageBuffer to return false on invalid buffer ID., - ConfigureImpl (both OpenGL and Vulkan) now propagates binding failure., - Pipeline::Configure returns false if CBUF binding fails., - PrepareDraw cancels rendering if pipeline configuration fails., This avoids undefined GPU behavior, draw corruption, or crashes caused by uninitialized or invalid constant buffer (CBUF0) access, particularly in games with faulty or missing shader bindings. Eden Collaborator: Authored-by: CamilleLaVey Signed-off-by: Bix Co-authored-by: Bix <114880614+Bixbr@users.noreply.github.com> Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2 Co-authored-by: Ghost <> Co-committed-by: Ghost <> --- src/video_core/buffer_cache/buffer_cache.h | 3 ++- .../buffer_cache/buffer_cache_base.h | 2 +- .../renderer_opengl/gl_graphics_pipeline.cpp | 9 +++++++-- .../renderer_opengl/gl_graphics_pipeline.h | 13 ++++++++----- .../renderer_opengl/gl_rasterizer.cpp | 3 ++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 19 ++++++++++++++----- .../renderer_vulkan/vk_graphics_pipeline.h | 13 ++++++++----- .../renderer_vulkan/vk_rasterizer.cpp | 3 ++- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0ce2abc627..a6e87a3583 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -416,7 +416,7 @@ void BufferCache

::UnbindGraphicsStorageBuffers(size_t stage) { } template -void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, +bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written) { channel_state->enabled_storage_buffers[stage] |= 1U << ssbo_index; channel_state->written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index; @@ -425,6 +425,7 @@ void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; channel_state->storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, cbuf_index, is_written); + return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID); } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index d45f595ea8..1c7320dcc1 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -235,7 +235,7 @@ public: void UnbindGraphicsStorageBuffers(size_t stage); - void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, + bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written); void UnbindGraphicsTextureBuffers(size_t stage); diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index af0a453ee7..b4417de703 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -128,7 +131,7 @@ bool Passes(const std::array& stage_infos, u32 enabled_mask) { return true; } -using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool); +using ConfigureFuncPtr = bool (*)(GraphicsPipeline*, bool); template ConfigureFuncPtr FindSpec(const std::array& stage_infos, u32 enabled_mask) { @@ -275,7 +278,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c } template -void GraphicsPipeline::ConfigureImpl(bool is_indexed) { +bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; std::array samplers; size_t views_index{}; @@ -556,6 +559,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if (image_binding != 0) { glBindImageTextures(0, image_binding, images.data()); } + + return true; } void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 2f70c1ae9c..66ab677919 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,8 +83,8 @@ public: const std::array& infos, const GraphicsPipelineKey& key_, bool force_context_flush = false); - void Configure(bool is_indexed) { - configure_func(this, is_indexed); + bool Configure(bool is_indexed) { + return configure_func(this, is_indexed); } void ConfigureTransformFeedback() const { @@ -107,7 +110,7 @@ public: template static auto MakeConfigureSpecFunc() { return [](GraphicsPipeline* pipeline, bool is_indexed) { - pipeline->ConfigureImpl(is_indexed); + return pipeline->ConfigureImpl(is_indexed); }; } @@ -118,7 +121,7 @@ public: private: template - void ConfigureImpl(bool is_indexed); + bool ConfigureImpl(bool is_indexed); void ConfigureTransformFeedbackImpl() const; @@ -134,7 +137,7 @@ private: StateTracker& state_tracker; const GraphicsPipelineKey key; - void (*configure_func)(GraphicsPipeline*, bool){}; + bool (*configure_func)(GraphicsPipeline*, bool){}; std::array source_programs; std::array assembly_programs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4fb193293b..131b7463e0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -250,7 +250,8 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { program_manager.LocalMemoryWarmup(); } pipeline->SetEngine(maxwell3d, gpu_memory); - pipeline->Configure(is_indexed); + if (!pipeline->Configure(is_indexed)) + return; SyncState(); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index e7cec364b6..eb757d68f5 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -175,7 +178,7 @@ bool Passes(const std::array& modules, return true; } -using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool); +using ConfigureFuncPtr = bool (*)(GraphicsPipeline*, bool); template ConfigureFuncPtr FindSpec(const std::array& modules, @@ -302,7 +305,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { } template -void GraphicsPipeline::ConfigureImpl(bool is_indexed) { +bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; std::array samplers; size_t sampler_index{}; @@ -321,8 +324,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { size_t ssbo_index{}; for (const auto& desc : info.storage_buffers_descriptors) { ASSERT(desc.count == 1); - buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index, - desc.cbuf_offset, desc.is_written); + if (!buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index, + desc.cbuf_offset, desc.is_written)) + return false; ++ssbo_index; } } @@ -382,6 +386,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { add_image(desc, desc.is_written); } } + + return true; }}; if constexpr (Spec::enabled_stages[0]) { config_stage(0); @@ -396,7 +402,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { config_stage(3); } if constexpr (Spec::enabled_stages[4]) { - config_stage(4); + if (!config_stage(4)) + return false; } texture_cache.FillGraphicsImageViews(std::span(views.data(), view_index)); @@ -490,6 +497,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { texture_cache.UpdateRenderTargets(false); texture_cache.CheckFeedbackLoop(views); ConfigureDraw(rescaling, render_area); + + return true; } void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 99e56e9ad8..7e9dbb583a 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -86,8 +89,8 @@ public: void AddTransition(GraphicsPipeline* transition); - void Configure(bool is_indexed) { - configure_func(this, is_indexed); + bool Configure(bool is_indexed) { + return configure_func(this, is_indexed); } [[nodiscard]] GraphicsPipeline* Next(const GraphicsPipelineCacheKey& current_key) noexcept { @@ -105,7 +108,7 @@ public: template static auto MakeConfigureSpecFunc() { - return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl(is_indexed); }; + return [](GraphicsPipeline* pl, bool is_indexed) { return pl->ConfigureImpl(is_indexed); }; } void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { @@ -115,7 +118,7 @@ public: private: template - void ConfigureImpl(bool is_indexed); + bool ConfigureImpl(bool is_indexed); void ConfigureDraw(const RescalingPushConstant& rescaling, const RenderAreaPushConstant& render_are); @@ -134,7 +137,7 @@ private: Scheduler& scheduler; GuestDescriptorQueue& guest_descriptor_queue; - void (*configure_func)(GraphicsPipeline*, bool){}; + bool (*configure_func)(GraphicsPipeline*, bool){}; std::vector transition_keys; std::vector transitions; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a2492f1d74..0243693049 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -226,7 +226,8 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); - pipeline->Configure(is_indexed); + if (!pipeline->Configure(is_indexed)) + return; UpdateDynamicStates();