Render images on background thread to keep UI responsive

This commit is contained in:
2025-02-15 18:33:18 -05:00
parent a82389224f
commit a461faf89d
5 changed files with 337 additions and 163 deletions
+37 -67
View File
@@ -5,12 +5,11 @@ use egui::{
TextureOptions, Ui, ViewportBuilder, ViewportId,
};
use egui_extras::{Column, Size, StripBuilder, TableBuilder};
use serde::{Deserialize, Serialize};
use crate::{
emulator::SimId,
memory::{MemoryMonitor, MemoryView},
vram::{VramImage, VramImageLoader, VramResource as _, VramTextureLoader},
vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
window::{utils::UiExt, AppWindow},
};
@@ -18,26 +17,32 @@ use super::utils::{parse_palette, CharacterGrid, GENERIC_PALETTE};
pub struct BgMapWindow {
sim_id: SimId,
loader: Arc<VramTextureLoader<BgMapLoader>>,
loader: Arc<VramTextureLoader>,
bgmaps: MemoryView,
cell_index: usize,
cell_index_str: String,
generic_palette: bool,
params: VramParams<BgMapParams>,
scale: f32,
show_grid: bool,
generic_palette: bool,
}
impl BgMapWindow {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor, vram: &mut VramProcessor) -> Self {
let renderer = BgMapRenderer::new(sim_id, memory);
let ([cell, bgmap], params) = vram.add(renderer);
let loader =
VramTextureLoader::new([("vram://cell".into(), cell), ("vram://bgmap".into(), bgmap)]);
Self {
sim_id,
loader: Arc::new(VramTextureLoader::new(BgMapLoader::new(sim_id, memory))),
loader: Arc::new(loader),
bgmaps: memory.view(sim_id, 0x00020000, 0x1d800),
cell_index: 0,
cell_index_str: "0".into(),
generic_palette: false,
params,
scale: 1.0,
show_grid: false,
generic_palette: false,
}
}
@@ -88,11 +93,7 @@ impl BgMapWindow {
});
});
});
let resource = BgMapResource::Cell {
index: self.cell_index,
generic_palette: self.generic_palette,
};
let image = Image::new(resource.to_uri())
let image = Image::new("vram://cell")
.maintain_aspect_ratio(true)
.tint(Color32::RED)
.texture_options(TextureOptions::NEAREST);
@@ -154,14 +155,14 @@ impl BgMapWindow {
});
});
});
self.params.write(BgMapParams {
cell_index: self.cell_index,
generic_palette: self.generic_palette,
});
}
fn show_bgmap(&mut self, ui: &mut Ui) {
let resource = BgMapResource::BgMap {
index: self.cell_index / 4096,
generic_palette: self.generic_palette,
};
let grid = CharacterGrid::new(resource.to_uri())
let grid = CharacterGrid::new("vram://bgmap")
.with_scale(self.scale)
.with_grid(self.show_grid)
.with_selected(self.cell_index % 4096);
@@ -192,7 +193,6 @@ impl AppWindow for BgMapWindow {
}
fn show(&mut self, ctx: &Context) {
self.loader.begin_pass();
CentralPanel::default().show(ctx, |ui| {
ui.horizontal_top(|ui| {
StripBuilder::new(ui)
@@ -219,20 +219,20 @@ fn parse_cell(cell: u16) -> (usize, bool, bool, usize) {
(char_index, vflip, hflip, palette_index)
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
enum BgMapResource {
BgMap { index: usize, generic_palette: bool },
Cell { index: usize, generic_palette: bool },
#[derive(Default, Clone, PartialEq, Eq)]
struct BgMapParams {
cell_index: usize,
generic_palette: bool,
}
struct BgMapLoader {
struct BgMapRenderer {
chardata: MemoryView,
bgmaps: MemoryView,
brightness: MemoryView,
palettes: MemoryView,
}
impl BgMapLoader {
impl BgMapRenderer {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
Self {
chardata: memory.view(sim_id, 0x00078000, 0x8000),
@@ -242,7 +242,7 @@ impl BgMapLoader {
}
}
fn update_bgmap(&self, image: &mut VramImage, bgmap_index: usize, generic_palette: bool) {
fn render_bgmap(&self, image: &mut VramImage, bgmap_index: usize, generic_palette: bool) {
let chardata = self.chardata.borrow();
let bgmaps = self.bgmaps.borrow();
let brightness = self.brightness.borrow();
@@ -284,7 +284,7 @@ impl BgMapLoader {
}
}
fn update_bgmap_cell(&self, image: &mut VramImage, index: usize, generic_palette: bool) {
fn render_bgmap_cell(&self, image: &mut VramImage, index: usize, generic_palette: bool) {
let chardata = self.chardata.borrow();
let bgmaps = self.bgmaps.borrow();
let brightness = self.brightness.borrow();
@@ -324,49 +324,19 @@ impl BgMapLoader {
}
}
impl VramImageLoader for BgMapLoader {
type Resource = BgMapResource;
impl VramRenderer<2> for BgMapRenderer {
type Params = BgMapParams;
fn id(&self) -> &str {
concat!(module_path!(), "::BgMapLoader")
fn sizes(&self) -> [[usize; 2]; 2] {
[[8, 8], [8 * 64, 8 * 64]]
}
fn add(&self, resource: &Self::Resource) -> Option<VramImage> {
match resource {
BgMapResource::BgMap {
index,
generic_palette,
} => {
let mut image = VramImage::new(64 * 8, 64 * 8);
self.update_bgmap(&mut image, *index, *generic_palette);
Some(image)
}
BgMapResource::Cell {
index,
generic_palette,
} => {
let mut image = VramImage::new(8, 8);
self.update_bgmap_cell(&mut image, *index, *generic_palette);
Some(image)
}
}
}
fn update<'a>(
&'a self,
resources: impl Iterator<Item = (&'a Self::Resource, &'a mut VramImage)>,
) {
for (resource, image) in resources {
match resource {
BgMapResource::BgMap {
index,
generic_palette,
} => self.update_bgmap(image, *index, *generic_palette),
BgMapResource::Cell {
index,
generic_palette,
} => self.update_bgmap_cell(image, *index, *generic_palette),
}
}
fn render(&mut self, params: &Self::Params, images: &mut [VramImage; 2]) {
self.render_bgmap_cell(&mut images[0], params.cell_index, params.generic_palette);
self.render_bgmap(
&mut images[1],
params.cell_index / 4096,
params.generic_palette,
);
}
}
+101 -18
View File
@@ -10,14 +10,15 @@ use serde::{Deserialize, Serialize};
use crate::{
emulator::SimId,
memory::{MemoryMonitor, MemoryView},
vram::{VramImage, VramImageLoader, VramResource as _, VramTextureLoader},
vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
window::{utils::UiExt as _, AppWindow},
};
use super::utils::{self, CharacterGrid};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum VramPalette {
#[default]
Generic,
Bg0,
Bg1,
@@ -77,26 +78,34 @@ impl Display for VramPalette {
pub struct CharacterDataWindow {
sim_id: SimId,
loader: Arc<VramTextureLoader<CharDataLoader>>,
loader: Arc<VramTextureLoader>,
brightness: MemoryView,
palettes: MemoryView,
palette: VramPalette,
index: usize,
index_str: String,
params: VramParams<CharDataParams>,
scale: f32,
show_grid: bool,
}
impl CharacterDataWindow {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor, vram: &mut VramProcessor) -> Self {
let renderer = CharDataRenderer::new(sim_id, memory);
let ([char, chardata], params) = vram.add(renderer);
let loader = VramTextureLoader::new([
("vram://char".into(), char),
("vram://chardata".into(), chardata),
]);
Self {
sim_id,
loader: Arc::new(VramTextureLoader::new(CharDataLoader::new(sim_id, memory))),
loader: Arc::new(loader),
brightness: memory.view(sim_id, 0x0005f824, 8),
palettes: memory.view(sim_id, 0x0005f860, 16),
palette: VramPalette::Generic,
index: 0,
index_str: "0".into(),
palette: params.palette,
index: params.index,
index_str: params.index.to_string(),
params,
scale: 4.0,
show_grid: true,
}
@@ -150,11 +159,7 @@ impl CharacterDataWindow {
});
});
});
let resource = CharDataResource::Character {
palette: self.palette,
index: self.index,
};
let image = Image::new(resource.to_uri())
let image = Image::new("vram://char")
.maintain_aspect_ratio(true)
.tint(Color32::RED)
.texture_options(TextureOptions::NEAREST);
@@ -207,6 +212,11 @@ impl CharacterDataWindow {
ui.checkbox(&mut self.show_grid, "Show grid");
});
});
self.params.write(CharDataParams {
palette: self.palette,
index: self.index,
});
}
fn load_palette_colors(&self) -> [u8; 4] {
@@ -220,10 +230,7 @@ impl CharacterDataWindow {
}
fn show_chardata(&mut self, ui: &mut Ui) {
let resource = CharDataResource::CharacterData {
palette: self.palette,
};
let grid = CharacterGrid::new(resource.to_uri())
let grid = CharacterGrid::new("vram://chardata")
.with_scale(self.scale)
.with_grid(self.show_grid)
.with_selected(self.index);
@@ -254,7 +261,6 @@ impl AppWindow for CharacterDataWindow {
}
fn show(&mut self, ctx: &Context) {
self.loader.begin_pass();
CentralPanel::default().show(ctx, |ui| {
ui.horizontal_top(|ui| {
StripBuilder::new(ui)
@@ -279,6 +285,82 @@ enum CharDataResource {
CharacterData { palette: VramPalette },
}
#[derive(Clone, Default, PartialEq, Eq)]
struct CharDataParams {
palette: VramPalette,
index: usize,
}
struct CharDataRenderer {
chardata: MemoryView,
brightness: MemoryView,
palettes: MemoryView,
}
impl VramRenderer<2> for CharDataRenderer {
type Params = CharDataParams;
fn sizes(&self) -> [[usize; 2]; 2] {
[[8, 8], [16 * 8, 128 * 8]]
}
fn render(&mut self, params: &Self::Params, image: &mut [VramImage; 2]) {
self.render_character(&mut image[0], params.palette, params.index);
self.render_character_data(&mut image[1], params.palette);
}
}
impl CharDataRenderer {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
Self {
chardata: memory.view(sim_id, 0x00078000, 0x8000),
brightness: memory.view(sim_id, 0x0005f824, 8),
palettes: memory.view(sim_id, 0x0005f860, 16),
}
}
fn render_character(&self, image: &mut VramImage, palette: VramPalette, index: usize) {
if index >= 2048 {
return;
}
let palette = self.load_palette(palette);
let chardata = self.chardata.borrow();
let character = chardata.range::<u16>(index * 8, 8);
for (row, pixels) in character.iter().enumerate() {
for col in 0..8 {
let char = (pixels >> (col * 2)) & 0x03;
image.write((col, row), palette[char as usize]);
}
}
}
fn render_character_data(&self, image: &mut VramImage, palette: VramPalette) {
let palette = self.load_palette(palette);
let chardata = self.chardata.borrow();
for (row, pixels) in chardata.range::<u16>(0, 8 * 2048).iter().enumerate() {
let char_index = row / 8;
let row_index = row % 8;
let x = (char_index % 16) * 8;
let y = (char_index / 16) * 8 + row_index;
for col in 0..8 {
let char = (pixels >> (col * 2)) & 0x03;
image.write((x + col, y), palette[char as usize]);
}
}
}
fn load_palette(&self, palette: VramPalette) -> [u8; 4] {
let Some(offset) = palette.offset() else {
return utils::GENERIC_PALETTE;
};
let palette = self.palettes.borrow().read(offset);
let brightnesses = self.brightness.borrow();
let brts = brightnesses.range(0, 8);
utils::parse_palette(palette, brts)
}
}
/*
struct CharDataLoader {
chardata: MemoryView,
brightness: MemoryView,
@@ -373,3 +455,4 @@ impl VramImageLoader for CharDataLoader {
}
}
}
*/