mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-09 21:47:56 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc04115e87 |
@@ -114,7 +114,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
name: Int,
|
||||
|
||||
container: ViewGroup,
|
||||
setting: BooleanSetting
|
||||
setting: BooleanSetting,
|
||||
isEnabled: Boolean = true,
|
||||
onValueChanged: ((Boolean) -> Unit)? = null
|
||||
) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
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)
|
||||
switchContainer.visibility = View.VISIBLE
|
||||
switchContainer.isEnabled = isEnabled
|
||||
switchView.isChecked = setting.getBoolean()
|
||||
switchView.isEnabled = isEnabled
|
||||
itemView.alpha = if (isEnabled) 1.0f else 0.5f
|
||||
|
||||
switchView.setOnCheckedChangeListener { _, isChecked ->
|
||||
setting.setBoolean(isChecked)
|
||||
saveSettings()
|
||||
onValueChanged?.invoke(isChecked)
|
||||
}
|
||||
|
||||
switchContainer.setOnClickListener {
|
||||
switchView.toggle()
|
||||
if (switchView.isEnabled) {
|
||||
switchView.toggle()
|
||||
}
|
||||
}
|
||||
container.addView(itemView)
|
||||
}
|
||||
@@ -177,7 +185,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
setting: AbstractSetting,
|
||||
minValue: Int = 0,
|
||||
maxValue: Int = 100,
|
||||
units: String = ""
|
||||
units: String = "",
|
||||
isEnabled: Boolean = true,
|
||||
onValueChanged: ((Int) -> Unit)? = null
|
||||
) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
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)
|
||||
sliderContainer.visibility = View.VISIBLE
|
||||
sliderContainer.isEnabled = isEnabled
|
||||
|
||||
slider.valueFrom = minValue.toFloat()
|
||||
slider.valueTo = maxValue.toFloat()
|
||||
slider.stepSize = 1f
|
||||
slider.isEnabled = isEnabled
|
||||
itemView.alpha = if (isEnabled) 1.0f else 0.5f
|
||||
val currentValue = when (setting) {
|
||||
is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt()
|
||||
is AbstractIntSetting -> setting.getInt(needsGlobal = false)
|
||||
@@ -204,8 +217,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
val displayValue = "${slider.value.toInt()}$units"
|
||||
valueDisplay.text = displayValue
|
||||
|
||||
slider.addOnChangeListener { _, value, chanhed ->
|
||||
if (chanhed) {
|
||||
slider.addOnChangeListener { _, value, changed ->
|
||||
if (changed) {
|
||||
val intValue = value.toInt()
|
||||
when (setting) {
|
||||
is AbstractShortSetting -> setting.setShort(intValue.toShort())
|
||||
@@ -213,6 +226,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
}
|
||||
saveSettings()
|
||||
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.InputHandler
|
||||
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.NativeFreedrenoConfig
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||
@@ -1157,6 +1158,36 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
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(
|
||||
R.string.renderer_accuracy,
|
||||
container,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "input_common/main.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "jni/native.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||
if (!surface) {
|
||||
@@ -125,10 +126,14 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
|
||||
}
|
||||
|
||||
float EmuWindow_Android::GetPresentedFrameMultiplier() {
|
||||
if (!Settings::values.frame_gen.GetValue()) {
|
||||
return 1.0f;
|
||||
if (EmulationSession::GetInstance().IsRunning()) {
|
||||
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 {
|
||||
|
||||
@@ -389,7 +389,7 @@ struct Values {
|
||||
true};
|
||||
|
||||
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,
|
||||
2,
|
||||
@@ -399,7 +399,7 @@ struct Values {
|
||||
Category::Renderer,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
|
||||
@@ -419,7 +419,7 @@ struct Values {
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
|
||||
@@ -442,11 +442,11 @@ struct Values {
|
||||
Category::Renderer,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
&frame_gen};
|
||||
|
||||
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",
|
||||
Category::Renderer};
|
||||
|
||||
@@ -61,6 +61,7 @@ add_library(video_core STATIC
|
||||
engines/puller.h
|
||||
framebuffer_config.cpp
|
||||
framebuffer_config.h
|
||||
frame_gen/frame_gen_config.h
|
||||
fsr.cpp
|
||||
fsr.h
|
||||
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
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/graphics_context.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -22,6 +23,16 @@ RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
|
||||
RendererBase::~RendererBase() = default;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "video_core/frame_gen/frame_gen_config.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
@@ -30,6 +32,20 @@ struct RendererSettings {
|
||||
std::function<void(bool)> screenshot_complete_callback;
|
||||
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||
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 {
|
||||
|
||||
@@ -23,13 +23,14 @@ constexpr size_t COLOR_CHANNELS = 4;
|
||||
constexpr u64 LSFG_REQUIRED_FRAMES = 2;
|
||||
constexpr u32 LSFG_RECURRENCE_FRAMES = 2;
|
||||
|
||||
[[nodiscard]] f32 ManualFlowScale() {
|
||||
return static_cast<f32>(Settings::values.frame_gen_flow_scale.GetValue()) / 100.0f;
|
||||
[[nodiscard]] f32 ManualFlowScale(const VideoCore::FrameGenConfig& config) {
|
||||
return static_cast<f32>(config.flow_scale) / 100.0f;
|
||||
}
|
||||
|
||||
[[nodiscard]] f32 ConfiguredFlowScale(VkExtent2D guest_extent, VkExtent2D presented_extent) {
|
||||
if (!Settings::values.frame_gen_flow_scale_auto.GetValue()) {
|
||||
return ManualFlowScale();
|
||||
[[nodiscard]] f32 ConfiguredFlowScale(const VideoCore::FrameGenConfig& config,
|
||||
VkExtent2D guest_extent, VkExtent2D presented_extent) {
|
||||
if (!config.flow_scale_auto) {
|
||||
return ManualFlowScale(config);
|
||||
}
|
||||
if (guest_extent.width == 0 || presented_extent.width == 0) {
|
||||
return 1.0f;
|
||||
@@ -194,15 +195,61 @@ FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
|
||||
|
||||
FrameGen::~FrameGen() = default;
|
||||
|
||||
void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
||||
VkExtent2D guest_extent) {
|
||||
generated = false;
|
||||
void FrameGen::UpdateConfig(const VideoCore::FrameGenConfig& new_config) {
|
||||
if (!has_config) {
|
||||
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) {
|
||||
scheduler.Finish();
|
||||
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;
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +260,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
||||
}
|
||||
|
||||
if (!shaders) {
|
||||
shaders.emplace(device);
|
||||
shaders.emplace(device, config.fp16);
|
||||
if (!shaders->IsValid()) {
|
||||
unavailable = true;
|
||||
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);
|
||||
|
||||
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 ||
|
||||
built_format != format || built_flow_scale != 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) {
|
||||
dumped = false;
|
||||
} else if (!dumped) {
|
||||
@@ -261,7 +308,7 @@ size_t FrameGen::WantedGenerations(size_t capacity) {
|
||||
plan = {};
|
||||
return 0;
|
||||
}
|
||||
plan = pacer.Plan(capacity);
|
||||
plan = pacer.Plan(capacity, config);
|
||||
return plan.generations;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <optional>
|
||||
|
||||
#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/lsfg_chain.h"
|
||||
#include "video_core/renderer_vulkan/present/lsfg_shaders.h"
|
||||
@@ -22,6 +23,8 @@ public:
|
||||
explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler);
|
||||
~FrameGen();
|
||||
|
||||
void UpdateConfig(const VideoCore::FrameGenConfig& new_config);
|
||||
|
||||
void Process(const Device& device, Frame* frame, VkFormat format, VkExtent2D guest_extent);
|
||||
|
||||
[[nodiscard]] size_t WantedGenerations(size_t capacity);
|
||||
@@ -39,6 +42,7 @@ private:
|
||||
|
||||
std::optional<LsfgShaders> shaders;
|
||||
std::optional<LsfgChain> chain;
|
||||
VideoCore::FrameGenConfig config;
|
||||
FrameGenPacer pacer;
|
||||
FrameGenPlan plan{};
|
||||
VkExtent2D peak_guest_extent{};
|
||||
@@ -52,6 +56,7 @@ private:
|
||||
bool generated{};
|
||||
bool unavailable{};
|
||||
bool dumped{};
|
||||
bool has_config{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -46,8 +45,8 @@ constexpr auto PROBE_STEP_DELAY = std::chrono::milliseconds(250);
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
|
||||
const size_t ceiling = std::min(capacity, Settings::FrameGenMaxGenerations());
|
||||
FrameGenPlan FrameGenPacer::Plan(size_t capacity, const VideoCore::FrameGenConfig& config) {
|
||||
const size_t ceiling = std::min(capacity, config.MaxGenerations());
|
||||
if (ceiling == 0) {
|
||||
Reset();
|
||||
return {};
|
||||
@@ -69,7 +68,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
|
||||
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) {
|
||||
f32 burst_threshold = BURST_CADENCE_RATIO / smoothed_interval;
|
||||
@@ -109,7 +108,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
|
||||
}
|
||||
|
||||
if (target_rate == 0.0f) {
|
||||
limit = std::min(Settings::FrameGenGenerations(), ceiling);
|
||||
limit = std::min(config.Generations(), ceiling);
|
||||
output_credit = 0.0f;
|
||||
issued_generations = limit;
|
||||
return {.generations = limit, .warm = limit > 0};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/frame_gen/frame_gen_config.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@@ -17,7 +18,7 @@ struct FrameGenPlan {
|
||||
|
||||
class FrameGenPacer {
|
||||
public:
|
||||
[[nodiscard]] FrameGenPlan Plan(size_t capacity);
|
||||
[[nodiscard]] FrameGenPlan Plan(size_t capacity, const VideoCore::FrameGenConfig& config);
|
||||
|
||||
void Reset();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "video_core/frame_gen/lossless_dll.h"
|
||||
#include "video_core/renderer_vulkan/present/lsfg_shaders.h"
|
||||
#include "video_core/renderer_vulkan/present/util.h"
|
||||
@@ -9,16 +8,15 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
LsfgShaders::LsfgShaders(const Device& device) {
|
||||
LsfgShaders::LsfgShaders(const Device& device, bool prefer_fp16) {
|
||||
if (!device.IsVulkanMemoryModelSupported() || !device.HasNullDescriptor()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool allow_fp16 = device.IsFloat16Supported();
|
||||
const bool prefer_fp16 = allow_fp16 && Settings::values.frame_gen_fp16.GetValue();
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class Device;
|
||||
|
||||
class LsfgShaders {
|
||||
public:
|
||||
explicit LsfgShaders(const Device& device);
|
||||
explicit LsfgShaders(const Device& device, bool prefer_fp16);
|
||||
|
||||
[[nodiscard]] bool IsValid() const {
|
||||
return valid;
|
||||
|
||||
@@ -145,6 +145,7 @@ try
|
||||
, swapchain(*surface,
|
||||
device,
|
||||
scheduler,
|
||||
Settings(),
|
||||
render_window.GetFramebufferLayout().width,
|
||||
render_window.GetFramebufferLayout().height)
|
||||
, present_manager(instance,
|
||||
@@ -152,6 +153,7 @@ try
|
||||
device,
|
||||
memory_allocator,
|
||||
scheduler,
|
||||
Settings(),
|
||||
swapchain,
|
||||
surface)
|
||||
, blit_swapchain(device_memory,
|
||||
@@ -206,6 +208,9 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
||||
}
|
||||
|
||||
RenderScreenshot(framebuffers);
|
||||
#ifdef HAS_LSFG
|
||||
frame_gen.UpdateConfig(Settings().GetFrameGenConfig());
|
||||
#endif
|
||||
Frame* frame = present_manager.GetRenderFrame();
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#ifdef HAS_LSFG
|
||||
#include "video_core/renderer_vulkan/present/lsfg_common.h"
|
||||
#endif
|
||||
@@ -29,9 +30,6 @@ static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS);
|
||||
|
||||
bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) {
|
||||
#ifdef HAS_LSFG
|
||||
if (!Settings::values.frame_gen.GetValue()) {
|
||||
return false;
|
||||
}
|
||||
const VkFormatProperties props{physical_device.GetFormatProperties(format)};
|
||||
return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
|
||||
#else
|
||||
@@ -120,6 +118,7 @@ PresentManager::PresentManager(const vk::Instance& instance_,
|
||||
const Device& device_,
|
||||
MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_,
|
||||
const VideoCore::RendererSettings& renderer_settings_,
|
||||
Swapchain& swapchain_,
|
||||
vk::SurfaceKHR& surface_)
|
||||
: instance{instance_}
|
||||
@@ -127,13 +126,14 @@ PresentManager::PresentManager(const vk::Instance& instance_,
|
||||
, device{device_}
|
||||
, memory_allocator{memory_allocator_}
|
||||
, scheduler{scheduler_}
|
||||
, renderer_settings{renderer_settings_}
|
||||
, swapchain{swapchain_}
|
||||
, surface{surface_}
|
||||
, blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}
|
||||
, storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())}
|
||||
, use_present_thread{Settings::values.async_presentation.GetValue()}
|
||||
{
|
||||
SetImageCount();
|
||||
UpdateSwapchainImageCount();
|
||||
|
||||
auto& dld = device.GetLogical();
|
||||
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,
|
||||
.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++) {
|
||||
Frame& frame = frames[i];
|
||||
frame.index = i;
|
||||
@@ -174,7 +179,10 @@ Frame* PresentManager::GetRenderFrame() {
|
||||
|
||||
// Wait for free presentation frames
|
||||
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
|
||||
Frame* frame = free_queue.front();
|
||||
@@ -202,7 +210,8 @@ void PresentManager::Present(Frame* frame) {
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -351,22 +360,30 @@ void PresentManager::PresentThread(std::stop_token token) {
|
||||
|
||||
void PresentManager::RecreateSwapchain(Frame* frame) {
|
||||
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.
|
||||
// FRAMES_IN_FLIGHT is 8, and the cache TICKS_TO_DESTROY is 8.
|
||||
// Mali drivers will give us 6.
|
||||
const size_t minimum = swapchain_image_count.load(std::memory_order_acquire);
|
||||
#ifdef HAS_LSFG
|
||||
const size_t generations = Settings::FrameGenMaxGenerations();
|
||||
const size_t queued_composites = Settings::values.frame_gen_queue_target.GetValue() + 1;
|
||||
image_count =
|
||||
std::clamp<size_t>((generations + 1) * queued_composites, swapchain.GetImageCount(),
|
||||
MAX_FRAMES_IN_FLIGHT);
|
||||
#else
|
||||
image_count = std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT);
|
||||
const VideoCore::FrameGenConfig config = renderer_settings.GetFrameGenConfig();
|
||||
if (config.enabled) {
|
||||
const size_t queued_composites = std::clamp<size_t>(config.queue_target, 0, 2) + 1;
|
||||
return std::clamp<size_t>((config.MaxGenerations() + 1) * queued_composites, minimum,
|
||||
MAX_FRAMES_IN_FLIGHT);
|
||||
}
|
||||
#endif
|
||||
return minimum;
|
||||
}
|
||||
|
||||
void PresentManager::CopyToSwapchain(Frame* frame) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <boost/container/deque.hpp>
|
||||
@@ -19,6 +20,10 @@ namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace VideoCore {
|
||||
struct RendererSettings;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
@@ -45,6 +50,7 @@ public:
|
||||
const Device& device,
|
||||
MemoryAllocator& memory_allocator,
|
||||
Scheduler& scheduler,
|
||||
const VideoCore::RendererSettings& renderer_settings,
|
||||
Swapchain& swapchain,
|
||||
vk::SurfaceKHR& surface);
|
||||
~PresentManager();
|
||||
@@ -74,7 +80,9 @@ private:
|
||||
|
||||
void RecreateSwapchain(Frame* frame);
|
||||
|
||||
void SetImageCount();
|
||||
void UpdateSwapchainImageCount();
|
||||
|
||||
[[nodiscard]] size_t DesiredFrameCount() const;
|
||||
|
||||
private:
|
||||
const vk::Instance& instance;
|
||||
@@ -82,6 +90,7 @@ private:
|
||||
const Device& device;
|
||||
MemoryAllocator& memory_allocator;
|
||||
Scheduler& scheduler;
|
||||
const VideoCore::RendererSettings& renderer_settings;
|
||||
Swapchain& swapchain;
|
||||
vk::SurfaceKHR& surface;
|
||||
vk::CommandPool cmdpool;
|
||||
@@ -97,7 +106,7 @@ private:
|
||||
bool blit_supported;
|
||||
bool storage_supported;
|
||||
bool use_present_thread;
|
||||
std::size_t image_count{};
|
||||
std::atomic_size_t swapchain_image_count{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.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_swapchain.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,
|
||||
bool has_fifo_relaxed) {
|
||||
bool has_fifo_relaxed, bool frame_gen_enabled) {
|
||||
// Mailbox doesn't lock the application like FIFO (vsync)
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
if (Settings::values.use_speed_limit.GetValue() &&
|
||||
@@ -121,11 +122,13 @@ Swapchain::Swapchain(
|
||||
VkSurfaceKHR_T* surface_,
|
||||
const Device& device_,
|
||||
Scheduler& scheduler_,
|
||||
const VideoCore::RendererSettings& renderer_settings_,
|
||||
u32 width_,
|
||||
u32 height_)
|
||||
: surface(surface_)
|
||||
, device{device_}
|
||||
, scheduler{scheduler_}
|
||||
, renderer_settings{renderer_settings_}
|
||||
{
|
||||
Create(surface, width_, height_);
|
||||
}
|
||||
@@ -263,7 +266,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
|
||||
|
||||
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
|
||||
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};
|
||||
// Ensure Triple buffering if possible.
|
||||
@@ -366,7 +370,9 @@ void Swapchain::Destroy() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace Layout {
|
||||
struct FramebufferLayout;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
struct RendererSettings;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
@@ -26,6 +30,7 @@ public:
|
||||
VkSurfaceKHR_T* surface,
|
||||
const Device& device,
|
||||
Scheduler& scheduler,
|
||||
const VideoCore::RendererSettings& renderer_settings,
|
||||
u32 width,
|
||||
u32 height);
|
||||
~Swapchain();
|
||||
@@ -122,6 +127,7 @@ private:
|
||||
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
const VideoCore::RendererSettings& renderer_settings;
|
||||
|
||||
vk::SwapchainKHR swapchain;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user