1use crate::{DebugRenderContext, DebugRenderFn, SelectableButton}; 2use egui::{CentralPanel, ScrollArea, Vec2}; 3use jgenesis_common::frontend::Color; 4use snes_core::api::SnesEmulator; 5 6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 7enum Tab { 8 Cgram, 9 #[default] 10 Vram, 11} 12 13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 14enum VramMode { 15 TwoBpp, 16 #[default] 17 FourBpp, 18 EightBpp, 19 Mode7, 20} 21 22const CGRAM_BUFFER_LEN: usize = 256; 23const VRAM_BUFFER_LEN: usize = 256 * 1024; 24 25struct State { 26 tab: Tab, 27 vram_mode: VramMode, 28 vram_palette: u8, 29 cgram_texture: Option<(wgpu::Texture, egui::TextureId)>, 30 cgram_buffer: Box<[Color; CGRAM_BUFFER_LEN]>, 31 vram_2bpp_texture: Option<(wgpu::Texture, egui::TextureId)>, 32 vram_4bpp_texture: Option<(wgpu::Texture, egui::TextureId)>, 33 vram_8bpp_texture: Option<(wgpu::Texture, egui::TextureId)>, 34 vram_mode7_texture: Option<(wgpu::Texture, egui::TextureId)>, 35 vram_buffer: Box<[Color; VRAM_BUFFER_LEN]>, 36} 37 38impl State { 39 fn new() -> Self { 40 Self { 41 tab: Tab::default(), 42 vram_mode: VramMode::default(), 43 vram_palette: 0, 44 cgram_texture: None, 45 cgram_buffer: vec![Color::default(); CGRAM_BUFFER_LEN] 46 .into_boxed_slice() 47 .try_into() 48 .unwrap(), 49 vram_2bpp_texture: None, 50 vram_4bpp_texture: None, 51 vram_8bpp_texture: None, 52 vram_mode7_texture: None, 53 vram_buffer: vec![Color::default(); VRAM_BUFFER_LEN] 54 .into_boxed_slice() 55 .try_into() 56 .unwrap(), 57 } 58 } 59} 60 61#[must_use] 62pub fn render_fn() -> Box<DebugRenderFn<SnesEmulator>> { 63 let mut state = State::new(); 64 Box::new(move |ctx, emulator| render(ctx, emulator, &mut state)) 65} 66 67fn render(mut ctx: DebugRenderContext<'_>, emulator: &mut SnesEmulator, state: &mut State) { 68 update_cgram_texture(&mut ctx, emulator, state); 69 update_vram_texture(&mut ctx, emulator, state); 70 71 let screen_width = crate::screen_width(ctx.egui_ui); 72 73 CentralPanel::default().show_inside(ctx.egui_ui, |ui| { 74 ui.horizontal(|ui| { 75 ui.add(SelectableButton::new("VRAM", &mut state.tab, Tab::Vram)); 76 ui.add(SelectableButton::new("CGRAM", &mut state.tab, Tab::Cgram)); 77 }); 78 79 ui.add_space(15.0); 80 81 match state.tab { 82 Tab::Cgram => { 83 ui.vertical_centered(|ui| { 84 let egui_texture = state.cgram_texture.as_ref().unwrap().1; 85 ui.image((egui_texture, Vec2::new(screen_width * 0.65, screen_width * 0.65))); 86 }); 87 } 88 Tab::Vram => { 89 let original_vram_mode = state.vram_mode; 90 91 ui.horizontal(|ui| { 92 ui.label("Mode:"); 93 ui.add(SelectableButton::new("2bpp", &mut state.vram_mode, VramMode::TwoBpp)); 94 ui.add(SelectableButton::new("4bpp", &mut state.vram_mode, VramMode::FourBpp)); 95 ui.add(SelectableButton::new("8bpp", &mut state.vram_mode, VramMode::EightBpp)); 96 ui.add(SelectableButton::new("Mode 7", &mut state.vram_mode, VramMode::Mode7)); 97 }); 98 99 ui.add_space(5.0); 100 101 ui.add_enabled_ui( 102 matches!(state.vram_mode, VramMode::TwoBpp | VramMode::FourBpp), 103 |ui| { 104 ui.horizontal(|ui| { 105 ui.label("Palette:"); 106 107 for bg_palette in 0..8 { 108 ui.add(SelectableButton::new( 109 format!("BG{bg_palette}"), 110 &mut state.vram_palette, 111 bg_palette, 112 )); 113 } 114 115 for obj_palette in 0..8 { 116 ui.add_enabled( 117 state.vram_mode == VramMode::FourBpp, 118 SelectableButton::new( 119 format!("OBJ{obj_palette}"), 120 &mut state.vram_palette, 121 obj_palette + 8, 122 ), 123 ); 124 } 125 }); 126 }, 127 ); 128 129 ui.add_space(10.0); 130 131 ScrollArea::vertical().show(ui, |ui| match original_vram_mode { 132 VramMode::TwoBpp => { 133 let egui_texture = state.vram_2bpp_texture.as_ref().unwrap().1; 134 ui.image((egui_texture, Vec2::new(screen_width, screen_width))); 135 } 136 VramMode::FourBpp => { 137 let egui_texture = state.vram_4bpp_texture.as_ref().unwrap().1; 138 ui.image((egui_texture, Vec2::new(screen_width, screen_width * 0.5))); 139 } 140 VramMode::EightBpp => { 141 let egui_texture = state.vram_8bpp_texture.as_ref().unwrap().1; 142 ui.image((egui_texture, Vec2::new(screen_width, screen_width))); 143 } 144 VramMode::Mode7 => { 145 let egui_texture = state.vram_mode7_texture.as_ref().unwrap().1; 146 ui.image((egui_texture, Vec2::new(screen_width, screen_width))); 147 } 148 }); 149 } 150 } 151 }); 152} 153 154fn update_cgram_texture( 155 ctx: &mut DebugRenderContext<'_>, 156 emulator: &mut SnesEmulator, 157 state: &mut State, 158) { 159 emulator.copy_cgram(state.cgram_buffer.as_mut()); 160 161 if state.cgram_texture.is_none() { 162 let (wgpu_texture, egui_texture) = 163 crate::create_texture("debug_snes_cgram", 16, 16, ctx.device, ctx.renderer); 164 state.cgram_texture = Some((wgpu_texture, egui_texture)); 165 } 166 167 let (wgpu_texture, egui_texture) = state.cgram_texture.as_ref().unwrap(); 168 169 crate::write_textures( 170 wgpu_texture, 171 *egui_texture, 172 bytemuck::cast_slice(state.cgram_buffer.as_ref()), 173 ctx, 174 ); 175} 176 177fn update_vram_texture( 178 ctx: &mut DebugRenderContext<'_>, 179 emulator: &mut SnesEmulator, 180 state: &mut State, 181) { 182 match state.vram_mode { 183 VramMode::TwoBpp => update_vram_2bpp_texture(ctx, emulator, state), 184 VramMode::FourBpp => update_vram_4bpp_texture(ctx, emulator, state), 185 VramMode::EightBpp => update_vram_8bpp_texture(ctx, emulator, state), 186 VramMode::Mode7 => update_vram_mode7_texture(ctx, emulator, state), 187 } 188} 189 190fn update_vram_2bpp_texture( 191 ctx: &mut DebugRenderContext<'_>, 192 emulator: &mut SnesEmulator, 193 state: &mut State, 194) { 195 emulator.copy_vram_2bpp(state.vram_buffer.as_mut(), state.vram_palette, 64); 196 197 if state.vram_2bpp_texture.is_none() { 198 let (wgpu_texture, egui_texture) = 199 crate::create_texture("debug_snes_vram_2bpp", 64 * 8, 64 * 8, ctx.device, ctx.renderer); 200 state.vram_2bpp_texture = Some((wgpu_texture, egui_texture)); 201 } 202 203 let (wgpu_texture, egui_texture) = state.vram_2bpp_texture.as_ref().unwrap(); 204 205 crate::write_textures( 206 wgpu_texture, 207 *egui_texture, 208 bytemuck::cast_slice(state.vram_buffer.as_ref()), 209 ctx, 210 ); 211} 212 213fn update_vram_4bpp_texture( 214 ctx: &mut DebugRenderContext<'_>, 215 emulator: &mut SnesEmulator, 216 state: &mut State, 217) { 218 emulator.copy_vram_4bpp(state.vram_buffer.as_mut(), state.vram_palette, 64); 219 220 if state.vram_4bpp_texture.is_none() { 221 let (wgpu_texture, egui_texture) = 222 crate::create_texture("debug_snes_vram_4bpp", 64 * 8, 32 * 8, ctx.device, ctx.renderer); 223 state.vram_4bpp_texture = Some((wgpu_texture, egui_texture)); 224 } 225 226 let (wgpu_texture, egui_texture) = state.vram_4bpp_texture.as_ref().unwrap(); 227 228 crate::write_textures( 229 wgpu_texture, 230 *egui_texture, 231 bytemuck::cast_slice(state.vram_buffer.as_ref()), 232 ctx, 233 ); 234} 235 236fn update_vram_8bpp_texture( 237 ctx: &mut DebugRenderContext<'_>, 238 emulator: &mut SnesEmulator, 239 state: &mut State, 240) { 241 emulator.copy_vram_8bpp(state.vram_buffer.as_mut(), 32); 242 243 if state.vram_8bpp_texture.is_none() { 244 let (wgpu_texture, egui_texture) = 245 crate::create_texture("debug_snes_vram_8bpp", 32 * 8, 32 * 8, ctx.device, ctx.renderer); 246 state.vram_8bpp_texture = Some((wgpu_texture, egui_texture)); 247 } 248 249 let (wgpu_texture, egui_texture) = state.vram_8bpp_texture.as_ref().unwrap(); 250 251 crate::write_textures( 252 wgpu_texture, 253 *egui_texture, 254 bytemuck::cast_slice(state.vram_buffer.as_ref()), 255 ctx, 256 ); 257} 258 259fn update_vram_mode7_texture( 260 ctx: &mut DebugRenderContext<'_>, 261 emulator: &mut SnesEmulator, 262 state: &mut State, 263) { 264 emulator.copy_vram_mode7(state.vram_buffer.as_mut(), 16); 265 266 if state.vram_mode7_texture.is_none() { 267 let (wgpu_texture, egui_texture) = crate::create_texture( 268 "debug_snes_vram_mode7", 269 16 * 8, 270 16 * 8, 271 ctx.device, 272 ctx.renderer, 273 ); 274 state.vram_mode7_texture = Some((wgpu_texture, egui_texture)); 275 } 276 277 let (wgpu_texture, egui_texture) = state.vram_mode7_texture.as_ref().unwrap(); 278 279 crate::write_textures( 280 wgpu_texture, 281 *egui_texture, 282 bytemuck::cast_slice(state.vram_buffer.as_ref()), 283 ctx, 284 ); 285}