nes.rsannotatednes.rssource223 lines · 7.6 KB · raw
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}