mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
@@ -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");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user