Compare commits

..

3 Commits

Author SHA1 Message Date
lizzie 3bf713e43e 2026-09-07 23:35:54
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-07 23:35:54 +00:00
lizzie f38dc00727 fix 2026-09-07 23:33:42 +00:00
lizzie 519e2af2ed [hle/ns] implement IReadOnlyApplicationControlDataInterface::ListApplicationIcon
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-07 23:33:42 +00:00
24 changed files with 164 additions and 366 deletions
@@ -114,9 +114,7 @@ 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)
@@ -127,22 +125,16 @@ 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 {
if (switchView.isEnabled) {
switchView.toggle() switchView.toggle()
} }
}
container.addView(itemView) container.addView(itemView)
} }
@@ -185,9 +177,7 @@ 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)
@@ -200,13 +190,10 @@ 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)
@@ -217,8 +204,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, changed -> slider.addOnChangeListener { _, value, chanhed ->
if (changed) { if (chanhed) {
val intValue = value.toInt() val intValue = value.toInt()
when (setting) { when (setting) {
is AbstractShortSetting -> setting.setShort(intValue.toShort()) is AbstractShortSetting -> setting.setShort(intValue.toShort())
@@ -226,7 +213,6 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
} }
saveSettings() saveSettings()
valueDisplay.text = "$intValue$units" valueDisplay.text = "$intValue$units"
onValueChanged?.invoke(intValue)
} }
} }
@@ -92,7 +92,6 @@ 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
@@ -1158,36 +1157,6 @@ 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,7 +22,6 @@
#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) {
@@ -126,14 +125,10 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
} }
float EmuWindow_Android::GetPresentedFrameMultiplier() { float EmuWindow_Android::GetPresentedFrameMultiplier() {
if (EmulationSession::GetInstance().IsRunning()) { if (!Settings::values.frame_gen.GetValue()) {
const VideoCore::FrameGenConfig config = return 1.0f;
EmulationSession::GetInstance().System().Renderer().Settings().GetFrameGenConfig();
return config.enabled ? static_cast<float>(config.multiplier) : 1.0f;
} }
return Settings::values.frame_gen.GetValue() return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4));
? static_cast<float>(Settings::FrameGenMultiplier())
: 1.0f;
} }
float EmuWindow_Android::GetFrameRateHint() const { float EmuWindow_Android::GetFrameRateHint() const {
+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, true}; Specialization::Default, true, false};
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,
true, false,
&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,
true, false,
&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,
true, false,
&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, true, &frame_gen}; Specialization::Default, true, false, &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};
@@ -139,7 +139,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{405, nullptr, "ListApplicationControlCacheEntryInfo"}, {405, nullptr, "ListApplicationControlCacheEntryInfo"},
{406, nullptr, "GetApplicationControlProperty"}, {406, nullptr, "GetApplicationControlProperty"},
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"}, {407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
{408, nullptr, "ListApplicationIcon"}, {408, &IApplicationManagerInterface::ListApplicationIcon, "ListApplicationIcon"},
{411, nullptr, "Unknown411"}, //19.0.0+ {411, nullptr, "Unknown411"}, //19.0.0+
{412, nullptr, "Unknown412"}, //19.0.0+ {412, nullptr, "Unknown412"}, //19.0.0+
{413, nullptr, "Unknown413"}, //19.0.0+ {413, nullptr, "Unknown413"}, //19.0.0+
@@ -894,4 +894,9 @@ void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx)
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx); IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
} }
void IApplicationManagerInterface::ListApplicationIcon(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IReadOnlyApplicationControlDataInterface(system).ListApplicationIcon(ctx);
}
} // namespace Service::NS } // namespace Service::NS
@@ -78,6 +78,7 @@ public:
void PushApplicationRecord(HLERequestContext& ctx); void PushApplicationRecord(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx); void ListApplicationTitle(HLERequestContext& ctx);
void ListApplicationIcon(HLERequestContext& ctx);
private: private:
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
@@ -10,13 +10,17 @@
#include <string> #include <string>
#include "common/stb.h" #include "common/stb.h"
#include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ns/language.h" #include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h" #include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/ns/ns_results.h" #include "core/hle/service/ns/ns_results.h"
@@ -72,24 +76,26 @@ void SanitizeJPEGImageSize(std::vector<u8>& image) {
// IAsyncValue implementation for ListApplicationTitle // IAsyncValue implementation for ListApplicationTitle
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle // https://switchbrew.org/wiki/NS_services#ListApplicationTitle
class IAsyncValueForListApplicationTitle final : public ServiceFramework<IAsyncValueForListApplicationTitle> { class IAsyncValue final : public ServiceFramework<IAsyncValue> {
public: public:
explicit IAsyncValueForListApplicationTitle(Core::System& system_, s32 offset, s32 size) explicit IAsyncValue(Core::System& system_, s32 offset, s32 size)
: ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"}, : ServiceFramework{system_, "IAsyncValue"}
data_offset{offset}, data_size{size} { , service_context{system_, "IAsyncValue"}
, data_offset{offset}
, data_size{size}
{
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IAsyncValueForListApplicationTitle::GetSize, "GetSize"}, {0, D<&IAsyncValue::GetSize>, "GetSize"},
{1, &IAsyncValueForListApplicationTitle::Get, "Get"}, {1, D<&IAsyncValue::Get>, "Get"},
{2, &IAsyncValueForListApplicationTitle::Cancel, "Cancel"}, {2, D<&IAsyncValue::Cancel>, "Cancel"},
{3, &IAsyncValueForListApplicationTitle::GetErrorContext, "GetErrorContext"}, {3, D<&IAsyncValue::GetErrorContext>, "GetErrorContext"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
completion_event = service_context.CreateEvent("IAsyncValue:Completion"); completion_event = service_context.CreateEvent("IAsyncValue:Completion");
completion_event->GetReadableEvent().Signal(system.Kernel()); completion_event->GetReadableEvent().Signal(system.Kernel());
} }
~IAsyncValueForListApplicationTitle() override { ~IAsyncValue() override {
service_context.CloseEvent(completion_event); service_context.CloseEvent(completion_event);
} }
@@ -98,35 +104,24 @@ public:
} }
private: private:
void GetSize(HLERequestContext& ctx) { Result GetSize(Out<s64> out_data_size) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 4}; *out_data_size = data_size;
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push<s64>(data_size);
} }
Result Get(OutBuffer<BufferAttr_HipcMapAlias> out_data_offset) {
void Get(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
std::vector<u8> buffer(sizeof(s32)); std::memcpy(out_data_offset.data(), &data_offset, sizeof(s32));
std::memcpy(buffer.data(), &data_offset, sizeof(s32)); R_SUCCEED();
ctx.WriteBuffer(buffer);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result Cancel() {
void Cancel(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
} }
Result GetErrorContext() {
void GetErrorContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
} }
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
Kernel::KEvent* completion_event{}; Kernel::KEvent* completion_event{};
s32 data_offset; s32 data_offset;
@@ -144,6 +139,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{4, nullptr, "SelectApplicationDesiredLanguage"}, {4, nullptr, "SelectApplicationDesiredLanguage"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"}, {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
{10, &IReadOnlyApplicationControlDataInterface::ListApplicationIcon, "ListApplicationIcon"},
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"}, {13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"}, {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
}; };
@@ -160,8 +156,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
application_control_source, application_id); application_control_source, application_id);
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()};
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
const auto size = out_buffer.size(); const auto size = out_buffer.size();
@@ -169,8 +164,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
const auto total_size = sizeof(FileSys::RawNACP) + icon_size; const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
if (size < total_size) { if (size < total_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
size);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@@ -178,8 +172,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
const auto bytes = control.first->GetRawBytes(); const auto bytes = control.first->GetRawBytes();
std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
} else { } else {
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", application_id);
application_id);
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP)); std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
} }
@@ -204,15 +197,12 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
// Convert to application language, get priority list // Convert to application language, get priority list
const auto application_language = ConvertToApplicationLanguage(language_code); const auto application_language = ConvertToApplicationLanguage(language_code);
if (application_language == std::nullopt) { if (application_language == std::nullopt) {
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", language_code);
language_code);
R_THROW(Service::NS::ResultApplicationLanguageNotFound); R_THROW(Service::NS::ResultApplicationLanguageNotFound);
} }
const auto priority_list = GetApplicationLanguagePriorityList(*application_language); const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
if (!priority_list) { if (!priority_list) {
LOG_ERROR(Service_NS, LOG_ERROR(Service_NS, "Could not find application language priorities! application_language={}", *application_language);
"Could not find application language priorities! application_language={}",
*application_language);
R_THROW(Service::NS::ResultApplicationLanguageNotFound); R_THROW(Service::NS::ResultApplicationLanguageNotFound);
} }
@@ -256,8 +246,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
const auto nacp_size = sizeof(FileSys::RawNACP); const auto nacp_size = sizeof(FileSys::RawNACP);
if (size < nacp_size) { if (size < nacp_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", size, nacp_size);
size, nacp_size);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@@ -308,63 +297,81 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
R_SUCCEED(); R_SUCCEED();
} }
void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestContext& ctx) {
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) { LOG_WARNING(Service_NS, "(stubbed)");
/*
IPC::RequestParser rp{ctx};
auto control_source = rp.PopRaw<u8>();
rp.Skip(7, false);
auto transfer_memory_size = rp.Pop<u64>();
*/
const auto app_ids_buffer = ctx.ReadBuffer(); const auto app_ids_buffer = ctx.ReadBuffer();
const size_t app_count = app_ids_buffer.size() / sizeof(u64); const u64 app_count = app_ids_buffer.size() / sizeof(u64);
std::vector<u64> application_ids(app_count);
if (app_count > 0) {
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
}
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0)); auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe(); auto* t_mem = t_mem_obj.GetPointerUnsafe();
size_t out_length = 0;
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
const size_t total_data_size = app_count * title_entry_size;
constexpr s32 data_offset = 0;
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) { if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
auto& memory = t_mem->GetOwner()->GetMemory(); auto& memory = t_mem->GetOwner()->GetMemory();
const auto t_mem_address = t_mem->GetSourceAddress(); const auto t_mem_address = t_mem->GetSourceAddress();
// u64 - app count
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
out_length += sizeof(u64);
// [list of u64] - size of icons
for (size_t i = 0; i < app_count; ++i) { for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = application_ids[i]; const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
u64 full_size = control.second->GetSize();
FileSys::LanguageEntry entry{}; memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
if (control.first != nullptr) { out_length += sizeof(u64);
entry = control.first->GetLanguageEntry();
} }
// [list of raw icon data]
const size_t offset = i * title_entry_size; std::vector<u8> full_icon_data;
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size); for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
auto const full_size = control.second->GetSize();
if (full_size > 0) {
full_icon_data.resize(full_size);
control.second->Read(full_icon_data.data(), full_size, 0);
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
out_length += full_size;
} }
} }
}
auto async_value = std::make_shared<IAsyncValueForListApplicationTitle>( auto async_value = std::make_shared<IAsyncValue>(system, 0, s32(out_length));
system, data_offset, static_cast<s32>(total_data_size));
IPC::ResponseBuilder rb{ctx, 2, 1, 1}; IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent()); rb.PushCopyObjects(ctx, async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value)); rb.PushIpcInterface(ctx, std::move(async_value));
} }
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3( void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b, const auto app_ids_buffer = ctx.ReadBuffer();
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) { const size_t app_count = app_ids_buffer.size() / sizeof(u64);
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe();
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
const size_t total_data_size = app_count * title_entry_size;
constexpr s32 data_offset = 0;
if (t_mem != nullptr && app_count > 0) {
auto& memory = system.ApplicationMemory();
const auto t_mem_address = t_mem->GetSourceAddress();
for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
FileSys::LanguageEntry entry{};
if (control.first != nullptr) {
entry = control.first->GetLanguageEntry();
}
const size_t offset = i * title_entry_size;
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
}
}
auto async_value = std::make_shared<IAsyncValue>(system, data_offset, s32(total_data_size));
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value));
}
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b, Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}", LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
application_control_source, flag1, flag2, application_id); application_control_source, flag1, flag2, application_id);
@@ -34,6 +34,7 @@ public:
u8 flag1, u8 flag1,
u8 flag2, u8 flag2,
u64 application_id); u64 application_id);
void ListApplicationIcon(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx); void ListApplicationTitle(HLERequestContext& ctx);
Result GetApplicationControlData3( Result GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 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
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -16,10 +16,8 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"}, {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
{1, nullptr, "NotifyApplicationFailure"}, {1, nullptr, "NotifyApplicationFailure"},
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, "IsDataCorruptedResult"},
"IsDataCorruptedResult"}, {3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>, "ListApplicationRecord"},
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
"ListApplicationRecord"},
}; };
// clang-format on // clang-format on
-1
View File
@@ -61,7 +61,6 @@ 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
@@ -1,43 +0,0 @@
// 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,7 +7,6 @@
#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"
@@ -23,16 +22,6 @@ 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,12 +9,10 @@
#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"
@@ -32,20 +30,6 @@ 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,14 +23,13 @@ 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(const VideoCore::FrameGenConfig& config) { [[nodiscard]] f32 ManualFlowScale() {
return static_cast<f32>(config.flow_scale) / 100.0f; return static_cast<f32>(Settings::values.frame_gen_flow_scale.GetValue()) / 100.0f;
} }
[[nodiscard]] f32 ConfiguredFlowScale(const VideoCore::FrameGenConfig& config, [[nodiscard]] f32 ConfiguredFlowScale(VkExtent2D guest_extent, VkExtent2D presented_extent) {
VkExtent2D guest_extent, VkExtent2D presented_extent) { if (!Settings::values.frame_gen_flow_scale_auto.GetValue()) {
if (!config.flow_scale_auto) { return ManualFlowScale();
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;
@@ -195,61 +194,15 @@ FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
FrameGen::~FrameGen() = default; FrameGen::~FrameGen() = default;
void FrameGen::UpdateConfig(const VideoCore::FrameGenConfig& new_config) {
if (!has_config) {
config = new_config;
has_config = true;
return;
}
if (config == new_config) {
return;
}
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, void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
VkExtent2D guest_extent) { VkExtent2D guest_extent) {
generated = false; generated = false;
if (unavailable || !config.enabled) { if (unavailable || !Settings::values.frame_gen.GetValue()) {
if (chain) {
scheduler.Finish();
chain.reset();
}
warm_streak = 0; warm_streak = 0;
return; return;
} }
@@ -260,7 +213,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
} }
if (!shaders) { if (!shaders) {
shaders.emplace(device, config.fp16); shaders.emplace(device);
if (!shaders->IsValid()) { if (!shaders->IsValid()) {
unavailable = true; unavailable = true;
return; return;
@@ -271,7 +224,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(config, peak_guest_extent, extent); const f32 flow_scale = ConfiguredFlowScale(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);
@@ -294,7 +247,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
} }
}); });
const bool dump_requested = generated && config.dump_flow; const bool dump_requested = generated && Settings::values.frame_gen_dump_flow.GetValue();
if (!dump_requested) { if (!dump_requested) {
dumped = false; dumped = false;
} else if (!dumped) { } else if (!dumped) {
@@ -308,7 +261,7 @@ size_t FrameGen::WantedGenerations(size_t capacity) {
plan = {}; plan = {};
return 0; return 0;
} }
plan = pacer.Plan(capacity, config); plan = pacer.Plan(capacity);
return plan.generations; return plan.generations;
} }
@@ -6,7 +6,6 @@
#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"
@@ -23,8 +22,6 @@ 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);
@@ -42,7 +39,6 @@ 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{};
@@ -56,7 +52,6 @@ private:
bool generated{}; bool generated{};
bool unavailable{}; bool unavailable{};
bool dumped{}; bool dumped{};
bool has_config{};
}; };
} // namespace Vulkan } // namespace Vulkan
@@ -5,6 +5,7 @@
#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 {
@@ -45,8 +46,8 @@ constexpr auto PROBE_STEP_DELAY = std::chrono::milliseconds(250);
} // Anonymous namespace } // Anonymous namespace
FrameGenPlan FrameGenPacer::Plan(size_t capacity, const VideoCore::FrameGenConfig& config) { FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
const size_t ceiling = std::min(capacity, config.MaxGenerations()); const size_t ceiling = std::min(capacity, Settings::FrameGenMaxGenerations());
if (ceiling == 0) { if (ceiling == 0) {
Reset(); Reset();
return {}; return {};
@@ -68,7 +69,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity, const VideoCore::FrameGenConfi
return {}; return {};
} }
const f32 target_rate = static_cast<f32>(config.target_rate); const f32 target_rate = static_cast<f32>(Settings::values.frame_gen_target_rate.GetValue());
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;
@@ -108,7 +109,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity, const VideoCore::FrameGenConfi
} }
if (target_rate == 0.0f) { if (target_rate == 0.0f) {
limit = std::min(config.Generations(), ceiling); limit = std::min(Settings::FrameGenGenerations(), 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,7 +7,6 @@
#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 {
@@ -18,7 +17,7 @@ struct FrameGenPlan {
class FrameGenPacer { class FrameGenPacer {
public: public:
[[nodiscard]] FrameGenPlan Plan(size_t capacity, const VideoCore::FrameGenConfig& config); [[nodiscard]] FrameGenPlan Plan(size_t capacity);
void Reset(); void Reset();
@@ -1,6 +1,7 @@
// 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"
@@ -8,15 +9,16 @@
namespace Vulkan { namespace Vulkan {
LsfgShaders::LsfgShaders(const Device& device, bool prefer_fp16) { LsfgShaders::LsfgShaders(const Device& device) {
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, allow_fp16 && prefer_fp16) != if (VideoCore::FrameGen::LoadShaderModules(code, 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, bool prefer_fp16); explicit LsfgShaders(const Device& device);
[[nodiscard]] bool IsValid() const { [[nodiscard]] bool IsValid() const {
return valid; return valid;
@@ -145,7 +145,6 @@ 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,
@@ -153,7 +152,6 @@ try
device, device,
memory_allocator, memory_allocator,
scheduler, scheduler,
Settings(),
swapchain, swapchain,
surface) surface)
, blit_swapchain(device_memory, , blit_swapchain(device_memory,
@@ -208,9 +206,6 @@ 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,7 +7,6 @@
#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
@@ -30,6 +29,9 @@ 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
@@ -118,7 +120,6 @@ 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_}
@@ -126,14 +127,13 @@ 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()}
{ {
UpdateSwapchainImageCount(); SetImageCount();
auto& dld = device.GetLogical(); auto& dld = device.GetLogical();
cmdpool = dld.CreateCommandPool({ cmdpool = dld.CreateCommandPool({
@@ -143,14 +143,9 @@ 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(),
}); });
#ifdef HAS_LSFG auto cmdbuffers = cmdpool.Allocate(image_count);
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(frame_capacity); frames.resize(image_count);
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;
@@ -179,10 +174,7 @@ 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] { free_cv.wait(lock, [this] { return !free_queue.empty(); });
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();
@@ -210,8 +202,7 @@ void PresentManager::Present(Frame* frame) {
} }
size_t PresentManager::MaxExtraFrames() const { size_t PresentManager::MaxExtraFrames() const {
const size_t frame_count = DesiredFrameCount(); return image_count - 1;
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,
@@ -360,30 +351,22 @@ 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
UpdateSwapchainImageCount(); SetImageCount();
} }
void PresentManager::UpdateSwapchainImageCount() { void PresentManager::SetImageCount() {
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 VideoCore::FrameGenConfig config = renderer_settings.GetFrameGenConfig(); const size_t generations = Settings::FrameGenMaxGenerations();
if (config.enabled) { const size_t queued_composites = Settings::values.frame_gen_queue_target.GetValue() + 1;
const size_t queued_composites = std::clamp<size_t>(config.queue_target, 0, 2) + 1; image_count =
return std::clamp<size_t>((config.MaxGenerations() + 1) * queued_composites, minimum, std::clamp<size_t>((generations + 1) * queued_composites, swapchain.GetImageCount(),
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,7 +6,6 @@
#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>
@@ -20,10 +19,6 @@ 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;
@@ -50,7 +45,6 @@ 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();
@@ -80,9 +74,7 @@ private:
void RecreateSwapchain(Frame* frame); void RecreateSwapchain(Frame* frame);
void UpdateSwapchainImageCount(); void SetImageCount();
[[nodiscard]] size_t DesiredFrameCount() const;
private: private:
const vk::Instance& instance; const vk::Instance& instance;
@@ -90,7 +82,6 @@ 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;
@@ -106,7 +97,7 @@ private:
bool blit_supported; bool blit_supported;
bool storage_supported; bool storage_supported;
bool use_present_thread; bool use_present_thread;
std::atomic_size_t swapchain_image_count{}; std::size_t image_count{};
}; };
} // namespace Vulkan } // namespace Vulkan
@@ -16,7 +16,6 @@
#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"
@@ -43,13 +42,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 frame_gen_enabled) { bool has_fifo_relaxed) {
// 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, frame_gen_enabled]() { Settings::VSyncMode setting = [has_imm, has_mailbox]() {
// 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 (frame_gen_enabled) { if (Settings::values.frame_gen.GetValue()) {
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() &&
@@ -122,13 +121,11 @@ 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_);
} }
@@ -266,8 +263,7 @@ 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.
@@ -370,9 +366,7 @@ void Swapchain::Destroy() {
} }
bool Swapchain::NeedsPresentModeUpdate() const { bool Swapchain::NeedsPresentModeUpdate() const {
const auto requested_mode = ChooseSwapPresentMode( const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
has_imm, has_mailbox, has_fifo_relaxed,
renderer_settings.GetFrameGenConfig().enabled);
return present_mode != requested_mode; return present_mode != requested_mode;
} }
@@ -15,10 +15,6 @@ namespace Layout {
struct FramebufferLayout; struct FramebufferLayout;
} }
namespace VideoCore {
struct RendererSettings;
}
namespace Vulkan { namespace Vulkan {
class Device; class Device;
@@ -30,7 +26,6 @@ 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();
@@ -127,7 +122,6 @@ private:
const Device& device; const Device& device;
Scheduler& scheduler; Scheduler& scheduler;
const VideoCore::RendererSettings& renderer_settings;
vk::SwapchainKHR swapchain; vk::SwapchainKHR swapchain;