Render images on background thread to keep UI responsive
This commit is contained in:
+37
-67
@@ -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
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user