gb.rsannotatedgb.rssource266 lines · 9.1 KB · raw
1use crate::{DebugRenderContext, DebugRenderFn, SelectableButton};
2use egui::{CentralPanel, Grid, ScrollArea, Vec2};
3use gb_core::api::{BackgroundTileMap, GameBoyEmulator};
4use jgenesis_common::frontend::Color;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7enum Tab {
8    #[default]
9    Background,
10    Sprites,
11    Palettes,
12}
13
14#[derive(Debug)]
15struct State {
16    tab: Tab,
17    background_tile_map: BackgroundTileMap,
18    background_buffer: Vec<Color>,
19    background_texture: Option<(wgpu::Texture, egui::TextureId)>,
20    sprites_buffer: Vec<Color>,
21    sprites_texture: Option<(wgpu::Texture, egui::TextureId)>,
22    sprites_double_height_texture: Option<(wgpu::Texture, egui::TextureId)>,
23    bg_palettes_texture: Option<(wgpu::Texture, egui::TextureId)>,
24    obj_palettes_texture: Option<(wgpu::Texture, egui::TextureId)>,
25}
26
27impl State {
28    fn new() -> Self {
29        Self {
30            tab: Tab::default(),
31            background_tile_map: BackgroundTileMap::default(),
32            background_buffer: vec![Color::default(); 256 * 256],
33            background_texture: None,
34            sprites_buffer: vec![Color::default(); 40 * 8 * 16],
35            sprites_texture: None,
36            sprites_double_height_texture: None,
37            bg_palettes_texture: None,
38            obj_palettes_texture: None,
39        }
40    }
41}
42
43#[must_use]
44pub fn render_fn() -> Box<DebugRenderFn<GameBoyEmulator>> {
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 GameBoyEmulator, state: &mut State) {
50    update_background_texture(&mut ctx, emulator, state);
51    update_sprite_texture(&mut ctx, emulator, state);
52    update_palettes_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("Background", &mut state.tab, Tab::Background));
59            ui.add(SelectableButton::new("Sprites", &mut state.tab, Tab::Sprites));
60            ui.add(SelectableButton::new("Palettes", &mut state.tab, Tab::Palettes));
61        });
62
63        ui.add_space(15.0);
64
65        match state.tab {
66            Tab::Background => {
67                ui.horizontal(|ui| {
68                    ui.label("Tile map:");
69
70                    ui.radio_value(
71                        &mut state.background_tile_map,
72                        BackgroundTileMap::Zero,
73                        "$9800-$9BFF",
74                    );
75                    ui.radio_value(
76                        &mut state.background_tile_map,
77                        BackgroundTileMap::One,
78                        "$9C00-$9FFF",
79                    );
80                });
81
82                ui.add_space(10.0);
83
84                ScrollArea::vertical().show(ui, |ui| {
85                    ui.vertical_centered(|ui| {
86                        let egui_texture = state.background_texture.as_ref().unwrap().1;
87                        ui.image((
88                            egui_texture,
89                            Vec2::new(screen_width * 0.95, screen_width * 0.95),
90                        ))
91                    });
92                });
93            }
94            Tab::Sprites => {
95                if emulator.is_using_double_height_sprites() {
96                    ScrollArea::vertical().show(ui, |ui| {
97                        ui.vertical_centered(|ui| {
98                            let egui_texture =
99                                state.sprites_double_height_texture.as_ref().unwrap().1;
100                            ui.image((
101                                egui_texture,
102                                Vec2::new(screen_width * 0.6, screen_width * 0.6 * 80.0 / 64.0),
103                            ));
104                        });
105                    });
106                } else {
107                    ScrollArea::vertical().show(ui, |ui| {
108                        ui.vertical_centered(|ui| {
109                            let egui_texture = state.sprites_texture.as_ref().unwrap().1;
110                            ui.image((
111                                egui_texture,
112                                Vec2::new(screen_width * 0.95, screen_width * 0.95 * 40.0 / 64.0),
113                            ));
114                        });
115                    });
116                }
117            }
118            Tab::Palettes => {
119                let bg_texture = state.bg_palettes_texture.as_ref().unwrap().1;
120                let obj_texture = state.obj_palettes_texture.as_ref().unwrap().1;
121
122                Grid::new("debug_gb_palettes_grid").show(ui, |ui| {
123                    ui.vertical_centered(|ui| {
124                        ui.heading("Background");
125                    });
126                    ui.vertical_centered(|ui| {
127                        ui.heading("Sprites");
128                    });
129                    ui.end_row();
130
131                    ui.image((bg_texture, Vec2::new(screen_width * 0.3, screen_width * 0.3 * 2.0)));
132                    ui.image((
133                        obj_texture,
134                        Vec2::new(screen_width * 0.3, screen_width * 0.3 * 2.0),
135                    ));
136                    ui.end_row();
137                });
138            }
139        }
140    });
141}
142
143fn update_background_texture(
144    ctx: &mut DebugRenderContext<'_>,
145    emulator: &mut GameBoyEmulator,
146    state: &mut State,
147) {
148    if state.tab == Tab::Background {
149        emulator.copy_background(state.background_tile_map, &mut state.background_buffer);
150    }
151
152    if state.background_texture.is_none() {
153        let (wgpu_texture, egui_texture) =
154            crate::create_texture("debug_gb_bg", 256, 256, ctx.device, ctx.renderer);
155        state.background_texture = Some((wgpu_texture, egui_texture));
156    }
157
158    let (wgpu_texture, egui_texture) = state.background_texture.as_ref().unwrap();
159    let egui_texture = *egui_texture;
160
161    crate::write_textures(
162        wgpu_texture,
163        egui_texture,
164        bytemuck::cast_slice(&state.background_buffer),
165        ctx,
166    );
167}
168
169fn update_sprite_texture(
170    ctx: &mut DebugRenderContext<'_>,
171    emulator: &mut GameBoyEmulator,
172    state: &mut State,
173) {
174    if state.tab == Tab::Sprites {
175        emulator.copy_sprites(&mut state.sprites_buffer);
176    }
177
178    if state.sprites_texture.is_none() {
179        let (wgpu_texture, egui_texture) =
180            crate::create_texture("debug_gb_sprites", 8 * 8, 5 * 8, ctx.device, ctx.renderer);
181        state.sprites_texture = Some((wgpu_texture, egui_texture));
182    }
183
184    if state.sprites_double_height_texture.is_none() {
185        let (wgpu_texture, egui_texture) = crate::create_texture(
186            "debug_gb_sprites_2x_height",
187            8 * 8,
188            2 * 5 * 8,
189            ctx.device,
190            ctx.renderer,
191        );
192        state.sprites_double_height_texture = Some((wgpu_texture, egui_texture));
193    }
194
195    if emulator.is_using_double_height_sprites() {
196        let (wgpu_texture, egui_texture) = state.sprites_double_height_texture.as_ref().unwrap();
197        let egui_texture = *egui_texture;
198
199        crate::write_textures(
200            wgpu_texture,
201            egui_texture,
202            bytemuck::cast_slice(&state.sprites_buffer),
203            ctx,
204        );
205    } else {
206        let (wgpu_texture, egui_texture) = state.sprites_texture.as_ref().unwrap();
207        let egui_texture = *egui_texture;
208
209        crate::write_textures(
210            wgpu_texture,
211            egui_texture,
212            bytemuck::cast_slice(&state.sprites_buffer[..40 * 64]),
213            ctx,
214        );
215    }
216}
217
218fn update_palettes_texture(
219    ctx: &mut DebugRenderContext<'_>,
220    emulator: &mut GameBoyEmulator,
221    state: &mut State,
222) {
223    let mut palettes_buffer = [Color::default(); 64];
224
225    if state.tab == Tab::Palettes {
226        emulator.copy_palettes(&mut palettes_buffer);
227    }
228
229    if state.bg_palettes_texture.is_none() {
230        let (wgpu_texture, egui_texture) =
231            crate::create_texture("debug_gb_bg_palettes", 4, 8, ctx.device, ctx.renderer);
232        state.bg_palettes_texture = Some((wgpu_texture, egui_texture));
233    }
234
235    if state.obj_palettes_texture.is_none() {
236        let (wgpu_texture, egui_texture) =
237            crate::create_texture("debug_gb_obj_palettes", 4, 8, ctx.device, ctx.renderer);
238        state.obj_palettes_texture = Some((wgpu_texture, egui_texture));
239    }
240
241    let mut bg_palettes_buffer = [Color::TRANSPARENT; 32];
242    let mut obj_palettes_buffer = [Color::TRANSPARENT; 32];
243    if emulator.is_cgb_mode() {
244        bg_palettes_buffer.copy_from_slice(&palettes_buffer[..32]);
245        obj_palettes_buffer.copy_from_slice(&palettes_buffer[32..]);
246    } else {
247        bg_palettes_buffer[..4].copy_from_slice(&palettes_buffer[..4]);
248        obj_palettes_buffer[..8].copy_from_slice(&palettes_buffer[4..12]);
249    }
250
251    let (bg_wgpu_texture, bg_egui_texture) = state.bg_palettes_texture.as_ref().unwrap();
252    crate::write_textures(
253        bg_wgpu_texture,
254        *bg_egui_texture,
255        bytemuck::cast_slice(&bg_palettes_buffer),
256        ctx,
257    );
258
259    let (obj_wgpu_texture, obj_egui_texture) = state.obj_palettes_texture.as_ref().unwrap();
260    crate::write_textures(
261        obj_wgpu_texture,
262        *obj_egui_texture,
263        bytemuck::cast_slice(&obj_palettes_buffer),
264        ctx,
265    );
266}