blob: c8ce4fca04b5b4f85769439b49a7db4687af8b92 (
plain) (
tree)
|
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h"
#include "core/hle/service/vi/vsync_manager.h"
constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60};
namespace Service::VI {
Conductor::Conductor(Core::System& system, Container& container, DisplayList& displays)
: m_system(system), m_container(container) {
displays.ForEachDisplay([&](Display& display) {
m_vsync_managers.insert({display.GetId(), VsyncManager{}});
});
if (system.IsMulticore()) {
m_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
m_signal.Set();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
} else {
m_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
}
}
Conductor::~Conductor() {
m_system.CoreTiming().UnscheduleEvent(m_event);
if (m_system.IsMulticore()) {
m_thread.request_stop();
m_signal.Set();
}
}
void Conductor::LinkVsyncEvent(u64 display_id, Event* event) {
if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
it->second.LinkVsyncEvent(event);
}
}
void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) {
if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
it->second.UnlinkVsyncEvent(event);
}
}
void Conductor::ProcessVsync() {
for (auto& [display_id, manager] : m_vsync_managers) {
m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id);
manager.SignalVsync();
}
}
void Conductor::VsyncThread(std::stop_token token) {
Common::SetCurrentThreadName("VSyncThread");
while (!token.stop_requested()) {
m_signal.Wait();
if (m_system.IsShuttingDown()) {
return;
}
this->ProcessVsync();
}
}
s64 Conductor::GetNextTicks() const {
const auto& settings = Settings::values;
auto speed_scale = 1.f;
if (settings.use_multi_core.GetValue()) {
if (settings.use_speed_limit.GetValue()) {
// Scales the speed based on speed_limit setting on MC. SC is handled by
// SpeedLimiter::DoSpeedLimiting.
speed_scale = 100.f / settings.speed_limit.GetValue();
} else {
// Run at unlocked framerate.
speed_scale = 0.01f;
}
}
// Adjust by speed limit determined during composition.
speed_scale /= m_compose_speed_scale;
if (m_system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
// Run at intended presentation rate during video playback.
speed_scale = 1.f;
}
const f32 effective_fps = 60.f / static_cast<f32>(m_swap_interval);
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
}
} // namespace Service::VI
|