mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-01-07 18:33:53 +00:00
- Add frame generation settings (enabled/disabled, interpolation/extrapolation modes) - Add frame skipping settings (enabled/disabled, adaptive/fixed modes) - Implement frame skipping logic with adaptive and fixed modes - Enhance UE4 crash handling with recovery mechanisms - Add support for signed and float 32-bit image formats across shader backends - Update Vulkan Validation Layers to v1.4.321.0 - Fix duplicate frame skipping options in Qt UI - Improve memory handling for UE4 games (Hogwarts Legacy compatibility) - Add enhanced bindless texture handling with fallback approach - Update Android build configuration and dependencies Signed-off-by: Zephyron <zephyron@citron-emu.org>
104 lines
3.0 KiB
C++
104 lines
3.0 KiB
C++
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "video_core/frame_skipping.h"
|
|
#include "common/logging/log.h"
|
|
|
|
namespace VideoCore {
|
|
|
|
FrameSkipping::FrameSkipping()
|
|
: last_frame_time{std::chrono::steady_clock::now()},
|
|
frame_skipping_enabled{false},
|
|
skipping_mode{Settings::FrameSkippingMode::Adaptive},
|
|
consecutive_skips{0},
|
|
max_consecutive_skips{5} {
|
|
}
|
|
|
|
bool FrameSkipping::ShouldSkipFrame(std::chrono::steady_clock::time_point current_time,
|
|
double target_fps) {
|
|
// Update settings from current configuration
|
|
frame_skipping_enabled = Settings::values.frame_skipping.GetValue() == Settings::FrameSkipping::Enabled;
|
|
skipping_mode = Settings::values.frame_skipping_mode.GetValue();
|
|
|
|
if (!frame_skipping_enabled) {
|
|
consecutive_skips = 0;
|
|
return false;
|
|
}
|
|
|
|
const auto target_frame_time = std::chrono::microseconds(static_cast<u64>(1000000.0 / target_fps));
|
|
|
|
bool should_skip = false;
|
|
switch (skipping_mode) {
|
|
case Settings::FrameSkippingMode::Adaptive:
|
|
should_skip = ShouldSkipAdaptive(static_cast<double>(target_frame_time.count()) / 1000.0);
|
|
break;
|
|
case Settings::FrameSkippingMode::Fixed:
|
|
should_skip = ShouldSkipFixed();
|
|
break;
|
|
default:
|
|
should_skip = false;
|
|
break;
|
|
}
|
|
|
|
if (should_skip) {
|
|
consecutive_skips++;
|
|
if (consecutive_skips > max_consecutive_skips) {
|
|
// Prevent excessive skipping
|
|
should_skip = false;
|
|
consecutive_skips = 0;
|
|
}
|
|
} else {
|
|
consecutive_skips = 0;
|
|
}
|
|
|
|
return should_skip;
|
|
}
|
|
|
|
void FrameSkipping::UpdateFrameTime(std::chrono::microseconds frame_time) {
|
|
frame_times.push_back(frame_time);
|
|
|
|
// Keep only recent frame times
|
|
if (frame_times.size() > MAX_FRAME_HISTORY) {
|
|
frame_times.pop_front();
|
|
}
|
|
}
|
|
|
|
void FrameSkipping::Reset() {
|
|
frame_times.clear();
|
|
consecutive_skips = 0;
|
|
last_frame_time = std::chrono::steady_clock::now();
|
|
}
|
|
|
|
double FrameSkipping::GetAverageFrameTime() const {
|
|
if (frame_times.empty()) {
|
|
return 0.0;
|
|
}
|
|
|
|
double total_time = 0.0;
|
|
for (const auto& time : frame_times) {
|
|
total_time += static_cast<double>(time.count()) / 1000.0; // Convert to milliseconds
|
|
}
|
|
|
|
return total_time / static_cast<double>(frame_times.size());
|
|
}
|
|
|
|
bool FrameSkipping::ShouldSkipAdaptive(double target_frame_time) const {
|
|
const double avg_frame_time = GetAverageFrameTime();
|
|
|
|
if (avg_frame_time == 0.0) {
|
|
return false;
|
|
}
|
|
|
|
// Skip if average frame time is significantly higher than target
|
|
return avg_frame_time > (target_frame_time * ADAPTIVE_THRESHOLD);
|
|
}
|
|
|
|
bool FrameSkipping::ShouldSkipFixed() const {
|
|
// Simple pattern: skip every other frame when in fixed mode
|
|
static u32 frame_counter = 0;
|
|
frame_counter++;
|
|
|
|
return (frame_counter % 2) == 0; // Skip even-numbered frames
|
|
}
|
|
|
|
} // namespace VideoCore
|