Compare commits

..

1 Commits

Author SHA1 Message Date
lollipoponthestick dc04115e87 [renderer, android] Make loseless scaling flow runtime-editable and add QS UI (#4381)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This PR modifies the loseless scaling rendering hook to actually be runtime editable, allowing users much easier time experimenting with diffrent values
It also adds a (WIP) Android QS impl

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4381
2026-09-09 05:47:32 +02:00
38 changed files with 421 additions and 215 deletions
@@ -114,7 +114,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
name: Int, name: Int,
container: ViewGroup, container: ViewGroup,
setting: BooleanSetting setting: BooleanSetting,
isEnabled: Boolean = true,
onValueChanged: ((Boolean) -> Unit)? = null
) { ) {
val inflater = LayoutInflater.from(emulationFragment.requireContext()) val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
@@ -125,15 +127,21 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
titleView.text = YuzuApplication.appContext.getString(name) titleView.text = YuzuApplication.appContext.getString(name)
switchContainer.visibility = View.VISIBLE switchContainer.visibility = View.VISIBLE
switchContainer.isEnabled = isEnabled
switchView.isChecked = setting.getBoolean() switchView.isChecked = setting.getBoolean()
switchView.isEnabled = isEnabled
itemView.alpha = if (isEnabled) 1.0f else 0.5f
switchView.setOnCheckedChangeListener { _, isChecked -> switchView.setOnCheckedChangeListener { _, isChecked ->
setting.setBoolean(isChecked) setting.setBoolean(isChecked)
saveSettings() saveSettings()
onValueChanged?.invoke(isChecked)
} }
switchContainer.setOnClickListener { switchContainer.setOnClickListener {
switchView.toggle() if (switchView.isEnabled) {
switchView.toggle()
}
} }
container.addView(itemView) container.addView(itemView)
} }
@@ -177,7 +185,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
setting: AbstractSetting, setting: AbstractSetting,
minValue: Int = 0, minValue: Int = 0,
maxValue: Int = 100, maxValue: Int = 100,
units: String = "" units: String = "",
isEnabled: Boolean = true,
onValueChanged: ((Int) -> Unit)? = null
) { ) {
val inflater = LayoutInflater.from(emulationFragment.requireContext()) val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
@@ -190,10 +200,13 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
titleView.text = YuzuApplication.appContext.getString(name) titleView.text = YuzuApplication.appContext.getString(name)
sliderContainer.visibility = View.VISIBLE sliderContainer.visibility = View.VISIBLE
sliderContainer.isEnabled = isEnabled
slider.valueFrom = minValue.toFloat() slider.valueFrom = minValue.toFloat()
slider.valueTo = maxValue.toFloat() slider.valueTo = maxValue.toFloat()
slider.stepSize = 1f slider.stepSize = 1f
slider.isEnabled = isEnabled
itemView.alpha = if (isEnabled) 1.0f else 0.5f
val currentValue = when (setting) { val currentValue = when (setting) {
is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt() is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt()
is AbstractIntSetting -> setting.getInt(needsGlobal = false) is AbstractIntSetting -> setting.getInt(needsGlobal = false)
@@ -204,8 +217,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val displayValue = "${slider.value.toInt()}$units" val displayValue = "${slider.value.toInt()}$units"
valueDisplay.text = displayValue valueDisplay.text = displayValue
slider.addOnChangeListener { _, value, chanhed -> slider.addOnChangeListener { _, value, changed ->
if (chanhed) { if (changed) {
val intValue = value.toInt() val intValue = value.toInt()
when (setting) { when (setting) {
is AbstractShortSetting -> setting.setShort(intValue.toShort()) is AbstractShortSetting -> setting.setShort(intValue.toShort())
@@ -213,6 +226,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
} }
saveSettings() saveSettings()
valueDisplay.text = "$intValue$units" valueDisplay.text = "$intValue$units"
onValueChanged?.invoke(intValue)
} }
} }
@@ -92,6 +92,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils
@@ -1157,6 +1158,36 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addDivider(container) quickSettings.addDivider(container)
val frameGenAvailable = LosslessScalingHelper.isInstalled() &&
LosslessScalingHelper.isSupportedByGpu()
val frameGenEnabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean()
val usesFixedMultiplier =
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt() == 0
quickSettings.addBooleanSetting(
R.string.frame_gen,
container,
BooleanSetting.RENDERER_FRAME_GEN,
isEnabled = frameGenAvailable
) {
NativeLibrary.applySettings()
addQuickSettings()
}
quickSettings.addSliderSetting(
R.string.frame_gen_multiplier,
container,
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER,
minValue = 2,
maxValue = 4,
units = "x",
isEnabled = frameGenAvailable && frameGenEnabled && usesFixedMultiplier
) {
NativeLibrary.applySettings()
}
quickSettings.addDivider(container)
quickSettings.addIntSetting( quickSettings.addIntSetting(
R.string.renderer_accuracy, R.string.renderer_accuracy,
container, container,
@@ -22,6 +22,7 @@
#include "input_common/main.h" #include "input_common/main.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h"
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
if (!surface) { if (!surface) {
@@ -125,10 +126,14 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
} }
float EmuWindow_Android::GetPresentedFrameMultiplier() { float EmuWindow_Android::GetPresentedFrameMultiplier() {
if (!Settings::values.frame_gen.GetValue()) { if (EmulationSession::GetInstance().IsRunning()) {
return 1.0f; const VideoCore::FrameGenConfig config =
EmulationSession::GetInstance().System().Renderer().Settings().GetFrameGenConfig();
return config.enabled ? static_cast<float>(config.multiplier) : 1.0f;
} }
return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4)); return Settings::values.frame_gen.GetValue()
? static_cast<float>(Settings::FrameGenMultiplier())
: 1.0f;
} }
float EmuWindow_Android::GetFrameRateHint() const { float EmuWindow_Android::GetFrameRateHint() const {
+1 -1
View File
@@ -12,7 +12,7 @@
namespace AudioCore { namespace AudioCore {
AudioCore::AudioCore(Core::System& system) { AudioCore::AudioCore(Core::System& system) {
audio_manager.emplace(system); audio_manager.emplace();
CreateSinks(); CreateSinks();
// Must be created after the sinks // Must be created after the sinks
adsp.emplace(system, *output_sink); adsp.emplace(system, *output_sink);
+15 -12
View File
@@ -15,12 +15,12 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
Manager::Manager(Core::System& system) { Manager::Manager(Core::System& system_) : system{system_} {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxInSessions; num_free_sessions = MaxInSessions;
} }
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) { Result Manager::AcquireSessionId(size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) { void Manager::ReleaseSessionId(const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -41,20 +41,21 @@ void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager(Core::System& system) { Result Manager::LinkToManager() {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetInManager(&Manager::BufferReleaseAndRegister); system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start(Core::System& system) { void Manager::Start() {
if (sessions_started) { if (sessions_started) {
return; return;
} }
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
for (auto& session : sessions) { for (auto& session : sessions) {
if (session) { if (session) {
@@ -65,19 +66,21 @@ void Manager::Start(Core::System& system) {
sessions_started = true; sessions_started = true;
} }
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept { void Manager::BufferReleaseAndRegister() {
Manager* this_ = (Manager*)data; std::scoped_lock l{mutex};
std::scoped_lock l{this_->mutex}; for (auto& session : sessions) {
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) { u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
[[maybe_unused]] const bool filter) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LinkToManager(system);
LinkToManager();
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (!input_devices.empty() && !names.empty()) { if (!input_devices.empty() && !names.empty()) {
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac"); names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
+11 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,29 +30,31 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(Core::System& system, size_t& session_id); Result AcquireSessionId(size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(Core::System& system, const size_t session_id); void ReleaseSessionId(size_t session_id);
/** /**
* Link the audio in manager to the main audio manager. * Link the audio in manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(Core::System& system); Result LinkToManager();
/** /**
* Start the audio in manager. * Start the audio in manager.
*/ */
void Start(Core::System& system); void Start();
/// @brief Callback function, called by the audio manager when the audio in event is signalled. /**
static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept; * Callback function, called by the audio manager when the audio in event is signalled.
*/
void BufferReleaseAndRegister();
/** /**
* Get a list of audio in device names. * Get a list of audio in device names.
@@ -65,8 +64,10 @@ public:
* *
* @return Number of names written. * @return Number of names written.
*/ */
u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter); u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxInSessions> session_ids{}; std::array<size_t, MaxInSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+3 -3
View File
@@ -11,8 +11,8 @@
namespace AudioCore { namespace AudioCore {
AudioManager::AudioManager(Core::System& system) { AudioManager::AudioManager() {
thread = std::jthread([&](std::stop_token stop_token) { thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioManager"); Common::SetCurrentThreadName("AudioManager");
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
@@ -25,7 +25,7 @@ AudioManager::AudioManager(Core::System& system) {
const auto event_type = Event::Type(i); const auto event_type = Event::Type(i);
if (events.CheckAudioEventSet(event_type) || timed_out) { if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) { if (buffer_events[i]) {
buffer_events[i](this, system); buffer_events[i]();
} }
} }
events.SetAudioEvent(event_type, false); events.SetAudioEvent(event_type, false);
+3 -6
View File
@@ -16,10 +16,6 @@
#include "audio_core/audio_event.h" #include "audio_core/audio_event.h"
namespace Core {
class System;
}
union Result; union Result;
namespace AudioCore { namespace AudioCore {
@@ -38,9 +34,10 @@ namespace AudioCore {
* This is only used by audio in and audio out. * This is only used by audio in and audio out.
*/ */
class AudioManager { class AudioManager {
using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept; using BufferEventFunc = std::function<void()>;
public: public:
explicit AudioManager(Core::System& system); explicit AudioManager();
/** /**
* Shutdown the audio manager. * Shutdown the audio manager.
+15 -10
View File
@@ -14,12 +14,12 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Manager::Manager(Core::System& system) { Manager::Manager(Core::System& system_) : system{system_} {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxOutSessions; num_free_sessions = MaxOutSessions;
} }
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) { Result Manager::AcquireSessionId(size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) { void Manager::ReleaseSessionId(const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -40,17 +40,17 @@ void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager(Core::System& system) { Result Manager::LinkToManager() {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetOutManager(&Manager::BufferReleaseAndRegister); system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start(Core::System& system) { void Manager::Start() {
if (sessions_started) { if (sessions_started) {
return; return;
} }
@@ -65,14 +65,19 @@ void Manager::Start(Core::System& system) {
sessions_started = true; sessions_started = true;
} }
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept { void Manager::BufferReleaseAndRegister() {
Manager* this_ = (Manager*)data; std::scoped_lock l{mutex};
std::scoped_lock l{this_->mutex}; for (auto& session : sessions) {
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetAudioOutDeviceNames(
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
names.emplace_back("DeviceOut");
return 1;
}
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+15 -8
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -32,32 +29,42 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(Core::System& system, size_t& session_id); Result AcquireSessionId(size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(Core::System& system, const size_t session_id); void ReleaseSessionId(size_t session_id);
/** /**
* Link this manager to the main audio manager. * Link this manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(Core::System& system); Result LinkToManager();
/** /**
* Start the audio out manager. * Start the audio out manager.
*/ */
void Start(Core::System& system); void Start();
/** /**
* Callback function, called by the audio manager when the audio out event is signalled. * Callback function, called by the audio manager when the audio out event is signalled.
*/ */
static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept; void BufferReleaseAndRegister();
/**
* Get a list of audio out device names.
*
* @param names - Output container to write names to.
* @return Number of names written.
*/
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxOutSessions> session_ids{}; std::array<size_t, MaxOutSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+3 -8
View File
@@ -1,20 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/audio_render_manager.h" #include "audio_core/audio_render_manager.h"
#include "audio_core/common/audio_renderer_parameter.h" #include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/renderer/system_manager.h"
#include "audio_core/common/feature_support.h" #include "audio_core/common/feature_support.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Manager::Manager(Core::System& system_) Manager::Manager(Core::System& system_)
: system_manager{std::make_unique<SystemManager>(system_)} : system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
{
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
} }
@@ -64,11 +59,11 @@ u32 Manager::GetSessionCount() const {
return session_count; return session_count;
} }
bool Manager::AddSystem(Renderer::System& system_) { bool Manager::AddSystem(System& system_) {
return system_manager->Add(system_); return system_manager->Add(system_);
} }
bool Manager::RemoveSystem(Renderer::System& system_) { bool Manager::RemoveSystem(System& system_) {
return system_manager->Remove(system_); return system_manager->Remove(system_);
} }
+4 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -74,7 +71,7 @@ public:
* @param system - The system to add. * @param system - The system to add.
* @return True if the system was successfully added, otherwise false. * @return True if the system was successfully added, otherwise false.
*/ */
bool AddSystem(Renderer::System& system); bool AddSystem(System& system);
/** /**
* Remove a renderer system from the manager. * Remove a renderer system from the manager.
@@ -82,7 +79,7 @@ public:
* @param system - The system to remove. * @param system - The system to remove.
* @return True if the system was successfully removed, otherwise false. * @return True if the system was successfully removed, otherwise false.
*/ */
bool RemoveSystem(Renderer::System& system); bool RemoveSystem(System& system);
/** /**
* Free a session id when the system wants to shut down. * Free a session id when the system wants to shut down.
@@ -92,6 +89,8 @@ public:
void ReleaseSessionId(s32 session_id); void ReleaseSessionId(s32 session_id);
private: private:
/// Core system
Core::System& system;
/// Session ids, -1 when in use /// Session ids, -1 when in use
std::array<s32, MaxRendererSessions> session_ids{}; std::array<s32, MaxRendererSessions> session_ids{};
/// Number of active renderers /// Number of active renderers
+20 -24
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,43 +8,42 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_} : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
, audio_system{system_, event, session_id_} session_id_} {}
{}
void In::Free(Core::System& system) { void In::Free() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system, audio_system.GetSessionId()); manager.ReleaseSessionId(system.GetSessionId());
} }
System& In::GetSystem() { System& In::GetSystem() {
return audio_system; return system;
} }
AudioIn::State In::GetState() { AudioIn::State In::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetState(); return system.GetState();
} }
Result In::StartSystem() { Result In::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Start(); return system.Start();
} }
void In::StartSession() { void In::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.StartSession(); system.StartSession();
} }
Result In::StopSystem() { Result In::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Stop(); return system.Stop();
} }
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) { Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.AppendBuffer(buffer, tag)) { if (system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -55,20 +51,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
void In::ReleaseAndRegisterBuffers() { void In::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.GetState() == State::Started) { if (system.GetState() == State::Started) {
audio_system.ReleaseBuffers(); system.ReleaseBuffers();
audio_system.RegisterBuffers(); system.RegisterBuffers();
} }
} }
bool In::FlushAudioInBuffers() { bool In::FlushAudioInBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.FlushAudioInBuffers(); return system.FlushAudioInBuffers();
} }
u32 In::GetReleasedBuffers(std::span<u64> tags) { u32 In::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetReleasedBuffers(tags); return system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& In::GetBufferEvent() { Kernel::KReadableEvent& In::GetBufferEvent() {
@@ -78,27 +74,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
f32 In::GetVolume() const { f32 In::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetVolume(); return system.GetVolume();
} }
void In::SetVolume(f32 volume) { void In::SetVolume(f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.SetVolume(volume); system.SetVolume(volume);
} }
bool In::ContainsAudioBuffer(u64 tag) const { bool In::ContainsAudioBuffer(u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.ContainsAudioBuffer(tag); return system.ContainsAudioBuffer(tag);
} }
u32 In::GetBufferCount() const { u32 In::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetBufferCount(); return system.GetBufferCount();
} }
u64 In::GetPlayedSampleCount() const { u64 In::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetPlayedSampleCount(); return system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,7 +30,7 @@ public:
/** /**
* Free this audio in from the audio in manager. * Free this audio in from the audio in manager.
*/ */
void Free(Core::System& system); void Free();
/** /**
* Get this audio in's system. * Get this audio in's system.
@@ -144,7 +141,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio in system /// Main audio in system
System audio_system; System system;
}; };
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+20 -24
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,43 +8,42 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_} : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
, audio_system{system_, event, session_id_} session_id_} {}
{}
void Out::Free(Core::System& system) { void Out::Free() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system, audio_system.GetSessionId()); manager.ReleaseSessionId(system.GetSessionId());
} }
System& Out::GetSystem() { System& Out::GetSystem() {
return audio_system; return system;
} }
AudioOut::State Out::GetState() { AudioOut::State Out::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetState(); return system.GetState();
} }
Result Out::StartSystem() { Result Out::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Start(); return system.Start();
} }
void Out::StartSession() { void Out::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.StartSession(); system.StartSession();
} }
Result Out::StopSystem() { Result Out::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Stop(); return system.Stop();
} }
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) { Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.AppendBuffer(buffer, tag)) { if (system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -55,20 +51,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
void Out::ReleaseAndRegisterBuffers() { void Out::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.GetState() == State::Started) { if (system.GetState() == State::Started) {
audio_system.ReleaseBuffers(); system.ReleaseBuffers();
audio_system.RegisterBuffers(); system.RegisterBuffers();
} }
} }
bool Out::FlushAudioOutBuffers() { bool Out::FlushAudioOutBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.FlushAudioOutBuffers(); return system.FlushAudioOutBuffers();
} }
u32 Out::GetReleasedBuffers(std::span<u64> tags) { u32 Out::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetReleasedBuffers(tags); return system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& Out::GetBufferEvent() { Kernel::KReadableEvent& Out::GetBufferEvent() {
@@ -78,27 +74,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
f32 Out::GetVolume() const { f32 Out::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetVolume(); return system.GetVolume();
} }
void Out::SetVolume(const f32 volume) { void Out::SetVolume(const f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.SetVolume(volume); system.SetVolume(volume);
} }
bool Out::ContainsAudioBuffer(const u64 tag) const { bool Out::ContainsAudioBuffer(const u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.ContainsAudioBuffer(tag); return system.ContainsAudioBuffer(tag);
} }
u32 Out::GetBufferCount() const { u32 Out::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetBufferCount(); return system.GetBufferCount();
} }
u64 Out::GetPlayedSampleCount() const { u64 Out::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetPlayedSampleCount(); return system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,7 +30,7 @@ public:
/** /**
* Free this audio out from the audio out manager. * Free this audio out from the audio out manager.
*/ */
void Free(Core::System& system); void Free();
/** /**
* Get this audio out's system. * Get this audio out's system.
@@ -144,7 +141,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio out system /// Main audio out system
System audio_system; System system;
}; };
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+23 -18
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,48 +13,56 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
: system{system_}, manager{manager_} : core{system_}, manager{manager_}, system{system_, rendered_event} {}
, audio_system{system_, rendered_event}
{}
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) { Result Renderer::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory,
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
const u64 applet_resource_user_id, const s32 session_id) {
if (params.execution_mode == ExecutionMode::Auto) { if (params.execution_mode == ExecutionMode::Auto) {
if (!manager.AddSystem(audio_system)) { if (!manager.AddSystem(system)) {
LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio,
"Both Audio Render sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
} }
system_registered = true; system_registered = true;
} }
initialized = true; initialized = true;
audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id); system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
applet_resource_user_id, session_id);
return ResultSuccess; return ResultSuccess;
} }
void Renderer::Finalize() { void Renderer::Finalize() {
auto const session_id{audio_system.GetSessionId()}; auto session_id{system.GetSessionId()};
audio_system.Finalize();
system.Finalize();
if (system_registered) { if (system_registered) {
manager.RemoveSystem(audio_system); manager.RemoveSystem(system);
system_registered = false; system_registered = false;
} }
manager.ReleaseSessionId(session_id); manager.ReleaseSessionId(session_id);
} }
System& Renderer::GetSystem() { System& Renderer::GetSystem() {
return audio_system; return system;
} }
void Renderer::Start() { void Renderer::Start() {
audio_system.Start(); system.Start();
} }
void Renderer::Stop() { void Renderer::Stop() {
audio_system.Stop(); system.Stop();
} }
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) { Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance,
return audio_system.Update(input, performance, output); std::span<u8> output) {
return system.Update(input, performance, output);
} }
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -87,7 +84,7 @@ public:
private: private:
/// System core /// System core
Core::System& system; Core::System& core;
/// Manager this renderer is registered with /// Manager this renderer is registered with
Manager& manager; Manager& manager;
/// Is the audio renderer initialized? /// Is the audio renderer initialized?
@@ -95,7 +92,7 @@ private:
/// Is the system registered with the manager? /// Is the system registered with the manager?
bool system_registered{}; bool system_registered{};
/// Audio render system, main driver of audio rendering /// Audio render system, main driver of audio rendering
System audio_system; System system;
}; };
} // namespace Renderer } // namespace Renderer
+5 -5
View File
@@ -389,7 +389,7 @@ struct Values {
true}; true};
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer, SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
Specialization::Default, true, false}; Specialization::Default, true, true};
SwitchableSetting<u32, true> frame_gen_multiplier{linkage, SwitchableSetting<u32, true> frame_gen_multiplier{linkage,
2, 2,
@@ -399,7 +399,7 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Countable, Specialization::Countable,
true, true,
false, true,
&frame_gen}; &frame_gen};
SwitchableSetting<u32, true> frame_gen_target_rate{linkage, SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
@@ -419,7 +419,7 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Default, Specialization::Default,
true, true,
false, true,
&frame_gen}; &frame_gen};
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage, SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
@@ -442,11 +442,11 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Countable, Specialization::Countable,
true, true,
false, true,
&frame_gen}; &frame_gen};
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer, SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
Specialization::Default, true, false, &frame_gen}; Specialization::Default, true, true, &frame_gen};
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow", SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
Category::Renderer}; Category::Renderer};
+1 -1
View File
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioIn::~IAudioIn() { IAudioIn::~IAudioIn() {
impl->Free(system); impl->Free();
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -68,7 +65,7 @@ Result IAudioInManager::OpenAudioInAuto(
Result IAudioInManager::ListAudioInsAutoFiltered( Result IAudioInManager::ListAudioInsAutoFiltered(
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) { OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
*out_count = impl->GetDeviceNames(system, out_audio_ins, true); *out_count = impl->GetDeviceNames(out_audio_ins, true);
R_SUCCEED(); R_SUCCEED();
} }
@@ -93,8 +90,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager(system)); R_TRY(impl->LinkToManager());
R_TRY(impl->AcquireSessionId(system, new_session_id)); R_TRY(impl->AcquireSessionId(new_session_id));
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
impl->num_free_sessions); impl->num_free_sessions);
+1 -1
View File
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioOut::~IAudioOut() { IAudioOut::~IAudioOut() {
impl->Free(system); impl->Free();
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
} }
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager(system)); R_TRY(impl->LinkToManager());
R_TRY(impl->AcquireSessionId(system, new_session_id)); R_TRY(impl->AcquireSessionId(new_session_id));
const auto device_name = Common::StringFromBuffer(name[0].name); const auto device_name = Common::StringFromBuffer(name[0].name);
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
+1
View File
@@ -61,6 +61,7 @@ add_library(video_core STATIC
engines/puller.h engines/puller.h
framebuffer_config.cpp framebuffer_config.cpp
framebuffer_config.h framebuffer_config.h
frame_gen/frame_gen_config.h
fsr.cpp fsr.cpp
fsr.h fsr.h
host1x/codecs/decoder.cpp host1x/codecs/decoder.cpp
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <algorithm>
#include <cstddef>
#include "common/common_types.h"
namespace VideoCore {
constexpr u32 MIN_FRAME_GEN_MULTIPLIER = 2;
constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
struct FrameGenConfig {
bool enabled{};
u32 multiplier{MIN_FRAME_GEN_MULTIPLIER};
u32 target_rate{};
bool flow_scale_auto{true};
u32 flow_scale{75};
u32 queue_target{1};
bool fp16{true};
bool dump_flow{};
[[nodiscard]] size_t Generations() const {
return enabled ? std::clamp(multiplier, MIN_FRAME_GEN_MULTIPLIER,
MAX_FRAME_GEN_MULTIPLIER) -
1
: 0;
}
[[nodiscard]] size_t MaxGenerations() const {
if (!enabled) {
return 0;
}
return target_rate != 0 ? MAX_FRAME_GEN_MULTIPLIER - 1 : Generations();
}
bool operator==(const FrameGenConfig&) const = default;
};
} // namespace VideoCore
+11
View File
@@ -7,6 +7,7 @@
#include <thread> #include <thread>
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h" #include "core/frontend/graphics_context.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@@ -22,6 +23,16 @@ RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
RendererBase::~RendererBase() = default; RendererBase::~RendererBase() = default;
void RendererBase::RefreshBaseSettings() { void RendererBase::RefreshBaseSettings() {
renderer_settings.SetFrameGenConfig({
.enabled = Settings::values.frame_gen.GetValue(),
.multiplier = Settings::FrameGenMultiplier(),
.target_rate = Settings::values.frame_gen_target_rate.GetValue(),
.flow_scale_auto = Settings::values.frame_gen_flow_scale_auto.GetValue(),
.flow_scale = Settings::values.frame_gen_flow_scale.GetValue(),
.queue_target = Settings::values.frame_gen_queue_target.GetValue(),
.fp16 = Settings::values.frame_gen_fp16.GetValue(),
.dump_flow = Settings::values.frame_gen_dump_flow.GetValue(),
});
UpdateCurrentFramebufferLayout(); UpdateCurrentFramebufferLayout();
} }
+16
View File
@@ -9,10 +9,12 @@
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "video_core/frame_gen/frame_gen_config.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@@ -30,6 +32,20 @@ struct RendererSettings {
std::function<void(bool)> screenshot_complete_callback; std::function<void(bool)> screenshot_complete_callback;
Layout::FramebufferLayout screenshot_framebuffer_layout; Layout::FramebufferLayout screenshot_framebuffer_layout;
Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default}; Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default};
void SetFrameGenConfig(FrameGenConfig config) {
std::scoped_lock lock{frame_gen_mutex};
frame_gen_config = config;
}
[[nodiscard]] FrameGenConfig GetFrameGenConfig() const {
std::scoped_lock lock{frame_gen_mutex};
return frame_gen_config;
}
private:
mutable std::mutex frame_gen_mutex;
FrameGenConfig frame_gen_config;
}; };
class RendererBase { class RendererBase {
@@ -23,13 +23,14 @@ constexpr size_t COLOR_CHANNELS = 4;
constexpr u64 LSFG_REQUIRED_FRAMES = 2; constexpr u64 LSFG_REQUIRED_FRAMES = 2;
constexpr u32 LSFG_RECURRENCE_FRAMES = 2; constexpr u32 LSFG_RECURRENCE_FRAMES = 2;
[[nodiscard]] f32 ManualFlowScale() { [[nodiscard]] f32 ManualFlowScale(const VideoCore::FrameGenConfig& config) {
return static_cast<f32>(Settings::values.frame_gen_flow_scale.GetValue()) / 100.0f; return static_cast<f32>(config.flow_scale) / 100.0f;
} }
[[nodiscard]] f32 ConfiguredFlowScale(VkExtent2D guest_extent, VkExtent2D presented_extent) { [[nodiscard]] f32 ConfiguredFlowScale(const VideoCore::FrameGenConfig& config,
if (!Settings::values.frame_gen_flow_scale_auto.GetValue()) { VkExtent2D guest_extent, VkExtent2D presented_extent) {
return ManualFlowScale(); if (!config.flow_scale_auto) {
return ManualFlowScale(config);
} }
if (guest_extent.width == 0 || presented_extent.width == 0) { if (guest_extent.width == 0 || presented_extent.width == 0) {
return 1.0f; return 1.0f;
@@ -194,15 +195,61 @@ FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
FrameGen::~FrameGen() = default; FrameGen::~FrameGen() = default;
void FrameGen::Process(const Device& device, Frame* frame, VkFormat format, void FrameGen::UpdateConfig(const VideoCore::FrameGenConfig& new_config) {
VkExtent2D guest_extent) { if (!has_config) {
generated = false; config = new_config;
has_config = true;
return;
}
if (config == new_config) {
return;
}
if (unavailable || !Settings::values.frame_gen.GetValue()) { const bool has_toggled_lsfg = config.enabled != new_config.enabled;
const bool precision_changed = config.fp16 != new_config.fp16;
const bool enabling = !config.enabled && new_config.enabled;
const bool must_reset_pipeline = has_toggled_lsfg || precision_changed;
const bool must_reload_shaders = precision_changed || enabling;
const bool must_reset_pacer =
has_toggled_lsfg || config.multiplier != new_config.multiplier ||
config.target_rate != new_config.target_rate;
if (must_reset_pipeline) {
if (chain) { if (chain) {
scheduler.Finish(); scheduler.Finish();
chain.reset(); chain.reset();
} }
if (must_reload_shaders) {
shaders.reset();
}
plan = {};
peak_guest_extent = {};
built_extent = {};
built_format = VK_FORMAT_UNDEFINED;
built_flow_scale = 0.0f;
frame_count = 0;
warm_streak = 0;
generated = false;
dumped = false;
// this will pickup the dll again once toggled
if (must_reload_shaders) {
unavailable = false;
}
}
if (must_reset_pacer) {
pacer.Reset();
}
config = new_config;
}
void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
VkExtent2D guest_extent) {
generated = false;
if (unavailable || !config.enabled) {
warm_streak = 0; warm_streak = 0;
return; return;
} }
@@ -213,7 +260,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
} }
if (!shaders) { if (!shaders) {
shaders.emplace(device); shaders.emplace(device, config.fp16);
if (!shaders->IsValid()) { if (!shaders->IsValid()) {
unavailable = true; unavailable = true;
return; return;
@@ -224,7 +271,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
peak_guest_extent.height = std::max(peak_guest_extent.height, guest_extent.height); peak_guest_extent.height = std::max(peak_guest_extent.height, guest_extent.height);
const VkExtent2D extent{.width = frame->width, .height = frame->height}; const VkExtent2D extent{.width = frame->width, .height = frame->height};
const f32 flow_scale = ConfiguredFlowScale(peak_guest_extent, extent); const f32 flow_scale = ConfiguredFlowScale(config, peak_guest_extent, extent);
if (!chain || built_extent.width != extent.width || built_extent.height != extent.height || if (!chain || built_extent.width != extent.width || built_extent.height != extent.height ||
built_format != format || built_flow_scale != flow_scale) { built_format != format || built_flow_scale != flow_scale) {
Rebuild(device, extent, format, flow_scale); Rebuild(device, extent, format, flow_scale);
@@ -247,7 +294,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
} }
}); });
const bool dump_requested = generated && Settings::values.frame_gen_dump_flow.GetValue(); const bool dump_requested = generated && config.dump_flow;
if (!dump_requested) { if (!dump_requested) {
dumped = false; dumped = false;
} else if (!dumped) { } else if (!dumped) {
@@ -261,7 +308,7 @@ size_t FrameGen::WantedGenerations(size_t capacity) {
plan = {}; plan = {};
return 0; return 0;
} }
plan = pacer.Plan(capacity); plan = pacer.Plan(capacity, config);
return plan.generations; return plan.generations;
} }
@@ -6,6 +6,7 @@
#include <optional> #include <optional>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/frame_gen/frame_gen_config.h"
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h" #include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
#include "video_core/renderer_vulkan/present/lsfg_chain.h" #include "video_core/renderer_vulkan/present/lsfg_chain.h"
#include "video_core/renderer_vulkan/present/lsfg_shaders.h" #include "video_core/renderer_vulkan/present/lsfg_shaders.h"
@@ -22,6 +23,8 @@ public:
explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler); explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler);
~FrameGen(); ~FrameGen();
void UpdateConfig(const VideoCore::FrameGenConfig& new_config);
void Process(const Device& device, Frame* frame, VkFormat format, VkExtent2D guest_extent); void Process(const Device& device, Frame* frame, VkFormat format, VkExtent2D guest_extent);
[[nodiscard]] size_t WantedGenerations(size_t capacity); [[nodiscard]] size_t WantedGenerations(size_t capacity);
@@ -39,6 +42,7 @@ private:
std::optional<LsfgShaders> shaders; std::optional<LsfgShaders> shaders;
std::optional<LsfgChain> chain; std::optional<LsfgChain> chain;
VideoCore::FrameGenConfig config;
FrameGenPacer pacer; FrameGenPacer pacer;
FrameGenPlan plan{}; FrameGenPlan plan{};
VkExtent2D peak_guest_extent{}; VkExtent2D peak_guest_extent{};
@@ -52,6 +56,7 @@ private:
bool generated{}; bool generated{};
bool unavailable{}; bool unavailable{};
bool dumped{}; bool dumped{};
bool has_config{};
}; };
} // namespace Vulkan } // namespace Vulkan
@@ -5,7 +5,6 @@
#include <cmath> #include <cmath>
#include <utility> #include <utility>
#include "common/settings.h"
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h" #include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
namespace Vulkan { namespace Vulkan {
@@ -46,8 +45,8 @@ constexpr auto PROBE_STEP_DELAY = std::chrono::milliseconds(250);
} // Anonymous namespace } // Anonymous namespace
FrameGenPlan FrameGenPacer::Plan(size_t capacity) { FrameGenPlan FrameGenPacer::Plan(size_t capacity, const VideoCore::FrameGenConfig& config) {
const size_t ceiling = std::min(capacity, Settings::FrameGenMaxGenerations()); const size_t ceiling = std::min(capacity, config.MaxGenerations());
if (ceiling == 0) { if (ceiling == 0) {
Reset(); Reset();
return {}; return {};
@@ -69,7 +68,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
return {}; return {};
} }
const f32 target_rate = static_cast<f32>(Settings::values.frame_gen_target_rate.GetValue()); const f32 target_rate = static_cast<f32>(config.target_rate);
if (smoothed_interval > 0.0f) { if (smoothed_interval > 0.0f) {
f32 burst_threshold = BURST_CADENCE_RATIO / smoothed_interval; f32 burst_threshold = BURST_CADENCE_RATIO / smoothed_interval;
@@ -109,7 +108,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
} }
if (target_rate == 0.0f) { if (target_rate == 0.0f) {
limit = std::min(Settings::FrameGenGenerations(), ceiling); limit = std::min(config.Generations(), ceiling);
output_credit = 0.0f; output_credit = 0.0f;
issued_generations = limit; issued_generations = limit;
return {.generations = limit, .warm = limit > 0}; return {.generations = limit, .warm = limit > 0};
@@ -7,6 +7,7 @@
#include <optional> #include <optional>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/frame_gen/frame_gen_config.h"
namespace Vulkan { namespace Vulkan {
@@ -17,7 +18,7 @@ struct FrameGenPlan {
class FrameGenPacer { class FrameGenPacer {
public: public:
[[nodiscard]] FrameGenPlan Plan(size_t capacity); [[nodiscard]] FrameGenPlan Plan(size_t capacity, const VideoCore::FrameGenConfig& config);
void Reset(); void Reset();
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "common/settings.h"
#include "video_core/frame_gen/lossless_dll.h" #include "video_core/frame_gen/lossless_dll.h"
#include "video_core/renderer_vulkan/present/lsfg_shaders.h" #include "video_core/renderer_vulkan/present/lsfg_shaders.h"
#include "video_core/renderer_vulkan/present/util.h" #include "video_core/renderer_vulkan/present/util.h"
@@ -9,16 +8,15 @@
namespace Vulkan { namespace Vulkan {
LsfgShaders::LsfgShaders(const Device& device) { LsfgShaders::LsfgShaders(const Device& device, bool prefer_fp16) {
if (!device.IsVulkanMemoryModelSupported() || !device.HasNullDescriptor()) { if (!device.IsVulkanMemoryModelSupported() || !device.HasNullDescriptor()) {
return; return;
} }
const bool allow_fp16 = device.IsFloat16Supported(); const bool allow_fp16 = device.IsFloat16Supported();
const bool prefer_fp16 = allow_fp16 && Settings::values.frame_gen_fp16.GetValue();
VideoCore::FrameGen::ShaderModules code; VideoCore::FrameGen::ShaderModules code;
if (VideoCore::FrameGen::LoadShaderModules(code, allow_fp16, prefer_fp16) != if (VideoCore::FrameGen::LoadShaderModules(code, allow_fp16, allow_fp16 && prefer_fp16) !=
VideoCore::FrameGen::LosslessStatus::Ok) { VideoCore::FrameGen::LosslessStatus::Ok) {
return; return;
} }
@@ -14,7 +14,7 @@ class Device;
class LsfgShaders { class LsfgShaders {
public: public:
explicit LsfgShaders(const Device& device); explicit LsfgShaders(const Device& device, bool prefer_fp16);
[[nodiscard]] bool IsValid() const { [[nodiscard]] bool IsValid() const {
return valid; return valid;
@@ -145,6 +145,7 @@ try
, swapchain(*surface, , swapchain(*surface,
device, device,
scheduler, scheduler,
Settings(),
render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().width,
render_window.GetFramebufferLayout().height) render_window.GetFramebufferLayout().height)
, present_manager(instance, , present_manager(instance,
@@ -152,6 +153,7 @@ try
device, device,
memory_allocator, memory_allocator,
scheduler, scheduler,
Settings(),
swapchain, swapchain,
surface) surface)
, blit_swapchain(device_memory, , blit_swapchain(device_memory,
@@ -206,6 +208,9 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
} }
RenderScreenshot(framebuffers); RenderScreenshot(framebuffers);
#ifdef HAS_LSFG
frame_gen.UpdateConfig(Settings().GetFrameGenConfig());
#endif
Frame* frame = present_manager.GetRenderFrame(); Frame* frame = present_manager.GetRenderFrame();
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
@@ -7,6 +7,7 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "video_core/renderer_base.h"
#ifdef HAS_LSFG #ifdef HAS_LSFG
#include "video_core/renderer_vulkan/present/lsfg_common.h" #include "video_core/renderer_vulkan/present/lsfg_common.h"
#endif #endif
@@ -29,9 +30,6 @@ static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS);
bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) { bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) {
#ifdef HAS_LSFG #ifdef HAS_LSFG
if (!Settings::values.frame_gen.GetValue()) {
return false;
}
const VkFormatProperties props{physical_device.GetFormatProperties(format)}; const VkFormatProperties props{physical_device.GetFormatProperties(format)};
return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0; return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
#else #else
@@ -120,6 +118,7 @@ PresentManager::PresentManager(const vk::Instance& instance_,
const Device& device_, const Device& device_,
MemoryAllocator& memory_allocator_, MemoryAllocator& memory_allocator_,
Scheduler& scheduler_, Scheduler& scheduler_,
const VideoCore::RendererSettings& renderer_settings_,
Swapchain& swapchain_, Swapchain& swapchain_,
vk::SurfaceKHR& surface_) vk::SurfaceKHR& surface_)
: instance{instance_} : instance{instance_}
@@ -127,13 +126,14 @@ PresentManager::PresentManager(const vk::Instance& instance_,
, device{device_} , device{device_}
, memory_allocator{memory_allocator_} , memory_allocator{memory_allocator_}
, scheduler{scheduler_} , scheduler{scheduler_}
, renderer_settings{renderer_settings_}
, swapchain{swapchain_} , swapchain{swapchain_}
, surface{surface_} , surface{surface_}
, blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())} , blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}
, storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())} , storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())}
, use_present_thread{Settings::values.async_presentation.GetValue()} , use_present_thread{Settings::values.async_presentation.GetValue()}
{ {
SetImageCount(); UpdateSwapchainImageCount();
auto& dld = device.GetLogical(); auto& dld = device.GetLogical();
cmdpool = dld.CreateCommandPool({ cmdpool = dld.CreateCommandPool({
@@ -143,9 +143,14 @@ PresentManager::PresentManager(const vk::Instance& instance_,
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = device.GetGraphicsFamily(), .queueFamilyIndex = device.GetGraphicsFamily(),
}); });
auto cmdbuffers = cmdpool.Allocate(image_count); #ifdef HAS_LSFG
constexpr size_t frame_capacity = MAX_FRAMES_IN_FLIGHT;
#else
const size_t frame_capacity = DesiredFrameCount();
#endif
auto cmdbuffers = cmdpool.Allocate(frame_capacity);
frames.resize(image_count); frames.resize(frame_capacity);
for (u32 i = 0; i < frames.size(); i++) { for (u32 i = 0; i < frames.size(); i++) {
Frame& frame = frames[i]; Frame& frame = frames[i];
frame.index = i; frame.index = i;
@@ -174,7 +179,10 @@ Frame* PresentManager::GetRenderFrame() {
// Wait for free presentation frames // Wait for free presentation frames
std::unique_lock lock{free_mutex}; std::unique_lock lock{free_mutex};
free_cv.wait(lock, [this] { return !free_queue.empty(); }); free_cv.wait(lock, [this] {
const size_t outstanding = frames.size() - free_queue.size();
return !free_queue.empty() && outstanding < DesiredFrameCount();
});
// Take the frame from the queue // Take the frame from the queue
Frame* frame = free_queue.front(); Frame* frame = free_queue.front();
@@ -202,7 +210,8 @@ void PresentManager::Present(Frame* frame) {
} }
size_t PresentManager::MaxExtraFrames() const { size_t PresentManager::MaxExtraFrames() const {
return image_count - 1; const size_t frame_count = DesiredFrameCount();
return frame_count > 0 ? frame_count - 1 : 0;
} }
void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format, void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format,
@@ -351,22 +360,30 @@ void PresentManager::PresentThread(std::stop_token token) {
void PresentManager::RecreateSwapchain(Frame* frame) { void PresentManager::RecreateSwapchain(Frame* frame) {
swapchain.Create(*surface, frame->width, frame->height); // Pass raw pointer swapchain.Create(*surface, frame->width, frame->height); // Pass raw pointer
SetImageCount(); UpdateSwapchainImageCount();
} }
void PresentManager::SetImageCount() { void PresentManager::UpdateSwapchainImageCount() {
swapchain_image_count.store(
std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT),
std::memory_order_release);
free_cv.notify_all();
}
size_t PresentManager::DesiredFrameCount() const {
// We cannot have more than 7 images in flight at any given time. // We cannot have more than 7 images in flight at any given time.
// FRAMES_IN_FLIGHT is 8, and the cache TICKS_TO_DESTROY is 8. // FRAMES_IN_FLIGHT is 8, and the cache TICKS_TO_DESTROY is 8.
// Mali drivers will give us 6. // Mali drivers will give us 6.
const size_t minimum = swapchain_image_count.load(std::memory_order_acquire);
#ifdef HAS_LSFG #ifdef HAS_LSFG
const size_t generations = Settings::FrameGenMaxGenerations(); const VideoCore::FrameGenConfig config = renderer_settings.GetFrameGenConfig();
const size_t queued_composites = Settings::values.frame_gen_queue_target.GetValue() + 1; if (config.enabled) {
image_count = const size_t queued_composites = std::clamp<size_t>(config.queue_target, 0, 2) + 1;
std::clamp<size_t>((generations + 1) * queued_composites, swapchain.GetImageCount(), return std::clamp<size_t>((config.MaxGenerations() + 1) * queued_composites, minimum,
MAX_FRAMES_IN_FLIGHT); MAX_FRAMES_IN_FLIGHT);
#else }
image_count = std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT);
#endif #endif
return minimum;
} }
void PresentManager::CopyToSwapchain(Frame* frame) { void PresentManager::CopyToSwapchain(Frame* frame) {
@@ -6,6 +6,7 @@
#pragma once #pragma once
#include <atomic>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <boost/container/deque.hpp> #include <boost/container/deque.hpp>
@@ -19,6 +20,10 @@ namespace Core::Frontend {
class EmuWindow; class EmuWindow;
} // namespace Core::Frontend } // namespace Core::Frontend
namespace VideoCore {
struct RendererSettings;
}
namespace Vulkan { namespace Vulkan {
class Device; class Device;
@@ -45,6 +50,7 @@ public:
const Device& device, const Device& device,
MemoryAllocator& memory_allocator, MemoryAllocator& memory_allocator,
Scheduler& scheduler, Scheduler& scheduler,
const VideoCore::RendererSettings& renderer_settings,
Swapchain& swapchain, Swapchain& swapchain,
vk::SurfaceKHR& surface); vk::SurfaceKHR& surface);
~PresentManager(); ~PresentManager();
@@ -74,7 +80,9 @@ private:
void RecreateSwapchain(Frame* frame); void RecreateSwapchain(Frame* frame);
void SetImageCount(); void UpdateSwapchainImageCount();
[[nodiscard]] size_t DesiredFrameCount() const;
private: private:
const vk::Instance& instance; const vk::Instance& instance;
@@ -82,6 +90,7 @@ private:
const Device& device; const Device& device;
MemoryAllocator& memory_allocator; MemoryAllocator& memory_allocator;
Scheduler& scheduler; Scheduler& scheduler;
const VideoCore::RendererSettings& renderer_settings;
Swapchain& swapchain; Swapchain& swapchain;
vk::SurfaceKHR& surface; vk::SurfaceKHR& surface;
vk::CommandPool cmdpool; vk::CommandPool cmdpool;
@@ -97,7 +106,7 @@ private:
bool blit_supported; bool blit_supported;
bool storage_supported; bool storage_supported;
bool use_present_thread; bool use_present_thread;
std::size_t image_count{}; std::atomic_size_t swapchain_image_count{};
}; };
} // namespace Vulkan } // namespace Vulkan
@@ -16,6 +16,7 @@
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h" #include "common/settings_enums.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vk_enum_string_helper.h" #include "video_core/vulkan_common/vk_enum_string_helper.h"
@@ -42,13 +43,13 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
} }
static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox, static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
bool has_fifo_relaxed) { bool has_fifo_relaxed, bool frame_gen_enabled) {
// Mailbox doesn't lock the application like FIFO (vsync) // Mailbox doesn't lock the application like FIFO (vsync)
// FIFO present mode locks the framerate to the monitor's refresh rate // FIFO present mode locks the framerate to the monitor's refresh rate
Settings::VSyncMode setting = [has_imm, has_mailbox]() { Settings::VSyncMode setting = [has_imm, has_mailbox, frame_gen_enabled]() {
// Choose Mailbox or Immediate if unlocked and those modes are supported // Choose Mailbox or Immediate if unlocked and those modes are supported
const auto mode = Settings::values.vsync_mode.GetValue(); const auto mode = Settings::values.vsync_mode.GetValue();
if (Settings::values.frame_gen.GetValue()) { if (frame_gen_enabled) {
return mode == Settings::VSyncMode::FifoRelaxed ? mode : Settings::VSyncMode::Fifo; return mode == Settings::VSyncMode::FifoRelaxed ? mode : Settings::VSyncMode::Fifo;
} }
if (Settings::values.use_speed_limit.GetValue() && if (Settings::values.use_speed_limit.GetValue() &&
@@ -121,11 +122,13 @@ Swapchain::Swapchain(
VkSurfaceKHR_T* surface_, VkSurfaceKHR_T* surface_,
const Device& device_, const Device& device_,
Scheduler& scheduler_, Scheduler& scheduler_,
const VideoCore::RendererSettings& renderer_settings_,
u32 width_, u32 width_,
u32 height_) u32 height_)
: surface(surface_) : surface(surface_)
, device{device_} , device{device_}
, scheduler{scheduler_} , scheduler{scheduler_}
, renderer_settings{renderer_settings_}
{ {
Create(surface, width_, height_); Create(surface, width_, height_);
} }
@@ -263,7 +266,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)}; const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
surface_format = ChooseSwapSurfaceFormat(formats); surface_format = ChooseSwapSurfaceFormat(formats);
present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed); present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed,
renderer_settings.GetFrameGenConfig().enabled);
u32 requested_image_count{capabilities.minImageCount + 1}; u32 requested_image_count{capabilities.minImageCount + 1};
// Ensure Triple buffering if possible. // Ensure Triple buffering if possible.
@@ -366,7 +370,9 @@ void Swapchain::Destroy() {
} }
bool Swapchain::NeedsPresentModeUpdate() const { bool Swapchain::NeedsPresentModeUpdate() const {
const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed); const auto requested_mode = ChooseSwapPresentMode(
has_imm, has_mailbox, has_fifo_relaxed,
renderer_settings.GetFrameGenConfig().enabled);
return present_mode != requested_mode; return present_mode != requested_mode;
} }
@@ -15,6 +15,10 @@ namespace Layout {
struct FramebufferLayout; struct FramebufferLayout;
} }
namespace VideoCore {
struct RendererSettings;
}
namespace Vulkan { namespace Vulkan {
class Device; class Device;
@@ -26,6 +30,7 @@ public:
VkSurfaceKHR_T* surface, VkSurfaceKHR_T* surface,
const Device& device, const Device& device,
Scheduler& scheduler, Scheduler& scheduler,
const VideoCore::RendererSettings& renderer_settings,
u32 width, u32 width,
u32 height); u32 height);
~Swapchain(); ~Swapchain();
@@ -122,6 +127,7 @@ private:
const Device& device; const Device& device;
Scheduler& scheduler; Scheduler& scheduler;
const VideoCore::RendererSettings& renderer_settings;
vk::SwapchainKHR swapchain; vk::SwapchainKHR swapchain;