1use crate::{DebugRenderContext, DebugRenderFn, SelectableButton}; 2use egui::{CentralPanel, ScrollArea, Vec2}; 3use jgenesis_common::frontend::Color; 4use nes_core::api::{NesEmulator, PatternTable}; 5 6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 7enum Tab { 8 #[default] 9 Nametables, 10 Oam, 11 PaletteRam, 12} 13 14#[derive(Debug)] 15struct State { 16 tab: Tab, 17 nametables_pattern_table: PatternTable, 18 nametables_buffer: Vec<Color>, 19 nametables_texture: Option<(wgpu::Texture, egui::TextureId)>, 20 oam_pattern_table: PatternTable, 21 oam_buffer: Vec<Color>, 22 oam_texture: Option<(wgpu::Texture, egui::TextureId)>, 23 oam_double_height_texture: Option<(wgpu::Texture, egui::TextureId)>, 24 palette_ram_texture: Option<(wgpu::Texture, egui::TextureId)>, 25} 26 27impl State { 28 fn new() -> Self { 29 Self { 30 tab: Tab::default(), 31 nametables_pattern_table: PatternTable::Zero, 32 nametables_buffer: vec![Color::default(); 4 * 256 * 240], 33 nametables_texture: None, 34 oam_pattern_table: PatternTable::One, 35 oam_buffer: vec![Color::default(); 2 * 64 * 8 * 8], 36 oam_texture: None, 37 oam_double_height_texture: None, 38 palette_ram_texture: None, 39 } 40 } 41} 42 43#[must_use] 44pub fn render_fn() -> Box<DebugRenderFn<NesEmulator>> { 45 let mut state = State::new(); 46 Box::new(move |ctx, emulator| render(ctx, emulator, &mut state)) 47} 48 49fn render(mut ctx: DebugRenderContext<'_>, emulator: &mut NesEmulator, state: &mut State) { 50 update_nametables_texture(&mut ctx, emulator, state); 51 update_oam_texture(&mut ctx, emulator, state); 52 update_palette_ram_texture(&mut ctx, emulator, state); 53 54 let screen_width = crate::screen_width(ctx.egui_ui); 55 56 CentralPanel::default().show_inside(ctx.egui_ui, |ui| { 57 ui.horizontal(|ui| { 58 ui.add(SelectableButton::new("Nametables", &mut state.tab, Tab::Nametables)); 59 ui.add(SelectableButton::new("OAM", &mut state.tab, Tab::Oam)); 60 ui.add(SelectableButton::new("Palette RAM", &mut state.tab, Tab::PaletteRam)); 61 }); 62 63 ui.add_space(15.0); 64 65 match state.tab { 66 Tab::Nametables => { 67 ui.horizontal(|ui| { 68 ui.label("Pattern table:"); 69 70 ui.radio_value( 71 &mut state.nametables_pattern_table, 72 PatternTable::Zero, 73 "$0000", 74 ); 75 ui.radio_value(&mut state.nametables_pattern_table, PatternTable::One, "$1000"); 76 }); 77 78 ui.add_space(10.0); 79 80 ScrollArea::vertical().show(ui, |ui| { 81 ui.vertical_centered(|ui| { 82 let egui_texture = state.nametables_texture.as_ref().unwrap().1; 83 ui.image(( 84 egui_texture, 85 Vec2::new(screen_width * 0.95, screen_width * 0.95), 86 )); 87 }); 88 }); 89 } 90 Tab::Oam => { 91 ui.horizontal(|ui| { 92 ui.add_enabled_ui(!emulator.using_double_height_sprites(), |ui| { 93 ui.label("Pattern table:"); 94 95 ui.radio_value(&mut state.oam_pattern_table, PatternTable::Zero, "$0000"); 96 ui.radio_value(&mut state.oam_pattern_table, PatternTable::One, "$1000"); 97 }); 98 }); 99 100 ui.add_space(10.0); 101 102 ScrollArea::vertical().show(ui, |ui| { 103 ui.vertical_centered(|ui| { 104 if emulator.using_double_height_sprites() { 105 let egui_texture = state.oam_double_height_texture.as_ref().unwrap().1; 106 ui.image(( 107 egui_texture, 108 Vec2::new(screen_width * 0.325, screen_width * 0.65), 109 )); 110 } else { 111 let egui_texture = state.oam_texture.as_ref().unwrap().1; 112 ui.image(( 113 egui_texture, 114 Vec2::new(screen_width * 0.65, screen_width * 0.65), 115 )); 116 } 117 }); 118 }); 119 } 120 Tab::PaletteRam => { 121 ui.vertical_centered(|ui| { 122 let egui_texture = state.palette_ram_texture.as_ref().unwrap().1; 123 ui.image((egui_texture, Vec2::new(screen_width * 0.325, screen_width * 0.65))); 124 }); 125 } 126 } 127 }); 128} 129 130fn update_nametables_texture( 131 ctx: &mut DebugRenderContext<'_>, 132 emulator: &mut NesEmulator, 133 state: &mut State, 134) { 135 if state.tab == Tab::Nametables { 136 emulator.copy_nametables(state.nametables_pattern_table, &mut state.nametables_buffer); 137 } 138 139 if state.nametables_texture.is_none() { 140 let (wgpu_texture, egui_texture) = crate::create_texture( 141 "debug_nes_nametables", 142 2 * 256, 143 2 * 240, 144 ctx.device, 145 ctx.renderer, 146 ); 147 state.nametables_texture = Some((wgpu_texture, egui_texture)); 148 } 149 150 let (wgpu_texture, egui_texture) = state.nametables_texture.as_ref().unwrap(); 151 let egui_texture = *egui_texture; 152 153 crate::write_textures( 154 wgpu_texture, 155 egui_texture, 156 bytemuck::cast_slice(&state.nametables_buffer), 157 ctx, 158 ); 159} 160 161fn update_oam_texture( 162 ctx: &mut DebugRenderContext<'_>, 163 emulator: &mut NesEmulator, 164 state: &mut State, 165) { 166 if state.tab == Tab::Oam { 167 emulator.copy_oam(state.oam_pattern_table, &mut state.oam_buffer); 168 } 169 170 if state.oam_texture.is_none() { 171 let (wgpu_texture, egui_texture) = 172 crate::create_texture("debug_nes_oam", 8 * 8, 8 * 8, ctx.device, ctx.renderer); 173 state.oam_texture = Some((wgpu_texture, egui_texture)); 174 } 175 176 if state.oam_double_height_texture.is_none() { 177 let (wgpu_texture, egui_texture) = 178 crate::create_texture("debug_nes_oam_2x", 8 * 8, 2 * 8 * 8, ctx.device, ctx.renderer); 179 state.oam_double_height_texture = Some((wgpu_texture, egui_texture)); 180 } 181 182 if emulator.using_double_height_sprites() { 183 let (wgpu_texture, egui_texture) = state.oam_double_height_texture.as_ref().unwrap(); 184 let egui_texture = *egui_texture; 185 186 crate::write_textures( 187 wgpu_texture, 188 egui_texture, 189 bytemuck::cast_slice(&state.oam_buffer), 190 ctx, 191 ); 192 } else { 193 let (wgpu_texture, egui_texture) = state.oam_texture.as_ref().unwrap(); 194 let egui_texture = *egui_texture; 195 196 crate::write_textures( 197 wgpu_texture, 198 egui_texture, 199 bytemuck::cast_slice(&state.oam_buffer[..64 * 64]), 200 ctx, 201 ); 202 } 203} 204 205fn update_palette_ram_texture( 206 ctx: &mut DebugRenderContext<'_>, 207 emulator: &mut NesEmulator, 208 state: &mut State, 209) { 210 let mut colors = [Color::default(); 32]; 211 emulator.copy_palette_ram(&mut colors); 212 213 if state.palette_ram_texture.is_none() { 214 let (wgpu_texture, egui_texture) = 215 crate::create_texture("debug_nes_palette_ram", 4, 8, ctx.device, ctx.renderer); 216 state.palette_ram_texture = Some((wgpu_texture, egui_texture)); 217 } 218 219 let (wgpu_texture, egui_texture) = state.palette_ram_texture.as_ref().unwrap(); 220 let egui_texture = *egui_texture; 221 222 crate::write_textures(wgpu_texture, egui_texture, bytemuck::cast_slice(&colors), ctx); 223}