revert fix(vulkan): Async Presentation & Shader Logic
- Fix race condition in PipelineCache and ShaderPools using std::mutex.
- Fix stutter in Asynchronous Shader Building by properly returning nullptr for unbuilt pipelines (pop-in behavior).
- Modify AcquireNextImage to use MasterSemaphore for safer async presentation.

Needs more testing.
This commit is contained in:
Collecting
2026-02-12 03:54:36 +01:00
parent fa107b7581
commit 0ec9aceccf
7 changed files with 49 additions and 61 deletions

View File

@@ -25,14 +25,14 @@ using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
using Tegra::Texture::TexturePair; using Tegra::Texture::TexturePair;
ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_, ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_,
std::mutex& pipeline_cache_mutex_, DescriptorPool& descriptor_pool, DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue_, GuestDescriptorQueue& guest_descriptor_queue_,
Common::ThreadWorker* thread_worker, Common::ThreadWorker* thread_worker,
PipelineStatistics* pipeline_statistics, PipelineStatistics* pipeline_statistics,
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
vk::ShaderModule spv_module_) vk::ShaderModule spv_module_)
: device{device_}, pipeline_cache(pipeline_cache_), pipeline_cache_mutex(pipeline_cache_mutex_), : device{device_},
guest_descriptor_queue{guest_descriptor_queue_}, info{info_}, pipeline_cache(pipeline_cache_), guest_descriptor_queue{guest_descriptor_queue_}, info{info_},
spv_module(std::move(spv_module_)) { spv_module(std::move(spv_module_)) {
if (shader_notify) { if (shader_notify) {
shader_notify->MarkShaderBuilding(); shader_notify->MarkShaderBuilding();
@@ -58,7 +58,6 @@ ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipel
if (device.IsKhrPipelineExecutablePropertiesEnabled()) { if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
} }
std::scoped_lock cache_lock{pipeline_cache_mutex};
pipeline = device.GetLogical().CreateComputePipeline( pipeline = device.GetLogical().CreateComputePipeline(
{ {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,

View File

@@ -3,12 +3,10 @@
#pragma once #pragma once
#include <array>
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
#include "shader_recompiler/shader_info.h" #include "shader_recompiler/shader_info.h"
@@ -31,7 +29,7 @@ class Scheduler;
class ComputePipeline { class ComputePipeline {
public: public:
explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache, explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache,
std::mutex& pipeline_cache_mutex, DescriptorPool& descriptor_pool, DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue, GuestDescriptorQueue& guest_descriptor_queue,
Common::ThreadWorker* thread_worker, Common::ThreadWorker* thread_worker,
PipelineStatistics* pipeline_statistics, PipelineStatistics* pipeline_statistics,
@@ -50,7 +48,6 @@ public:
private: private:
const Device& device; const Device& device;
vk::PipelineCache& pipeline_cache; vk::PipelineCache& pipeline_cache;
std::mutex& pipeline_cache_mutex;
GuestDescriptorQueue& guest_descriptor_queue; GuestDescriptorQueue& guest_descriptor_queue;
Shader::Info info; Shader::Info info;

View File

@@ -88,8 +88,7 @@ bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) {
bool IsLine(VkPrimitiveTopology topology) { bool IsLine(VkPrimitiveTopology topology) {
static constexpr std::array line_topologies{ static constexpr std::array line_topologies{
VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
// VK_PRIMITIVE_TOPOLOGY_LINE_LOOP_EXT, // VK_PRIMITIVE_TOPOLOGY_LINE_LOOP_EXT,
}; };
return std::ranges::find(line_topologies, topology) == line_topologies.end(); return std::ranges::find(line_topologies, topology) == line_topologies.end();
@@ -238,16 +237,15 @@ ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& m
GraphicsPipeline::GraphicsPipeline( GraphicsPipeline::GraphicsPipeline(
Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
vk::PipelineCache& pipeline_cache_, std::mutex& pipeline_cache_mutex_, vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify,
VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, const Device& device_, DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue_, Common::ThreadWorker* worker_thread, GuestDescriptorQueue& guest_descriptor_queue_, Common::ThreadWorker* worker_thread,
PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos) const std::array<const Shader::Info*, NUM_STAGES>& infos)
: key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
pipeline_cache(pipeline_cache_), pipeline_cache_mutex(pipeline_cache_mutex_), pipeline_cache(pipeline_cache_), scheduler{scheduler_},
scheduler{scheduler_}, guest_descriptor_queue{guest_descriptor_queue_}, guest_descriptor_queue{guest_descriptor_queue_}, spv_modules{std::move(stages)} {
spv_modules{std::move(stages)} {
if (shader_notify) { if (shader_notify) {
shader_notify->MarkShaderBuilding(); shader_notify->MarkShaderBuilding();
} }
@@ -927,7 +925,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (device.IsKhrPipelineExecutablePropertiesEnabled()) { if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
} }
std::scoped_lock lock{pipeline_cache_mutex};
pipeline = device.GetLogical().CreateGraphicsPipeline( pipeline = device.GetLogical().CreateGraphicsPipeline(
{ {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,

View File

@@ -71,12 +71,11 @@ class GraphicsPipeline {
public: public:
explicit GraphicsPipeline( explicit GraphicsPipeline(
Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
vk::PipelineCache& pipeline_cache, std::mutex& pipeline_cache_mutex, vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify,
VideoCore::ShaderNotify* shader_notify, const Device& device, const Device& device, DescriptorPool& descriptor_pool,
DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, GuestDescriptorQueue& guest_descriptor_queue, Common::ThreadWorker* worker_thread,
Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos); const std::array<const Shader::Info*, NUM_STAGES>& infos);
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
@@ -132,7 +131,6 @@ private:
TextureCache& texture_cache; TextureCache& texture_cache;
BufferCache& buffer_cache; BufferCache& buffer_cache;
vk::PipelineCache& pipeline_cache; vk::PipelineCache& pipeline_cache;
std::mutex& pipeline_cache_mutex;
Scheduler& scheduler; Scheduler& scheduler;
GuestDescriptorQueue& guest_descriptor_queue; GuestDescriptorQueue& guest_descriptor_queue;

View File

@@ -677,6 +677,11 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
if (pipeline->IsBuilt()) { if (pipeline->IsBuilt()) {
return pipeline; return pipeline;
} }
if (!use_asynchronous_shaders) {
return pipeline;
}
// When asynchronous shaders are enabled, avoid blocking the main thread completely.
// Skip the draw until the pipeline is ready to prevent stutter.
return nullptr; return nullptr;
} }
@@ -764,11 +769,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
previous_stage = &program; previous_stage = &program;
} }
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
auto pipeline{std::make_unique<GraphicsPipeline>( return std::make_unique<GraphicsPipeline>(
scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, pipeline_cache_mutex, scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device,
&shader_notify, device, descriptor_pool, guest_descriptor_queue, thread_worker, statistics, descriptor_pool, guest_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
render_pass_cache, key, std::move(modules), infos)}; std::move(modules), infos);
return pipeline;
} catch (const vk::Exception& exception) { } catch (const vk::Exception& exception) {
if (exception.GetResult() == VK_ERROR_OUT_OF_DEVICE_MEMORY) { if (exception.GetResult() == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
@@ -800,7 +804,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
GraphicsEnvironments environments; GraphicsEnvironments environments;
GetGraphicsEnvironments(environments, graphics_key.unique_hashes); GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
std::scoped_lock lock{pools_mutex};
main_pools.ReleaseContents(); main_pools.ReleaseContents();
auto pipeline{ auto pipeline{
CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)}; CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
@@ -827,7 +830,6 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
env.SetCachedSize(shader->size_bytes); env.SetCachedSize(shader->size_bytes);
std::scoped_lock lock{pools_mutex};
main_pools.ReleaseContents(); main_pools.ReleaseContents();
auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)}; auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
if (!pipeline || pipeline_cache_filename.empty()) { if (!pipeline || pipeline_cache_filename.empty()) {
@@ -872,10 +874,9 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
spv_module.SetObjectNameEXT(name.c_str()); spv_module.SetObjectNameEXT(name.c_str());
} }
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, pipeline_cache_mutex, return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, descriptor_pool,
descriptor_pool, guest_descriptor_queue, thread_worker, guest_descriptor_queue, thread_worker, statistics,
statistics, &shader_notify, program.info, &shader_notify, program.info, std::move(spv_module));
std::move(spv_module));
} catch (const vk::Exception& exception) { } catch (const vk::Exception& exception) {
if (exception.GetResult() == VK_ERROR_OUT_OF_DEVICE_MEMORY) { if (exception.GetResult() == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
@@ -903,7 +904,6 @@ void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& fi
file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size()) file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size())
.write(reinterpret_cast<const char*>(&cache_version), sizeof(cache_version)); .write(reinterpret_cast<const char*>(&cache_version), sizeof(cache_version));
std::scoped_lock lock{pipeline_cache_mutex};
size_t cache_size = 0; size_t cache_size = 0;
std::vector<char> cache_data; std::vector<char> cache_data;
if (pipeline_cache) { if (pipeline_cache) {

View File

@@ -8,7 +8,6 @@
#include <cstddef> #include <cstddef>
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <mutex>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@@ -177,8 +176,6 @@ public:
static constexpr u64 MEMORY_PRESSURE_COOLDOWN = 300; static constexpr u64 MEMORY_PRESSURE_COOLDOWN = 300;
ShaderPools main_pools; ShaderPools main_pools;
std::mutex pools_mutex;
std::mutex pipeline_cache_mutex;
Shader::Profile profile; Shader::Profile profile;
Shader::HostTranslateInfo host_info; Shader::HostTranslateInfo host_info;

View File

@@ -64,35 +64,35 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
return mode; return mode;
} }
switch (mode) { switch (mode) {
case Settings::VSyncMode::Fifo: case Settings::VSyncMode::Fifo:
case Settings::VSyncMode::FifoRelaxed: case Settings::VSyncMode::FifoRelaxed:
if (has_mailbox) { if (has_mailbox) {
return Settings::VSyncMode::Mailbox; return Settings::VSyncMode::Mailbox;
} else if (has_imm) { } else if (has_imm) {
return Settings::VSyncMode::Immediate; return Settings::VSyncMode::Immediate;
} }
[[fallthrough]]; [[fallthrough]];
default: default:
return mode; return mode;
} }
}(); }();
if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) || if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
(setting == Settings::VSyncMode::Immediate && !has_imm) || (setting == Settings::VSyncMode::Immediate && !has_imm) ||
(setting == Settings::VSyncMode::FifoRelaxed && !has_fifo_relaxed)) { (setting == Settings::VSyncMode::FifoRelaxed && !has_fifo_relaxed)) {
setting = Settings::VSyncMode::Fifo; setting = Settings::VSyncMode::Fifo;
} }
switch (setting) { switch (setting) {
case Settings::VSyncMode::Immediate: case Settings::VSyncMode::Immediate:
return VK_PRESENT_MODE_IMMEDIATE_KHR; return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox: case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR; return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::Fifo: case Settings::VSyncMode::Fifo:
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FifoRelaxed: case Settings::VSyncMode::FifoRelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR; return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default: default:
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;
} }
} }
@@ -174,7 +174,7 @@ bool Swapchain::AcquireNextImage() {
break; break;
} }
scheduler.GetMasterSemaphore().Wait(resource_ticks[image_index]); scheduler.Wait(resource_ticks[image_index]);
resource_ticks[image_index] = scheduler.CurrentTick(); resource_ticks[image_index] = scheduler.CurrentTick();
return is_suboptimal || is_outdated; return is_suboptimal || is_outdated;