Hotkey for onscreen FPS (#770)

* fps counter

* more
This commit is contained in:
Logan McNaughton
2026-04-18 21:55:37 +02:00
committed by GitHub
parent 5a91d09f9e
commit bc3dd4d39a
7 changed files with 87 additions and 15 deletions
+1
View File
@@ -198,6 +198,7 @@ fn main() {
.allowlist_function("rdp_state_size")
.allowlist_function("rdp_save_state")
.allowlist_function("rdp_load_state")
.allowlist_function("rdp_set_fps")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
+42
View File
@@ -75,6 +75,11 @@ typedef struct {
FrameBufferInfo frame_buffer_info;
} RDP_DEVICE;
typedef struct {
uint32_t fps;
uint32_t vis;
} FPS_DATA;
typedef struct {
std::string message;
Vulkan::ImageHandle image;
@@ -106,6 +111,9 @@ static Vulkan::ImageHandle achievement_progress_indicator_image;
static std::map<uint32_t, std::string> leaderboard_trackers;
static bool display_challenge_indicator;
static bool display_fps;
static Vulkan::ImageHandle fps_image;
#define MESSAGE_TIME 3000 // 3 seconds
typedef struct {
@@ -147,6 +155,9 @@ bool sdl_event_filter(void *userdata, SDL_Event *event) {
if (gfx_info.fullscreen)
callback.emu_running = false;
break;
case SDL_SCANCODE_F1:
display_fps = !display_fps;
break;
case SDL_SCANCODE_F4:
crop_letterbox = !crop_letterbox;
break;
@@ -331,12 +342,15 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, const void *font,
leaderboard_trackers.clear();
achievement_challenge_indicator_image = Vulkan::ImageHandle();
achievement_progress_indicator_image = Vulkan::ImageHandle();
fps_image = Vulkan::ImageHandle();
display_fps = false;
}
void rdp_close() {
messages = std::queue<Message>();
achievement_challenge_indicator_image = Vulkan::ImageHandle();
achievement_progress_indicator_image = Vulkan::ImageHandle();
fps_image = Vulkan::ImageHandle();
if (wsi)
wsi->end_frame();
@@ -421,6 +435,18 @@ static void draw_indicator(CommandBufferHandle cmd,
cmd->draw(3);
}
static void draw_fps(CommandBufferHandle cmd, Vulkan::ImageHandle fps_image,
VkViewport &vp) {
cmd->set_texture(0, 0, fps_image->get_view(),
Vulkan::StockSampler::NearestClamp);
vp.y = vp.y + vp.height - fps_image->get_height();
vp.height = fps_image->get_height();
vp.width = fps_image->get_width();
cmd->set_viewport(vp);
cmd->draw(3);
}
static void render_frame(Vulkan::Device &device) {
RDP::ScanoutOptions options = {};
options.persist_frame_on_invalid_input = true;
@@ -517,6 +543,10 @@ static void render_frame(Vulkan::Device &device) {
display_challenge_indicator) {
draw_indicator(cmd, achievement_challenge_indicator_image, vp);
}
if (messages.empty() && display_fps && fps_image) {
draw_fps(cmd, fps_image, vp);
}
}
cmd->end_render_pass();
@@ -822,6 +852,18 @@ uint64_t rdp_process_commands() {
return interrupt_timer;
}
void rdp_set_fps_callback(void *userdata) {
FPS_DATA *data = (FPS_DATA *)userdata;
fps_image = create_message_image(
wsi->get_device(), 0, message_font,
std::format("FPS: {} VI/S: {}", data->fps, data->vis).c_str());
}
void rdp_set_fps(uint32_t fps, uint32_t vis) {
FPS_DATA data = {fps, vis};
SDL_RunOnMainThread(rdp_set_fps_callback, &data, true);
}
static void update_challenge_indicator() {
std::string message;
for (const auto &leaderboard_tracker : leaderboard_trackers) {
+1
View File
@@ -50,6 +50,7 @@ void rdp_check_framebuffers(uint32_t address, uint32_t length);
size_t rdp_state_size();
void rdp_save_state(uint8_t *state);
void rdp_load_state(const uint8_t *state);
void rdp_set_fps(uint32_t fps, uint32_t vis);
void achievement_challenge_indicator_add(const char *achievement_title);
void achievement_challenge_indicator_remove(const char *achievement_title);
-2
View File
@@ -482,8 +482,6 @@ impl Device {
enable_speed_limiter: true,
next_pace_deadline: None,
vi_counter: 0,
last_origin: 0,
internal_frame_counter: 0,
min_wait_time: std::time::Duration::from_secs(1),
frame_time: 0.0,
limit_freq: 2,
+4 -13
View File
@@ -27,8 +27,6 @@ pub struct Vi {
pub count_per_scanline: u64,
pub enable_speed_limiter: bool,
pub vi_counter: u64,
pub last_origin: u32,
pub internal_frame_counter: u64,
pub min_wait_time: std::time::Duration,
pub frame_time: f64,
pub limit_freq: u64,
@@ -110,10 +108,10 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
}
}
VI_ORIGIN_REG => {
let current_origin = device.vi.regs[reg as usize];
device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask);
if device.vi.regs[reg as usize] != device.vi.last_origin {
device.vi.last_origin = device.vi.regs[reg as usize];
device.vi.internal_frame_counter += 1;
if current_origin != device.vi.regs[reg as usize] {
let _ = device.ui.video.fps_tx.try_send(true);
}
}
_ => {
@@ -129,6 +127,7 @@ pub fn vertical_interrupt_event(device: &mut device::Device) {
}
ui::video::render_frame();
let _ = device.ui.video.vis_tx.try_send(true);
retroachievements::do_frame();
@@ -159,14 +158,6 @@ pub fn vertical_interrupt_event(device: &mut device::Device) {
unsafe { sdl3_sys::events::SDL_PumpEvents() };
/*
let vis = if device.cart.pal { 50 } else { 60 };
if device.vi.vi_counter % vis == 0 {
println!("FPS: {}", device.vi.internal_frame_counter);
device.vi.internal_frame_counter = 0;
}
*/
/* toggle vi field if in interlaced mode */
device.vi.field ^= (device.vi.regs[VI_STATUS_REG as usize] >> 6) & 0x1;
+10
View File
@@ -36,6 +36,10 @@ pub struct Storage {
pub struct Video {
pub window: *mut sdl3_sys::video::SDL_Window,
pub fullscreen: bool,
pub fps_tx: tokio::sync::mpsc::Sender<bool>,
pub fps_rx: Option<tokio::sync::mpsc::Receiver<bool>>,
pub vis_tx: tokio::sync::mpsc::Sender<bool>,
pub vis_rx: Option<tokio::sync::mpsc::Receiver<bool>>,
}
pub struct Usb {
@@ -113,6 +117,8 @@ impl Ui {
fn construct_ui(joysticks: Vec<sdl3_sys::everything::SDL_JoystickID>, with_sdl: bool) -> Ui {
let dirs = get_dirs();
let (fps_tx, fps_rx) = tokio::sync::mpsc::channel(1000);
let (vis_tx, vis_rx) = tokio::sync::mpsc::channel(1000);
Ui {
input: Input {
controllers: [
@@ -190,6 +196,10 @@ impl Ui {
video: Video {
window: std::ptr::null_mut(),
fullscreen: false,
fps_tx,
fps_rx: Some(fps_rx),
vis_tx,
vis_rx: Some(vis_rx),
},
usb: Usb {
usb_tx: None,
+29
View File
@@ -102,6 +102,35 @@ pub fn init(device: &mut device::Device) {
device.netplay.is_some(),
)
}
fps_counter(&mut device.ui);
}
fn fps_counter(ui: &mut ui::Ui) {
let mut fps_rx = ui.video.fps_rx.take().unwrap();
let mut vis_rx = ui.video.vis_rx.take().unwrap();
tokio::spawn(async move {
loop {
let mut fps: u32 = 0;
let mut vis: u32 = 0;
loop {
match fps_rx.try_recv() {
Ok(_fps_update) => fps += 1,
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return,
}
}
loop {
match vis_rx.try_recv() {
Ok(_vis_update) => vis += 1,
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return,
}
}
unsafe { rdp_set_fps(fps, vis) };
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
});
}
pub fn close(ui: &ui::Ui) {