Fix deadlock on settings in PresentationEngine after callback

Accessing the settings class during the execution of the `OnChangedCallback` results in a deadlock, as accesses to values are protected by a mutex. Instead, we now keep a local copy of the relevant settings and update those with the new value.
This commit is contained in:
lynxnb 2023-02-07 13:11:04 +01:00 committed by Billy Laws
parent fc9b34846c
commit dba191d2dc
2 changed files with 13 additions and 6 deletions

View File

@ -29,7 +29,9 @@ namespace skyline::gpu {
presentationTrack{static_cast<u64>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()}, presentationTrack{static_cast<u64>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()},
vsyncEvent{std::make_shared<kernel::type::KEvent>(state, true)}, vsyncEvent{std::make_shared<kernel::type::KEvent>(state, true)},
choreographerThread{&PresentationEngine::ChoreographerThread, this}, choreographerThread{&PresentationEngine::ChoreographerThread, this},
presentationThread{&PresentationEngine::PresentationThread, this} { presentationThread{&PresentationEngine::PresentationThread, this},
forceTripleBuffering{*state.settings->forceTripleBuffering},
disableFrameThrottling{*state.settings->disableFrameThrottling} {
auto desc{presentationTrack.Serialize()}; auto desc{presentationTrack.Serialize()};
desc.set_name("Presentation"); desc.set_name("Presentation");
perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc); perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc);
@ -203,7 +205,7 @@ namespace skyline::gpu {
}); // We don't care about suboptimal images as they are caused by not respecting the transform hint, we handle transformations externally }); // We don't care about suboptimal images as they are caused by not respecting the transform hint, we handle transformations externally
} }
timestamp = (timestamp && !*state.settings->disableFrameThrottling) ? timestamp : getMonotonicNsNow(); // We tie FPS to the submission time rather than presentation timestamp, if we don't have the presentation timestamp available or if frame throttling is disabled as we want the maximum measured FPS to not be restricted to the refresh rate timestamp = (timestamp && !disableFrameThrottling) ? timestamp : getMonotonicNsNow(); // We tie FPS to the submission time rather than presentation timestamp, if we don't have the presentation timestamp available or if frame throttling is disabled as we want the maximum measured FPS to not be restricted to the refresh rate
if (frameTimestamp) { if (frameTimestamp) {
i64 sampleWeight{Fps ? Fps : 1}; //!< The weight of each sample in calculating the average, we want to roughly average the past second i64 sampleWeight{Fps ? Fps : 1}; //!< The weight of each sample in calculating the average, we want to roughly average the past second
@ -281,7 +283,7 @@ namespace skyline::gpu {
} }
void PresentationEngine::UpdateSwapchain(texture::Format format, texture::Dimensions extent) { void PresentationEngine::UpdateSwapchain(texture::Format format, texture::Dimensions extent) {
auto minImageCount{std::max(vkSurfaceCapabilities.minImageCount, *state.settings->forceTripleBuffering ? 3U : 2U)}; auto minImageCount{std::max(vkSurfaceCapabilities.minImageCount, forceTripleBuffering ? 3U : 2U)};
if (minImageCount > MaxSwapchainImageCount) if (minImageCount > MaxSwapchainImageCount)
throw exception("Requesting swapchain with higher image count ({}) than maximum slot count ({})", minImageCount, MaxSwapchainImageCount); throw exception("Requesting swapchain with higher image count ({}) than maximum slot count ({})", minImageCount, MaxSwapchainImageCount);
@ -305,7 +307,7 @@ namespace skyline::gpu {
if ((capabilities.supportedUsageFlags & presentUsage) != presentUsage) if ((capabilities.supportedUsageFlags & presentUsage) != presentUsage)
throw exception("Swapchain doesn't support image usage '{}': {}", vk::to_string(presentUsage), vk::to_string(capabilities.supportedUsageFlags)); throw exception("Swapchain doesn't support image usage '{}': {}", vk::to_string(presentUsage), vk::to_string(capabilities.supportedUsageFlags));
auto requestedMode{*state.settings->disableFrameThrottling ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo}; auto requestedMode{disableFrameThrottling ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo};
auto modes{gpu.vkPhysicalDevice.getSurfacePresentModesKHR(**vkSurface)}; auto modes{gpu.vkPhysicalDevice.getSurfacePresentModesKHR(**vkSurface)};
if (std::find(modes.begin(), modes.end(), requestedMode) == modes.end()) if (std::find(modes.begin(), modes.end(), requestedMode) == modes.end())
throw exception("Swapchain doesn't support present mode: {}", vk::to_string(requestedMode)); throw exception("Swapchain doesn't support present mode: {}", vk::to_string(requestedMode));
@ -342,9 +344,11 @@ namespace skyline::gpu {
swapchainImageCount = vkImages.size(); swapchainImageCount = vkImages.size();
} }
void PresentationEngine::OnDisableFrameThrottlingChanged(const bool &value) { void PresentationEngine::OnDisableFrameThrottlingChanged(const bool value) {
std::scoped_lock guard{mutex}; std::scoped_lock guard{mutex};
disableFrameThrottling = value;
if (vkSurface && swapchainExtent && swapchainFormat) if (vkSurface && swapchainExtent && swapchainFormat)
UpdateSwapchain(swapchainFormat, swapchainExtent); UpdateSwapchain(swapchainFormat, swapchainExtent);
} }

View File

@ -46,6 +46,9 @@ namespace skyline::gpu {
size_t frameIndex{}; //!< The index of the next semaphore/fence to be used for acquiring swapchain images size_t frameIndex{}; //!< The index of the next semaphore/fence to be used for acquiring swapchain images
size_t swapchainImageCount{}; //!< The number of images in the current swapchain size_t swapchainImageCount{}; //!< The number of images in the current swapchain
bool forceTripleBuffering{}; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering
bool disableFrameThrottling{}; //!< Allow the guest to submit frames without any blocking calls
i64 frameTimestamp{}; //!< The timestamp of the last frame being shown in nanoseconds i64 frameTimestamp{}; //!< The timestamp of the last frame being shown in nanoseconds
i64 averageFrametimeNs{}; //!< The average time between frames in nanoseconds i64 averageFrametimeNs{}; //!< The average time between frames in nanoseconds
i64 averageFrametimeDeviationNs{}; //!< The average deviation of frametimes in nanoseconds i64 averageFrametimeDeviationNs{}; //!< The average deviation of frametimes in nanoseconds
@ -108,7 +111,7 @@ namespace skyline::gpu {
/** /**
* @brief Handles DisableFrameThrottling setting changed event * @brief Handles DisableFrameThrottling setting changed event
*/ */
void OnDisableFrameThrottlingChanged(const bool &value); void OnDisableFrameThrottlingChanged(const bool value);
public: public:
PresentationEngine(const DeviceState &state, GPU &gpu); PresentationEngine(const DeviceState &state, GPU &gpu);