Start profiler UI/thread
This commit is contained in:
		
							parent
							
								
									38b34c9cf9
								
							
						
					
					
						commit
						3bfdcc9366
					
				
							
								
								
									
										17
									
								
								src/app.rs
								
								
								
								
							
							
						
						
									
										17
									
								
								src/app.rs
								
								
								
								
							| 
						 | 
					@ -24,8 +24,8 @@ use crate::{
 | 
				
			||||||
    persistence::Persistence,
 | 
					    persistence::Persistence,
 | 
				
			||||||
    window::{
 | 
					    window::{
 | 
				
			||||||
        AboutWindow, AppWindow, BgMapWindow, CharacterDataWindow, FrameBufferWindow, GameWindow,
 | 
					        AboutWindow, AppWindow, BgMapWindow, CharacterDataWindow, FrameBufferWindow, GameWindow,
 | 
				
			||||||
        GdbServerWindow, HotkeysWindow, InputWindow, ObjectWindow, RegisterWindow, TerminalWindow,
 | 
					        GdbServerWindow, HotkeysWindow, InputWindow, ObjectWindow, ProfileWindow, RegisterWindow,
 | 
				
			||||||
        WorldWindow,
 | 
					        TerminalWindow, WorldWindow,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,7 @@ pub struct Application {
 | 
				
			||||||
    viewports: HashMap<ViewportId, Viewport>,
 | 
					    viewports: HashMap<ViewportId, Viewport>,
 | 
				
			||||||
    focused: Option<ViewportId>,
 | 
					    focused: Option<ViewportId>,
 | 
				
			||||||
    init_debug_port: Option<u16>,
 | 
					    init_debug_port: Option<u16>,
 | 
				
			||||||
 | 
					    init_profiling: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Application {
 | 
					impl Application {
 | 
				
			||||||
| 
						 | 
					@ -61,6 +62,7 @@ impl Application {
 | 
				
			||||||
        client: EmulatorClient,
 | 
					        client: EmulatorClient,
 | 
				
			||||||
        proxy: EventLoopProxy<UserEvent>,
 | 
					        proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
        debug_port: Option<u16>,
 | 
					        debug_port: Option<u16>,
 | 
				
			||||||
 | 
					        profiling: bool,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        let wgpu = WgpuState::new();
 | 
					        let wgpu = WgpuState::new();
 | 
				
			||||||
        let icon = load_icon().ok().map(Arc::new);
 | 
					        let icon = load_icon().ok().map(Arc::new);
 | 
				
			||||||
| 
						 | 
					@ -89,6 +91,7 @@ impl Application {
 | 
				
			||||||
            viewports: HashMap::new(),
 | 
					            viewports: HashMap::new(),
 | 
				
			||||||
            focused: None,
 | 
					            focused: None,
 | 
				
			||||||
            init_debug_port: debug_port,
 | 
					            init_debug_port: debug_port,
 | 
				
			||||||
 | 
					            init_profiling: profiling,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,6 +116,11 @@ impl ApplicationHandler<UserEvent> for Application {
 | 
				
			||||||
            server.launch(port);
 | 
					            server.launch(port);
 | 
				
			||||||
            self.open(event_loop, Box::new(server));
 | 
					            self.open(event_loop, Box::new(server));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if self.init_profiling {
 | 
				
			||||||
 | 
					            let mut profiler = ProfileWindow::new(SimId::Player1, self.client.clone());
 | 
				
			||||||
 | 
					            profiler.launch();
 | 
				
			||||||
 | 
					            self.open(event_loop, Box::new(profiler));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        let app = GameWindow::new(
 | 
					        let app = GameWindow::new(
 | 
				
			||||||
            self.client.clone(),
 | 
					            self.client.clone(),
 | 
				
			||||||
            self.proxy.clone(),
 | 
					            self.proxy.clone(),
 | 
				
			||||||
| 
						 | 
					@ -247,6 +255,10 @@ impl ApplicationHandler<UserEvent> for Application {
 | 
				
			||||||
                let terminal = TerminalWindow::new(sim_id, &self.client);
 | 
					                let terminal = TerminalWindow::new(sim_id, &self.client);
 | 
				
			||||||
                self.open(event_loop, Box::new(terminal));
 | 
					                self.open(event_loop, Box::new(terminal));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            UserEvent::OpenProfiler(sim_id) => {
 | 
				
			||||||
 | 
					                let profile = ProfileWindow::new(sim_id, self.client.clone());
 | 
				
			||||||
 | 
					                self.open(event_loop, Box::new(profile));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            UserEvent::OpenDebugger(sim_id) => {
 | 
					            UserEvent::OpenDebugger(sim_id) => {
 | 
				
			||||||
                let debugger =
 | 
					                let debugger =
 | 
				
			||||||
                    GdbServerWindow::new(sim_id, self.client.clone(), self.proxy.clone());
 | 
					                    GdbServerWindow::new(sim_id, self.client.clone(), self.proxy.clone());
 | 
				
			||||||
| 
						 | 
					@ -521,6 +533,7 @@ pub enum UserEvent {
 | 
				
			||||||
    OpenFrameBuffers(SimId),
 | 
					    OpenFrameBuffers(SimId),
 | 
				
			||||||
    OpenRegisters(SimId),
 | 
					    OpenRegisters(SimId),
 | 
				
			||||||
    OpenTerminal(SimId),
 | 
					    OpenTerminal(SimId),
 | 
				
			||||||
 | 
					    OpenProfiler(SimId),
 | 
				
			||||||
    OpenDebugger(SimId),
 | 
					    OpenDebugger(SimId),
 | 
				
			||||||
    OpenInput,
 | 
					    OpenInput,
 | 
				
			||||||
    OpenHotkeys,
 | 
					    OpenHotkeys,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ use tracing::{error, warn};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    audio::Audio,
 | 
					    audio::Audio,
 | 
				
			||||||
    emulator::{cart::Cart, profiler::Profiler},
 | 
					    emulator::{cart::Cart, shrooms_vb_core::SimEvent},
 | 
				
			||||||
    graphics::TextureSink,
 | 
					    graphics::TextureSink,
 | 
				
			||||||
    memory::{MemoryRange, MemoryRegion},
 | 
					    memory::{MemoryRange, MemoryRegion},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,6 @@ pub use shrooms_vb_core::{VBKey, VBRegister, VBWatchpointType};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod address_set;
 | 
					mod address_set;
 | 
				
			||||||
mod cart;
 | 
					mod cart;
 | 
				
			||||||
mod profiler;
 | 
					 | 
				
			||||||
mod shrooms_vb_core;
 | 
					mod shrooms_vb_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
					#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
				
			||||||
| 
						 | 
					@ -70,7 +69,6 @@ pub struct EmulatorBuilder {
 | 
				
			||||||
    audio_on: Arc<[AtomicBool; 2]>,
 | 
					    audio_on: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    linked: Arc<AtomicBool>,
 | 
					    linked: Arc<AtomicBool>,
 | 
				
			||||||
    start_paused: bool,
 | 
					    start_paused: bool,
 | 
				
			||||||
    monitor_events: bool,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl EmulatorBuilder {
 | 
					impl EmulatorBuilder {
 | 
				
			||||||
| 
						 | 
					@ -87,7 +85,6 @@ impl EmulatorBuilder {
 | 
				
			||||||
            audio_on: Arc::new([AtomicBool::new(true), AtomicBool::new(true)]),
 | 
					            audio_on: Arc::new([AtomicBool::new(true), AtomicBool::new(true)]),
 | 
				
			||||||
            linked: Arc::new(AtomicBool::new(false)),
 | 
					            linked: Arc::new(AtomicBool::new(false)),
 | 
				
			||||||
            start_paused: false,
 | 
					            start_paused: false,
 | 
				
			||||||
            monitor_events: false,
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let client = EmulatorClient {
 | 
					        let client = EmulatorClient {
 | 
				
			||||||
            queue,
 | 
					            queue,
 | 
				
			||||||
| 
						 | 
					@ -113,13 +110,6 @@ impl EmulatorBuilder {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn monitor_events(self, monitor_events: bool) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            monitor_events,
 | 
					 | 
				
			||||||
            ..self
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn build(self) -> Result<Emulator> {
 | 
					    pub fn build(self) -> Result<Emulator> {
 | 
				
			||||||
        let mut emulator = Emulator::new(
 | 
					        let mut emulator = Emulator::new(
 | 
				
			||||||
            self.commands,
 | 
					            self.commands,
 | 
				
			||||||
| 
						 | 
					@ -134,9 +124,6 @@ impl EmulatorBuilder {
 | 
				
			||||||
        if self.start_paused {
 | 
					        if self.start_paused {
 | 
				
			||||||
            emulator.pause_sims()?;
 | 
					            emulator.pause_sims()?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if self.monitor_events {
 | 
					 | 
				
			||||||
            emulator.monitor_events(SimId::Player1)?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(emulator)
 | 
					        Ok(emulator)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -150,7 +137,7 @@ pub struct Emulator {
 | 
				
			||||||
    state: Arc<Atomic<EmulatorState>>,
 | 
					    state: Arc<Atomic<EmulatorState>>,
 | 
				
			||||||
    audio_on: Arc<[AtomicBool; 2]>,
 | 
					    audio_on: Arc<[AtomicBool; 2]>,
 | 
				
			||||||
    linked: Arc<AtomicBool>,
 | 
					    linked: Arc<AtomicBool>,
 | 
				
			||||||
    profilers: [Profiler; 2],
 | 
					    profilers: [Option<ProfileSender>; 2],
 | 
				
			||||||
    renderers: HashMap<SimId, TextureSink>,
 | 
					    renderers: HashMap<SimId, TextureSink>,
 | 
				
			||||||
    messages: HashMap<SimId, mpsc::Sender<Toast>>,
 | 
					    messages: HashMap<SimId, mpsc::Sender<Toast>>,
 | 
				
			||||||
    debuggers: HashMap<SimId, DebugInfo>,
 | 
					    debuggers: HashMap<SimId, DebugInfo>,
 | 
				
			||||||
| 
						 | 
					@ -178,7 +165,7 @@ impl Emulator {
 | 
				
			||||||
            state,
 | 
					            state,
 | 
				
			||||||
            audio_on,
 | 
					            audio_on,
 | 
				
			||||||
            linked,
 | 
					            linked,
 | 
				
			||||||
            profilers: [Profiler::new(), Profiler::new()],
 | 
					            profilers: [None, None],
 | 
				
			||||||
            renderers: HashMap::new(),
 | 
					            renderers: HashMap::new(),
 | 
				
			||||||
            messages: HashMap::new(),
 | 
					            messages: HashMap::new(),
 | 
				
			||||||
            debuggers: HashMap::new(),
 | 
					            debuggers: HashMap::new(),
 | 
				
			||||||
| 
						 | 
					@ -228,12 +215,29 @@ impl Emulator {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let sim = &mut self.sims[index];
 | 
					        let sim = &mut self.sims[index];
 | 
				
			||||||
        sim.reset();
 | 
					        sim.reset();
 | 
				
			||||||
        sim.monitor_events(self.profilers[sim_id.to_index()].is_enabled());
 | 
					 | 
				
			||||||
        if let Some(cart) = new_cart {
 | 
					        if let Some(cart) = new_cart {
 | 
				
			||||||
            sim.load_cart(cart.rom.clone(), cart.sram.clone())?;
 | 
					            sim.load_cart(cart.rom.clone(), cart.sram.clone())?;
 | 
				
			||||||
            self.carts[index] = Some(cart);
 | 
					            self.carts[index] = Some(cart);
 | 
				
			||||||
            self.sim_state[index].store(SimState::Ready, Ordering::Release);
 | 
					            self.sim_state[index].store(SimState::Ready, Ordering::Release);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        let mut profiling = false;
 | 
				
			||||||
 | 
					        if let Some(profiler) = self.profilers[sim_id.to_index()].as_ref() {
 | 
				
			||||||
 | 
					            if let Some(cart) = self.carts[index].as_ref() {
 | 
				
			||||||
 | 
					                if profiler
 | 
				
			||||||
 | 
					                    .send(ProfileEvent::Start {
 | 
				
			||||||
 | 
					                        file_path: cart.file_path.clone(),
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .is_ok()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    sim.monitor_events(true);
 | 
				
			||||||
 | 
					                    profiling = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if !profiling {
 | 
				
			||||||
 | 
					            sim.monitor_events(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.sim_state[index].load(Ordering::Acquire) == SimState::Ready {
 | 
					        if self.sim_state[index].load(Ordering::Acquire) == SimState::Ready {
 | 
				
			||||||
            self.resume_sims();
 | 
					            self.resume_sims();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -331,6 +335,11 @@ impl Emulator {
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn start_profiling(&mut self, sim_id: SimId, sender: ProfileSender) -> Result<()> {
 | 
				
			||||||
 | 
					        self.profilers[sim_id.to_index()] = Some(sender);
 | 
				
			||||||
 | 
					        self.reset_sim(sim_id, None)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn start_debugging(&mut self, sim_id: SimId, sender: DebugSender) {
 | 
					    fn start_debugging(&mut self, sim_id: SimId, sender: DebugSender) {
 | 
				
			||||||
        if self.sim_state[sim_id.to_index()].load(Ordering::Acquire) != SimState::Ready {
 | 
					        if self.sim_state[sim_id.to_index()].load(Ordering::Acquire) != SimState::Ready {
 | 
				
			||||||
            // Can't debug unless a game is connected
 | 
					            // Can't debug unless a game is connected
 | 
				
			||||||
| 
						 | 
					@ -399,11 +408,6 @@ impl Emulator {
 | 
				
			||||||
        self.watched_regions.insert(range, region);
 | 
					        self.watched_regions.insert(range, region);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn monitor_events(&mut self, sim_id: SimId) -> Result<()> {
 | 
					 | 
				
			||||||
        self.profilers[sim_id.to_index()].enable(true);
 | 
					 | 
				
			||||||
        self.reset_sim(sim_id, None)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn run(&mut self) {
 | 
					    pub fn run(&mut self) {
 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let idle = self.tick();
 | 
					            let idle = self.tick();
 | 
				
			||||||
| 
						 | 
					@ -469,10 +473,20 @@ impl Emulator {
 | 
				
			||||||
            .zip(self.profilers.iter_mut())
 | 
					            .zip(self.profilers.iter_mut())
 | 
				
			||||||
            .zip([p1_running, p2_running])
 | 
					            .zip([p1_running, p2_running])
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if !running || !profiler.is_enabled() {
 | 
					            if !running {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            profiler.track(cycles, sim.take_sim_event());
 | 
					            if let Some(p) = profiler {
 | 
				
			||||||
 | 
					                if p.send(ProfileEvent::Update {
 | 
				
			||||||
 | 
					                    cycles,
 | 
				
			||||||
 | 
					                    event: sim.take_sim_event(),
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .is_err()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    sim.monitor_events(false);
 | 
				
			||||||
 | 
					                    *profiler = None;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if state == EmulatorState::Stepping {
 | 
					        if state == EmulatorState::Stepping {
 | 
				
			||||||
| 
						 | 
					@ -614,6 +628,11 @@ impl Emulator {
 | 
				
			||||||
                    self.report_error(SimId::Player1, format!("Error setting speed: {error}"));
 | 
					                    self.report_error(SimId::Player1, format!("Error setting speed: {error}"));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::StartProfiling(sim_id, profiler) => {
 | 
				
			||||||
 | 
					                if let Err(error) = self.start_profiling(sim_id, profiler) {
 | 
				
			||||||
 | 
					                    self.report_error(SimId::Player1, format!("Error enaling profiler: {error}"));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            EmulatorCommand::StartDebugging(sim_id, debugger) => {
 | 
					            EmulatorCommand::StartDebugging(sim_id, debugger) => {
 | 
				
			||||||
                self.start_debugging(sim_id, debugger);
 | 
					                self.start_debugging(sim_id, debugger);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -751,6 +770,7 @@ pub enum EmulatorCommand {
 | 
				
			||||||
    Resume,
 | 
					    Resume,
 | 
				
			||||||
    FrameAdvance,
 | 
					    FrameAdvance,
 | 
				
			||||||
    SetSpeed(f64),
 | 
					    SetSpeed(f64),
 | 
				
			||||||
 | 
					    StartProfiling(SimId, ProfileSender),
 | 
				
			||||||
    StartDebugging(SimId, DebugSender),
 | 
					    StartDebugging(SimId, DebugSender),
 | 
				
			||||||
    StopDebugging(SimId),
 | 
					    StopDebugging(SimId),
 | 
				
			||||||
    DebugInterrupt(SimId),
 | 
					    DebugInterrupt(SimId),
 | 
				
			||||||
| 
						 | 
					@ -792,6 +812,7 @@ pub enum EmulatorState {
 | 
				
			||||||
    Debugging,
 | 
					    Debugging,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ProfileSender = tokio::sync::mpsc::UnboundedSender<ProfileEvent>;
 | 
				
			||||||
type DebugSender = tokio::sync::mpsc::UnboundedSender<DebugEvent>;
 | 
					type DebugSender = tokio::sync::mpsc::UnboundedSender<DebugEvent>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
				
			||||||
| 
						 | 
					@ -815,6 +836,16 @@ pub enum DebugEvent {
 | 
				
			||||||
    Stopped(DebugStopReason),
 | 
					    Stopped(DebugStopReason),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum ProfileEvent {
 | 
				
			||||||
 | 
					    Start {
 | 
				
			||||||
 | 
					        file_path: PathBuf,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Update {
 | 
				
			||||||
 | 
					        cycles: u32,
 | 
				
			||||||
 | 
					        event: Option<SimEvent>,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
pub struct EmulatorClient {
 | 
					pub struct EmulatorClient {
 | 
				
			||||||
    queue: mpsc::Sender<EmulatorCommand>,
 | 
					    queue: mpsc::Sender<EmulatorCommand>,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +0,0 @@
 | 
				
			||||||
use crate::emulator::shrooms_vb_core::SimEvent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct Profiler {
 | 
					 | 
				
			||||||
    enabled: bool,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Profiler {
 | 
					 | 
				
			||||||
    pub fn new() -> Self {
 | 
					 | 
				
			||||||
        Self { enabled: false }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn enable(&mut self, enabled: bool) {
 | 
					 | 
				
			||||||
        self.enabled = enabled;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn is_enabled(&self) -> bool {
 | 
					 | 
				
			||||||
        self.enabled
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn track(&mut self, cycles: u32, event: Option<SimEvent>) {
 | 
					 | 
				
			||||||
        println!("profiler {cycles} {event:0x?}");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										13
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										13
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
					@ -22,6 +22,7 @@ mod images;
 | 
				
			||||||
mod input;
 | 
					mod input;
 | 
				
			||||||
mod memory;
 | 
					mod memory;
 | 
				
			||||||
mod persistence;
 | 
					mod persistence;
 | 
				
			||||||
 | 
					mod profiler;
 | 
				
			||||||
mod window;
 | 
					mod window;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Parser)]
 | 
					#[derive(Parser)]
 | 
				
			||||||
| 
						 | 
					@ -110,10 +111,7 @@ fn main() -> Result<()> {
 | 
				
			||||||
        builder = builder.start_paused(true);
 | 
					        builder = builder.start_paused(true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if args.profile {
 | 
					    if args.profile {
 | 
				
			||||||
        if args.rom.is_none() {
 | 
					        builder = builder.start_paused(true)
 | 
				
			||||||
            bail!("to start profiling, please select a game.");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        builder = builder.monitor_events(true)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ThreadBuilder::default()
 | 
					    ThreadBuilder::default()
 | 
				
			||||||
| 
						 | 
					@ -133,6 +131,11 @@ fn main() -> Result<()> {
 | 
				
			||||||
    let event_loop = EventLoop::with_user_event().build().unwrap();
 | 
					    let event_loop = EventLoop::with_user_event().build().unwrap();
 | 
				
			||||||
    event_loop.set_control_flow(ControlFlow::Poll);
 | 
					    event_loop.set_control_flow(ControlFlow::Poll);
 | 
				
			||||||
    let proxy = event_loop.create_proxy();
 | 
					    let proxy = event_loop.create_proxy();
 | 
				
			||||||
    event_loop.run_app(&mut Application::new(client, proxy, args.debug_port))?;
 | 
					    event_loop.run_app(&mut Application::new(
 | 
				
			||||||
 | 
					        client,
 | 
				
			||||||
 | 
					        proxy,
 | 
				
			||||||
 | 
					        args.debug_port,
 | 
				
			||||||
 | 
					        args.profile,
 | 
				
			||||||
 | 
					    ))?;
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,79 @@
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    sync::{
 | 
				
			||||||
 | 
					        Arc,
 | 
				
			||||||
 | 
					        atomic::{AtomicBool, Ordering},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    thread,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use tokio::{select, sync::mpsc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::emulator::{EmulatorClient, EmulatorCommand, ProfileEvent, SimId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Profiler {
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
 | 
					    client: EmulatorClient,
 | 
				
			||||||
 | 
					    running: Arc<AtomicBool>,
 | 
				
			||||||
 | 
					    killer: Option<oneshot::Sender<()>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Profiler {
 | 
				
			||||||
 | 
					    pub fn new(sim_id: SimId, client: EmulatorClient) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
 | 
					            client,
 | 
				
			||||||
 | 
					            running: Arc::new(AtomicBool::new(false)),
 | 
				
			||||||
 | 
					            killer: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn started(&self) -> bool {
 | 
				
			||||||
 | 
					        self.running.load(Ordering::Relaxed)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start(&mut self) {
 | 
				
			||||||
 | 
					        let sim_id = self.sim_id;
 | 
				
			||||||
 | 
					        let client = self.client.clone();
 | 
				
			||||||
 | 
					        let running = self.running.clone();
 | 
				
			||||||
 | 
					        let (tx, rx) = oneshot::channel();
 | 
				
			||||||
 | 
					        self.killer = Some(tx);
 | 
				
			||||||
 | 
					        thread::spawn(move || {
 | 
				
			||||||
 | 
					            tokio::runtime::Builder::new_current_thread()
 | 
				
			||||||
 | 
					                .enable_all()
 | 
				
			||||||
 | 
					                .build()
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .block_on(async move {
 | 
				
			||||||
 | 
					                    select! {
 | 
				
			||||||
 | 
					                        _ = run_profile(sim_id, client, running.clone()) => {}
 | 
				
			||||||
 | 
					                        _ = rx => {
 | 
				
			||||||
 | 
					                            running.store(false, Ordering::Relaxed);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn stop(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(killer) = self.killer.take() {
 | 
				
			||||||
 | 
					            let _ = killer.send(());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn run_profile(sim_id: SimId, client: EmulatorClient, running: Arc<AtomicBool>) {
 | 
				
			||||||
 | 
					    let (profile_sync, mut profile_source) = mpsc::unbounded_channel();
 | 
				
			||||||
 | 
					    client.send_command(EmulatorCommand::StartProfiling(sim_id, profile_sync));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    running.store(true, Ordering::Relaxed);
 | 
				
			||||||
 | 
					    while let Some(event) = profile_source.recv().await {
 | 
				
			||||||
 | 
					        match event {
 | 
				
			||||||
 | 
					            ProfileEvent::Start { file_path } => {
 | 
				
			||||||
 | 
					                println!("profiling {}", file_path.display());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ProfileEvent::Update { cycles, event } => {
 | 
				
			||||||
 | 
					                println!("update {cycles} {event:#x?}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    running.store(false, Ordering::Release);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ pub use game::GameWindow;
 | 
				
			||||||
pub use gdb::GdbServerWindow;
 | 
					pub use gdb::GdbServerWindow;
 | 
				
			||||||
pub use hotkeys::HotkeysWindow;
 | 
					pub use hotkeys::HotkeysWindow;
 | 
				
			||||||
pub use input::InputWindow;
 | 
					pub use input::InputWindow;
 | 
				
			||||||
 | 
					pub use profile::ProfileWindow;
 | 
				
			||||||
pub use terminal::TerminalWindow;
 | 
					pub use terminal::TerminalWindow;
 | 
				
			||||||
pub use vip::{
 | 
					pub use vip::{
 | 
				
			||||||
    BgMapWindow, CharacterDataWindow, FrameBufferWindow, ObjectWindow, RegisterWindow, WorldWindow,
 | 
					    BgMapWindow, CharacterDataWindow, FrameBufferWindow, ObjectWindow, RegisterWindow, WorldWindow,
 | 
				
			||||||
| 
						 | 
					@ -18,6 +19,7 @@ mod game_screen;
 | 
				
			||||||
mod gdb;
 | 
					mod gdb;
 | 
				
			||||||
mod hotkeys;
 | 
					mod hotkeys;
 | 
				
			||||||
mod input;
 | 
					mod input;
 | 
				
			||||||
 | 
					mod profile;
 | 
				
			||||||
mod terminal;
 | 
					mod terminal;
 | 
				
			||||||
mod utils;
 | 
					mod utils;
 | 
				
			||||||
mod vip;
 | 
					mod vip;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -228,6 +228,11 @@ impl GameWindow {
 | 
				
			||||||
                    .send_event(UserEvent::OpenTerminal(self.sim_id))
 | 
					                    .send_event(UserEvent::OpenTerminal(self.sim_id))
 | 
				
			||||||
                    .unwrap();
 | 
					                    .unwrap();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ui.button("Profiler").clicked() {
 | 
				
			||||||
 | 
					                self.proxy
 | 
				
			||||||
 | 
					                    .send_event(UserEvent::OpenProfiler(self.sim_id))
 | 
				
			||||||
 | 
					                    .unwrap();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if ui.button("GDB Server").clicked() {
 | 
					            if ui.button("GDB Server").clicked() {
 | 
				
			||||||
                self.proxy
 | 
					                self.proxy
 | 
				
			||||||
                    .send_event(UserEvent::OpenDebugger(self.sim_id))
 | 
					                    .send_event(UserEvent::OpenDebugger(self.sim_id))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,54 @@
 | 
				
			||||||
 | 
					use egui::{CentralPanel, ViewportBuilder, ViewportId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    emulator::{EmulatorClient, SimId},
 | 
				
			||||||
 | 
					    profiler::Profiler,
 | 
				
			||||||
 | 
					    window::AppWindow,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct ProfileWindow {
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
 | 
					    profiler: Profiler,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ProfileWindow {
 | 
				
			||||||
 | 
					    pub fn new(sim_id: SimId, client: EmulatorClient) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
 | 
					            profiler: Profiler::new(sim_id, client),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn launch(&mut self) {
 | 
				
			||||||
 | 
					        self.profiler.start();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AppWindow for ProfileWindow {
 | 
				
			||||||
 | 
					    fn viewport_id(&self) -> ViewportId {
 | 
				
			||||||
 | 
					        ViewportId::from_hash_of(format!("Profile-{}", self.sim_id))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn sim_id(&self) -> SimId {
 | 
				
			||||||
 | 
					        self.sim_id
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initial_viewport(&self) -> ViewportBuilder {
 | 
				
			||||||
 | 
					        ViewportBuilder::default()
 | 
				
			||||||
 | 
					            .with_title(format!("Profiler ({})", self.sim_id))
 | 
				
			||||||
 | 
					            .with_inner_size((300.0, 200.0))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn show(&mut self, ctx: &egui::Context) {
 | 
				
			||||||
 | 
					        CentralPanel::default().show(ctx, |ui| {
 | 
				
			||||||
 | 
					            let mut started = self.profiler.started();
 | 
				
			||||||
 | 
					            if ui.checkbox(&mut started, "Profiling enabled?").changed() {
 | 
				
			||||||
 | 
					                if started {
 | 
				
			||||||
 | 
					                    self.profiler.start();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    self.profiler.stop();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue