mirror of
https://git.uzuy-edge.org/Uzuy-Edge/Uzuy
synced 2024-11-23 10:29:42 +00:00
feat(renderer): Add frame skipping and interpolation logic
- Further Implemented frame skipping functionality to improve performance on lower-end devices by skipping alternate frames. - Added interpolation logic to create additional frames, resulting in smoother gameplay when frame interpolation is enabled. - Introduced new settings for toggling frame skipping and interpolation in the renderer configuration.
This commit is contained in:
parent
9a34d40523
commit
308cc609c5
@ -29,7 +29,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
SHOW_THERMAL_OVERLAY("show_thermal_overlay"),
|
||||
CORE_USE_MULTI_CORE("use_multi_core"),
|
||||
SYNC_CORE_SPEED("sync_core_speed"),
|
||||
FRAME_SKIPPING("frame_skipping");
|
||||
FRAME_SKIPPING("frame_skipping"),
|
||||
FRAME_INTERPOLATION("frame_interpolation");
|
||||
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||
NativeConfig.getBoolean(key, needsGlobal)
|
||||
|
@ -177,6 +177,7 @@ private fun addPhoenixHacksSubmenu(sl: ArrayList<SettingsItem>) {
|
||||
|
||||
// Add settings using keys directly
|
||||
add(BooleanSetting.FRAME_SKIPPING.key)
|
||||
add(BooleanSetting.FRAME_INTERPOLATION.key)
|
||||
add(BooleanSetting.CORE_USE_MULTI_CORE.key)
|
||||
add(BooleanSetting.SYNC_CORE_SPEED.key)
|
||||
add(IntSetting.RENDERER_SHADER_BACKEND.key)
|
||||
@ -187,6 +188,25 @@ private fun addPhoenixHacksSubmenu(sl: ArrayList<SettingsItem>) {
|
||||
}
|
||||
}
|
||||
|
||||
private val useInterpolationSetting = object : AbstractBooleanSetting {
|
||||
override val key = BooleanSetting.FRAME_INTERPOLATION.key
|
||||
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean {
|
||||
return BooleanSetting.FRAME_INTERPOLATION.getBoolean(needsGlobal)
|
||||
}
|
||||
|
||||
override fun setBoolean(value: Boolean) {
|
||||
BooleanSetting.FRAME_INTERPOLATION.setBoolean(value)
|
||||
}
|
||||
|
||||
override val defaultValue = BooleanSetting.FRAME_INTERPOLATION.defaultValue
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
||||
BooleanSetting.FRAME_INTERPOLATION.getValueAsString(needsGlobal)
|
||||
|
||||
override fun reset() = BooleanSetting.FRAME_INTERPOLATION.reset()
|
||||
}
|
||||
|
||||
private val useSyncCoreSpeedSetting = object : AbstractBooleanSetting {
|
||||
override val key = BooleanSetting.SYNC_CORE_SPEED.key
|
||||
|
||||
@ -226,34 +246,26 @@ private fun addPhoenixHacksSubmenu(sl: ArrayList<SettingsItem>) {
|
||||
}
|
||||
|
||||
private val frameSkippingSetting = object : AbstractBooleanSetting {
|
||||
override val key = "frame_skipping"
|
||||
override val key = BooleanSetting.FRAME_SKIPPING.key
|
||||
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean {
|
||||
return BooleanSetting.FRAME_SKIPPING.getBoolean(needsGlobal)
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean {
|
||||
return BooleanSetting.FRAME_SKIPPING.getBoolean(needsGlobal)
|
||||
}
|
||||
|
||||
override fun setBoolean(value: Boolean) {
|
||||
BooleanSetting.FRAME_SKIPPING.setBoolean(value)
|
||||
}
|
||||
|
||||
override val defaultValue = BooleanSetting.FRAME_SKIPPING.defaultValue
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
||||
BooleanSetting.FRAME_SKIPPING.getValueAsString(needsGlobal)
|
||||
|
||||
override fun reset() = BooleanSetting.FRAME_SKIPPING.reset()
|
||||
}
|
||||
|
||||
override fun setBoolean(value: Boolean) {
|
||||
BooleanSetting.FRAME_SKIPPING.setBoolean(value)
|
||||
NativeLibrary.setFrameSkipping(value) // Ensure this toggles the frame skipping in Vulkan renderer
|
||||
}
|
||||
|
||||
override val defaultValue = BooleanSetting.FRAME_SKIPPING.defaultValue
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = getBoolean(needsGlobal).toString()
|
||||
|
||||
override fun reset() = setBoolean(defaultValue)
|
||||
}
|
||||
|
||||
private fun addPhoenixHacksSettings(sl: ArrayList<SettingsItem>) {
|
||||
private fun addPhoenixHacksSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
// Add the multi-core setting to Phoenix Hacks submenu
|
||||
add(
|
||||
SwitchSetting(
|
||||
frameSkippingSetting,
|
||||
titleId = R.string.frame_skipping,
|
||||
descriptionId = R.string.frame_skipping_description
|
||||
)
|
||||
)
|
||||
add(
|
||||
SwitchSetting(
|
||||
useSyncCoreSpeedSetting,
|
||||
@ -268,7 +280,20 @@ private fun addPhoenixHacksSettings(sl: ArrayList<SettingsItem>) {
|
||||
descriptionId = R.string.use_multi_core_description
|
||||
)
|
||||
)
|
||||
// Add the new settings
|
||||
add(
|
||||
SwitchSetting(
|
||||
frameSkippingSetting,
|
||||
titleId = R.string.frame_skipping,
|
||||
descriptionId = R.string.frame_skipping_description
|
||||
)
|
||||
)
|
||||
add(
|
||||
SwitchSetting(
|
||||
useInterpolationSetting, // The interpolation setting object you've created
|
||||
titleId = R.string.frame_interpolation, // Use appropriate string resources for the title
|
||||
descriptionId = R.string.frame_interpolation_description // Description resource for the interpolation setting
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.RENDERER_SHADER_BACKEND,
|
||||
|
@ -390,9 +390,15 @@
|
||||
<string name="sync_core_speed">Sync Core Speed</string>
|
||||
<string name="sync_core_speed_description">Synchronizes the CPU core speed with the game\'s maximum rendering speed.</string>
|
||||
|
||||
<string name="frame_interpolation">Frame Interpolation</string>
|
||||
<string name="frame_interpolation_description">Enable or disable frame interpolation for smoother transitions between frames.</string>
|
||||
|
||||
<string name="frame_skipping">Frame Skipping</string>
|
||||
<string name="frame_skipping_description">Enables frame skipping to improve performance by skipping certain frames during rendering.</string>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<string name="preferences_phoenix_hacks">🐦🔥 Phoenix Hacks</string>
|
||||
<string name="preferences_phoenix_hacks_description">Enable or adjust Phoenix-specific hacks</string>
|
||||
<string name="preferences_phoenix_hacks">🐦🔥 Phoenix Tweaks</string>
|
||||
<string name="preferences_phoenix_hacks_description">Enable or adjust Phoenix-specific tweaks</string>
|
||||
<string name="slider_default">Default</string>
|
||||
<string name="ini_saved">Saved settings</string>
|
||||
<string name="gameid_saved">Saved settings for %1$s</string>
|
||||
|
@ -210,10 +210,12 @@ struct Values {
|
||||
true,
|
||||
true,
|
||||
&use_speed_limit};
|
||||
SwitchableSetting<bool> sync_core_speed{linkage, false, "sync_core_speed", Category::Core, Specialization::Default};
|
||||
SwitchableSetting<bool> sync_core_speed{linkage, true, "sync_core_speed", Category::Core, Specialization::Default};
|
||||
|
||||
// Frame-Skipping (Experimental)
|
||||
SwitchableSetting<bool> frame_skipping{linkage, false, "frame_skipping", Category::Core, Specialization::Default};
|
||||
SwitchableSetting<bool> frame_skipping{linkage, false, "frame_skipping", Category::Renderer, Specialization::Default};
|
||||
// Frame Interpolation (Experimental)
|
||||
SwitchableSetting<bool> frame_interpolation{linkage, false, "frame_interpolation", Category::Renderer, Specialization::Default};
|
||||
|
||||
// Cpu
|
||||
SwitchableSetting<CpuBackend, true> cpu_backend{linkage,
|
||||
|
@ -189,6 +189,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
tr("Enables frame skipping to improve performance in cases where the emulator cannot maintain the target frame rate. "
|
||||
"Frame skipping will cause some frames to be skipped to maintain smoother gameplay at the cost of visual fluidity. "
|
||||
"This is especially useful on lower-end hardware."));
|
||||
INSERT(Settings, frame_interpolation, tr("Enable Frame Interpolation"),
|
||||
tr("Enables frame interpolation to smooth out transitions between frames for a more fluid visual experience. "
|
||||
"This can be useful when the emulator's frame rate is lower than the target frame rate, creating smoother movement at the cost of input lag or visual accuracy. "
|
||||
"Recommended for higher-end hardware or when fluidity is prioritized over precision."));
|
||||
|
||||
// Renderer (Advanced Graphics)
|
||||
INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
|
||||
|
@ -124,6 +124,12 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
||||
rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
|
||||
scheduler),
|
||||
applet_frame() {
|
||||
// Move frame skipping initialization here
|
||||
frame_skipping_enabled = Settings::values.frame_skipping.GetValue();
|
||||
|
||||
// Initialize frame interpolation here
|
||||
frame_interpolation_enabled = Settings::values.frame_interpolation.GetValue();
|
||||
|
||||
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
|
||||
turbo_mode.emplace(instance, dld);
|
||||
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
||||
@ -139,80 +145,83 @@ RendererVulkan::~RendererVulkan() {
|
||||
void(device.GetLogical().WaitIdle());
|
||||
}
|
||||
|
||||
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||
if (frame_skip_interval > 0 && (frame_counter % (frame_skip_interval + 1)) == 0) {
|
||||
frame_counter++;
|
||||
is_frame_skipped = true; // Mark this frame as skipped
|
||||
return; // Skip this frame
|
||||
void RendererVulkan::InterpolateFrames(Frame* prev_frame, Frame* next_frame, Frame* interpolated_frame, float interpolation_factor) {
|
||||
if (prev_frame && next_frame && interpolated_frame) {
|
||||
// Implement further interpolation logic here, e.g., linear interpolation, spherical interpolation, etc.
|
||||
for (size_t i = 0; i < interpolated_frame->data.size(); ++i) {
|
||||
interpolated_frame->data[i] = (1.0f - interpolation_factor) * prev_frame->data[i] +
|
||||
interpolation_factor * next_frame->data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame_counter++;
|
||||
bool frame_skipping_enabled = false;
|
||||
bool frame_interpolation_enabled = false;
|
||||
|
||||
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||
static int frame_counter = 0; // Track frame count
|
||||
static Frame* prev_frame = nullptr; // Track the previous frame
|
||||
Frame* interpolated_frame = nullptr;
|
||||
|
||||
// Frame skipping logic
|
||||
if (frame_skipping_enabled) {
|
||||
// Skip rendering every other frame
|
||||
if (++frame_counter % 2 != 0) {
|
||||
return; // Skip this frame
|
||||
}
|
||||
}
|
||||
|
||||
if (framebuffers.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SCOPE_EXIT {
|
||||
render_window.OnFrameDisplayed();
|
||||
render_window.OnFrameDisplayed();
|
||||
};
|
||||
|
||||
RenderAppletCaptureLayer(framebuffers);
|
||||
// Check if frame interpolation is enabled
|
||||
if (frame_interpolation_enabled && prev_frame != nullptr) {
|
||||
// Allocate or retrieve a new frame for interpolation
|
||||
interpolated_frame = present_manager.GetRenderFrame();
|
||||
|
||||
if (!render_window.IsShown()) {
|
||||
return;
|
||||
// Calculate interpolation factor (e.g., halfway between frames = 0.5)
|
||||
float interpolation_factor = 0.5f;
|
||||
|
||||
// Interpolate between prev_frame and the current frame
|
||||
InterpolateFrames(prev_frame, present_manager.GetRenderFrame(), interpolated_frame, interpolation_factor);
|
||||
|
||||
// Render the interpolated frame instead of the current frame
|
||||
blit_swapchain.DrawToFrame(rasterizer, interpolated_frame, framebuffers,
|
||||
render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
|
||||
swapchain.GetImageViewFormat());
|
||||
|
||||
scheduler.Flush(*interpolated_frame->render_ready);
|
||||
present_manager.Present(interpolated_frame);
|
||||
|
||||
} else {
|
||||
// Render the current frame as normal if interpolation is disabled
|
||||
RenderAppletCaptureLayer(framebuffers);
|
||||
|
||||
if (!render_window.IsShown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderScreenshot(framebuffers);
|
||||
Frame* frame = present_manager.GetRenderFrame();
|
||||
blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers,
|
||||
render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
|
||||
swapchain.GetImageViewFormat());
|
||||
scheduler.Flush(*frame->render_ready);
|
||||
present_manager.Present(frame);
|
||||
}
|
||||
|
||||
if (is_frame_skipped && previous_frame_buffer.has_value()) {
|
||||
// Interpolate between the previous frame and the current frame
|
||||
InterpolateFrame(previous_frame_buffer.value(), framebuffers);
|
||||
is_frame_skipped = false;
|
||||
}
|
||||
|
||||
// Capture current frame buffer for future interpolation
|
||||
vk::Buffer current_frame_buffer = RenderToBuffer(framebuffers, render_window.GetFramebufferLayout(),
|
||||
VK_FORMAT_B8G8R8A8_UNORM, layout.width * layout.height * 4);
|
||||
previous_frame_buffer = current_frame_buffer;
|
||||
|
||||
RenderScreenshot(framebuffers);
|
||||
Frame* frame = present_manager.GetRenderFrame();
|
||||
blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers,
|
||||
render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
|
||||
swapchain.GetImageViewFormat());
|
||||
scheduler.Flush(*frame->render_ready);
|
||||
present_manager.Present(frame);
|
||||
// Save the current frame as the previous frame for the next call
|
||||
prev_frame = present_manager.GetRenderFrame();
|
||||
|
||||
gpu.RendererFrameEndNotify();
|
||||
rasterizer.TickFrame();
|
||||
}
|
||||
|
||||
void RendererVulkan::InterpolateFrame(vk::Buffer& previous_frame, std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||
// Allocate a buffer for the interpolated frame
|
||||
vk::Buffer interpolated_frame_buffer = RenderToBuffer(framebuffers, render_window.GetFramebufferLayout(),
|
||||
VK_FORMAT_B8G8R8A8_UNORM, layout.width * layout.height * 4);
|
||||
|
||||
// Perform the interpolation here (simplified linear blending)
|
||||
// In reality, you'd need to read from both frame buffers and blend the pixel values
|
||||
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
|
||||
// For simplicity, assume a 50-50 blend between previous and current frames
|
||||
// More complex interpolation can be implemented by reading pixel data and manually blending
|
||||
|
||||
// vkCmdCopyBuffer and other Vulkan operations to blend the previous and current frames
|
||||
// Here you would implement your actual blending logic
|
||||
BlitBuffer(cmdbuf, previous_frame, interpolated_frame_buffer); // Pseudo-code function for blending
|
||||
});
|
||||
|
||||
scheduler.Finish(); // Ensure the interpolation finishes before presenting
|
||||
|
||||
// Present the interpolated frame
|
||||
present_manager.PresentInterpolatedFrame(interpolated_frame_buffer);
|
||||
}
|
||||
|
||||
void RendererVulkan::BlitBuffer(vk::CommandBuffer cmdbuf, vk::Buffer& src, vk::Buffer& dst) {
|
||||
// This is a placeholder for Vulkan operations that blend two frame buffers.
|
||||
// Vulkan commands would go here, such as vkCmdCopyBuffer, and shader operations
|
||||
// that blend the contents of src and dst.
|
||||
}
|
||||
|
||||
void RendererVulkan::Report() const {
|
||||
using namespace Common::Literals;
|
||||
const std::string vendor_name{device.GetVendorName()};
|
||||
|
@ -58,6 +58,8 @@ public:
|
||||
return device.GetDriverName();
|
||||
}
|
||||
|
||||
void InterpolateFrames(Frame* prev_frame, Frame* next_frame, Frame* interpolated_frame, float interpolation_factor);
|
||||
|
||||
private:
|
||||
void Report() const;
|
||||
|
||||
@ -92,11 +94,8 @@ private:
|
||||
|
||||
Frame applet_frame;
|
||||
|
||||
std::optional<vk::Buffer> previous_frame_buffer; // Store previous frame buffer for interpolation
|
||||
bool is_frame_skipped = false; // Flag to check if the previous frame was skipped
|
||||
|
||||
int frame_skip_interval = 0; // Number of frames to skip (0 means no skipping)
|
||||
int frame_counter = 0; // Keeps track of the current frame number
|
||||
bool frame_skipping_enabled;
|
||||
bool frame_interpolation_enabled;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
Loading…
Reference in New Issue
Block a user