WIP: [NEEDS TESTING & REVIEW] [GPU]: Implement a working Frame-Skip option #263
8 changed files with 66 additions and 35 deletions
|
@ -32,6 +32,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||||
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
|
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
|
||||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||||
PICTURE_IN_PICTURE("picture_in_picture"),
|
PICTURE_IN_PICTURE("picture_in_picture"),
|
||||||
|
FRAME_SKIPPING("frame_skipping"),
|
||||||
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
||||||
BLACK_BACKGROUNDS("black_backgrounds"),
|
BLACK_BACKGROUNDS("black_backgrounds"),
|
||||||
JOYSTICK_REL_CENTER("joystick_rel_center"),
|
JOYSTICK_REL_CENTER("joystick_rel_center"),
|
||||||
|
@ -51,7 +52,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||||
|
|
||||||
ENABLE_RAII("enable_raii"),
|
ENABLE_RAII("enable_raii"),
|
||||||
FRAME_INTERPOLATION("frame_interpolation"),
|
FRAME_INTERPOLATION("frame_interpolation"),
|
||||||
// FRAME_SKIPPING("frame_skipping"),
|
|
||||||
|
|
||||||
PERF_OVERLAY_BACKGROUND("perf_overlay_background"),
|
PERF_OVERLAY_BACKGROUND("perf_overlay_background"),
|
||||||
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
|
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
|
||||||
|
|
|
@ -242,15 +242,13 @@ abstract class SettingsItem(
|
||||||
descriptionId = R.string.frame_interpolation_description
|
descriptionId = R.string.frame_interpolation_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
// put(
|
SwitchSetting(
|
||||||
// SwitchSetting(
|
BooleanSetting.FRAME_SKIPPING,
|
||||||
// BooleanSetting.FRAME_SKIPPING,
|
titleId = R.string.frame_skipping,
|
||||||
// titleId = R.string.frame_skipping,
|
descriptionId = R.string.frame_skipping_description,
|
||||||
// descriptionId = R.string.frame_skipping_description
|
)
|
||||||
// )
|
)
|
||||||
// )
|
|
||||||
|
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
dockedModeSetting,
|
dockedModeSetting,
|
||||||
|
|
|
@ -450,6 +450,7 @@ class SettingsFragmentPresenter(
|
||||||
add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key)
|
add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key)
|
||||||
add(BooleanSetting.BUFFER_REORDER_DISABLE.key)
|
add(BooleanSetting.BUFFER_REORDER_DISABLE.key)
|
||||||
add(BooleanSetting.FRAME_INTERPOLATION.key)
|
add(BooleanSetting.FRAME_INTERPOLATION.key)
|
||||||
|
add(BooleanSetting.FRAME_SKIPPING.key)
|
||||||
add(BooleanSetting.RENDERER_FAST_GPU.key)
|
add(BooleanSetting.RENDERER_FAST_GPU.key)
|
||||||
add(IntSetting.FAST_GPU_TIME.key)
|
add(IntSetting.FAST_GPU_TIME.key)
|
||||||
add(IntSetting.RENDERER_SHADER_BACKEND.key)
|
add(IntSetting.RENDERER_SHADER_BACKEND.key)
|
||||||
|
|
|
@ -482,8 +482,8 @@
|
||||||
<string name="display">Display</string>
|
<string name="display">Display</string>
|
||||||
<string name="processing">Post-Processing</string>
|
<string name="processing">Post-Processing</string>
|
||||||
|
|
||||||
<string name="frame_skipping">WIP: Frameskip</string>
|
<string name="frame_skipping">Frame Skipping</string>
|
||||||
<string name="frame_skipping_description">Toggle frame skipping to improve performance by reducing the number of rendered frames. This feature is still being worked on and will be enabled in future releases.</string>
|
<string name="frame_skipping_description">Toggle frame skipping to improve performance by reducing the number of rendered frames.</string>
|
||||||
<string name="renderer_accuracy">Accuracy level</string>
|
<string name="renderer_accuracy">Accuracy level</string>
|
||||||
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
|
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
|
||||||
<string name="renderer_vsync">VSync mode</string>
|
<string name="renderer_vsync">VSync mode</string>
|
||||||
|
|
|
@ -325,9 +325,8 @@ struct Values {
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
SwitchableSetting<bool> frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer,
|
SwitchableSetting<bool> frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer,
|
||||||
Specialization::RuntimeList};
|
Specialization::RuntimeList};
|
||||||
SwitchableSetting<bool> frame_skipping{linkage, false, "frame_skipping", Category::Renderer,
|
|
||||||
Specialization::RuntimeList};
|
|
||||||
#endif
|
#endif
|
||||||
|
SwitchableSetting<bool> frame_skipping{linkage, false, "frame_skipping", Category::Renderer};
|
||||||
SwitchableSetting<bool> use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
|
SwitchableSetting<bool> use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
SwitchableSetting<SpirvOptimizeMode, true> optimize_spirv_output{linkage,
|
SwitchableSetting<SpirvOptimizeMode, true> optimize_spirv_output{linkage,
|
||||||
|
|
|
@ -223,17 +223,23 @@ struct GPU::Impl {
|
||||||
system.GetPerfStats().EndGameFrame();
|
system.GetPerfStats().EndGameFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetFrameCounter() {
|
||||||
|
frame_count.store(0, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs any additional setup necessary in order to begin GPU emulation.
|
/// Performs any additional setup necessary in order to begin GPU emulation.
|
||||||
/// This can be used to launch any necessary threads and register any necessary
|
/// This can be used to launch any necessary threads and register any necessary
|
||||||
/// core timing events.
|
/// core timing events.
|
||||||
void Start() {
|
void Start() {
|
||||||
Settings::UpdateGPUAccuracy();
|
Settings::UpdateGPUAccuracy();
|
||||||
|
ResetFrameCounter();
|
||||||
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
|
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyShutdown() {
|
void NotifyShutdown() {
|
||||||
std::unique_lock lk{sync_mutex};
|
std::unique_lock lk{sync_mutex};
|
||||||
shutting_down.store(true, std::memory_order::relaxed);
|
shutting_down.store(true, std::memory_order::relaxed);
|
||||||
|
ResetFrameCounter();
|
||||||
sync_cv.notify_all();
|
sync_cv.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,10 +297,16 @@ struct GPU::Impl {
|
||||||
|
|
||||||
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
||||||
std::vector<Service::Nvidia::NvFence>&& fences) {
|
std::vector<Service::Nvidia::NvFence>&& fences) {
|
||||||
|
// Increment frame counter
|
||||||
|
const u64 current_frame = frame_count.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||||
|
const u32 frame_skip_value = Settings::values.frame_skipping.GetValue();
|
||||||
|
const bool skip_frame = frame_skip_value > 0 &&
|
||||||
|
(current_frame % (static_cast<u64>(frame_skip_value) + 1) != 0);
|
||||||
|
|
||||||
size_t num_fences{fences.size()};
|
size_t num_fences{fences.size()};
|
||||||
size_t current_request_counter{};
|
size_t current_request_counter{};
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(request_swap_mutex);
|
std::unique_lock<std::mutex> lk{request_swap_mutex};
|
||||||
if (free_swap_counters.empty()) {
|
if (free_swap_counters.empty()) {
|
||||||
current_request_counter = request_swap_counters.size();
|
current_request_counter = request_swap_counters.size();
|
||||||
request_swap_counters.emplace_back(num_fences);
|
request_swap_counters.emplace_back(num_fences);
|
||||||
|
@ -305,23 +317,33 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto wait_fence =
|
const auto wait_fence =
|
||||||
RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
|
RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences, skip_frame] {
|
||||||
auto& syncpoint_manager = host1x.GetSyncpointManager();
|
auto& syncpoint_manager = host1x.GetSyncpointManager();
|
||||||
if (num_fences == 0) {
|
if (num_fences == 0) {
|
||||||
renderer->Composite(layers);
|
if (!skip_frame) {
|
||||||
}
|
renderer->Composite(layers);
|
||||||
const auto executer = [this, current_request_counter, layers_copy = layers]() {
|
} else {
|
||||||
{
|
system.GetPerfStats().EndGameFrame();
|
||||||
std::unique_lock<std::mutex> lk(request_swap_mutex);
|
}
|
||||||
if (--request_swap_counters[current_request_counter] != 0) {
|
} else {
|
||||||
return;
|
const auto executer = [this, current_request_counter, layers_copy = std::move(layers), skip_frame]() {
|
||||||
}
|
{
|
||||||
free_swap_counters.push_back(current_request_counter);
|
std::unique_lock<std::mutex> lk{request_swap_mutex};
|
||||||
|
if (--request_swap_counters[current_request_counter] != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free_swap_counters.push_back(current_request_counter);
|
||||||
|
}
|
||||||
|
// Handle frame skipping in executer
|
||||||
|
if (!skip_frame) {
|
||||||
|
renderer->Composite(layers_copy);
|
||||||
|
} else {
|
||||||
|
system.GetPerfStats().EndGameFrame();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < num_fences; i++) {
|
||||||
|
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
|
||||||
}
|
}
|
||||||
renderer->Composite(layers_copy);
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < num_fences; i++) {
|
|
||||||
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
gpu_thread.TickGPU();
|
gpu_thread.TickGPU();
|
||||||
|
@ -353,6 +375,8 @@ struct GPU::Impl {
|
||||||
/// When true, we are about to shut down emulation session, so terminate outstanding tasks
|
/// When true, we are about to shut down emulation session, so terminate outstanding tasks
|
||||||
std::atomic_bool shutting_down{};
|
std::atomic_bool shutting_down{};
|
||||||
|
|
||||||
|
std::atomic<u64> frame_count{0};
|
||||||
|
|
||||||
std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
|
std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
|
||||||
|
|
||||||
std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts;
|
std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts;
|
||||||
|
|
|
@ -220,6 +220,9 @@ public:
|
||||||
|
|
||||||
std::vector<u8> GetAppletCaptureBuffer();
|
std::vector<u8> GetAppletCaptureBuffer();
|
||||||
|
|
||||||
|
// Reset frame counter
|
||||||
|
void ResetFrameCounter();
|
||||||
|
|
||||||
/// Performs any additional setup necessary in order to begin GPU emulation.
|
/// Performs any additional setup necessary in order to begin GPU emulation.
|
||||||
/// This can be used to launch any necessary threads and register any necessary
|
/// This can be used to launch any necessary threads and register any necessary
|
||||||
/// core timing events.
|
/// core timing events.
|
||||||
|
|
|
@ -269,6 +269,18 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent)
|
||||||
INSERT(Settings, bg_blue, QString(), QString());
|
INSERT(Settings, bg_blue, QString(), QString());
|
||||||
|
|
||||||
// Renderer (Advanced Graphics)
|
// Renderer (Advanced Graphics)
|
||||||
|
INSERT(Settings,
|
||||||
|
enable_raii,
|
||||||
|
tr("RAII"),
|
||||||
|
tr("A method of automatic resource management in Vulkan "
|
||||||
|
"that ensures proper release of resources "
|
||||||
|
"when they are no longer needed, but may cause crashes in bundled games."));
|
||||||
|
INSERT(Settings,
|
||||||
|
frame_skipping,
|
||||||
|
tr("Frame Skipping"),
|
||||||
|
tr("Skips rendering every other frame while still presenting "
|
||||||
|
"the frame as rendered.\nThis cuts the effective FPS in half, but can make "
|
||||||
|
"games run stable at the game's target FPS on weaker hardware."));
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
async_presentation,
|
async_presentation,
|
||||||
tr("Enable asynchronous presentation (Vulkan only)"),
|
tr("Enable asynchronous presentation (Vulkan only)"),
|
||||||
|
@ -335,12 +347,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent)
|
||||||
tr("Improves rendering of transparency effects in specific games."));
|
tr("Improves rendering of transparency effects in specific games."));
|
||||||
|
|
||||||
// Renderer (Extensions)
|
// Renderer (Extensions)
|
||||||
INSERT(Settings,
|
|
||||||
enable_raii,
|
|
||||||
tr("RAII"),
|
|
||||||
tr("A method of automatic resource management in Vulkan "
|
|
||||||
"that ensures proper release of resources "
|
|
||||||
"when they are no longer needed, but may cause crashes in bundled games."));
|
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
dyna_state,
|
dyna_state,
|
||||||
tr("Extended Dynamic State"),
|
tr("Extended Dynamic State"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue