Avoid unnecessary computation/cloning in renderer

This commit is contained in:
Simon Gellis 2025-02-09 10:56:33 -05:00
parent 600148c781
commit a82389224f
3 changed files with 31 additions and 19 deletions

View File

@ -1,5 +1,5 @@
use std::{ use std::{
collections::{hash_map::Entry, HashMap}, collections::{hash_map::Entry, HashMap, HashSet},
hash::Hash, hash::Hash,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -11,12 +11,12 @@ use egui::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub trait VramResource: Sized + PartialEq + Eq + Hash { pub trait VramResource: Sized + Clone + PartialEq + Eq + Hash {
fn to_uri(&self) -> String; fn to_uri(&self) -> String;
fn from_uri(uri: &str) -> Option<Self>; fn from_uri(uri: &str) -> Option<Self>;
} }
impl<T: Serialize + for<'a> Deserialize<'a> + PartialEq + Eq + Hash> VramResource for T { impl<T: Serialize + for<'a> Deserialize<'a> + Clone + PartialEq + Eq + Hash> VramResource for T {
fn to_uri(&self) -> String { fn to_uri(&self) -> String {
format!("vram://{}", serde_json::to_string(self).unwrap()) format!("vram://{}", serde_json::to_string(self).unwrap())
} }
@ -28,7 +28,7 @@ impl<T: Serialize + for<'a> Deserialize<'a> + PartialEq + Eq + Hash> VramResourc
} }
pub enum VramImage { pub enum VramImage {
Unchanged(Arc<ColorImage>), Unchanged(Option<Arc<ColorImage>>),
Changed(ColorImage), Changed(ColorImage),
} }
@ -40,11 +40,12 @@ impl VramImage {
pub fn write(&mut self, coords: (usize, usize), shade: u8) { pub fn write(&mut self, coords: (usize, usize), shade: u8) {
match self { match self {
Self::Unchanged(image) => { Self::Unchanged(image) => {
let value = image[coords]; if image.as_ref().is_none_or(|i| i[coords].r() == shade) {
if value.r() == shade { return;
}
let Some(mut new_image) = image.take().map(Arc::unwrap_or_clone) else {
return; return;
}; };
let mut new_image = ColorImage::clone(image);
new_image[coords] = Color32::from_gray(shade); new_image[coords] = Color32::from_gray(shade);
*self = Self::Changed(new_image); *self = Self::Changed(new_image);
} }
@ -59,7 +60,7 @@ impl VramImage {
Self::Unchanged(_) => None, Self::Unchanged(_) => None,
Self::Changed(image) => { Self::Changed(image) => {
let arced = Arc::new(std::mem::take(image)); let arced = Arc::new(std::mem::take(image));
*self = Self::Unchanged(arced.clone()); *self = Self::Unchanged(Some(arced.clone()));
Some(arced) Some(arced)
} }
} }
@ -81,6 +82,7 @@ pub struct VramTextureLoader<T: VramImageLoader> {
id: String, id: String,
loader: Mutex<T>, loader: Mutex<T>,
cache: Mutex<HashMap<T::Resource, (TextureHandle, VramImage)>>, cache: Mutex<HashMap<T::Resource, (TextureHandle, VramImage)>>,
seen: Mutex<HashSet<T::Resource>>,
} }
impl<T: VramImageLoader> VramTextureLoader<T> { impl<T: VramImageLoader> VramTextureLoader<T> {
@ -89,8 +91,16 @@ impl<T: VramImageLoader> VramTextureLoader<T> {
id: loader.id().to_string(), id: loader.id().to_string(),
loader: Mutex::new(loader), loader: Mutex::new(loader),
cache: Mutex::new(HashMap::new()), cache: Mutex::new(HashMap::new()),
seen: Mutex::new(HashSet::new()),
} }
} }
pub fn begin_pass(&self) {
let mut cache = self.cache.lock().unwrap();
let mut seen = self.seen.lock().unwrap();
cache.retain(|res, _| seen.contains(res));
seen.clear();
}
} }
impl<T: VramImageLoader> TextureLoader for VramTextureLoader<T> { impl<T: VramImageLoader> TextureLoader for VramTextureLoader<T> {
@ -113,8 +123,10 @@ impl<T: VramImageLoader> TextureLoader for VramTextureLoader<T> {
"Only TextureOptions::NEAREST are supported".into(), "Only TextureOptions::NEAREST are supported".into(),
)); ));
} }
let loader = self.loader.lock().unwrap();
let mut cache = self.cache.lock().unwrap(); let mut cache = self.cache.lock().unwrap();
let mut seen = self.seen.lock().unwrap();
seen.insert(resource.clone());
let loader = self.loader.lock().unwrap();
let resources = cache.iter_mut().map(|(k, v)| (k, &mut v.1)); let resources = cache.iter_mut().map(|(k, v)| (k, &mut v.1));
loader.update(resources); loader.update(resources);
for (handle, image) in cache.values_mut() { for (handle, image) in cache.values_mut() {

View File

@ -18,7 +18,7 @@ use super::utils::{parse_palette, CharacterGrid, GENERIC_PALETTE};
pub struct BgMapWindow { pub struct BgMapWindow {
sim_id: SimId, sim_id: SimId,
loader: Option<BgMapLoader>, loader: Arc<VramTextureLoader<BgMapLoader>>,
bgmaps: MemoryView, bgmaps: MemoryView,
cell_index: usize, cell_index: usize,
cell_index_str: String, cell_index_str: String,
@ -31,7 +31,7 @@ impl BgMapWindow {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self { pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
Self { Self {
sim_id, sim_id,
loader: Some(BgMapLoader::new(sim_id, memory)), loader: Arc::new(VramTextureLoader::new(BgMapLoader::new(sim_id, memory))),
bgmaps: memory.view(sim_id, 0x00020000, 0x1d800), bgmaps: memory.view(sim_id, 0x00020000, 0x1d800),
cell_index: 0, cell_index: 0,
cell_index_str: "0".into(), cell_index_str: "0".into(),
@ -188,11 +188,11 @@ impl AppWindow for BgMapWindow {
} }
fn on_init(&mut self, ctx: &Context, _render_state: &egui_wgpu::RenderState) { fn on_init(&mut self, ctx: &Context, _render_state: &egui_wgpu::RenderState) {
let loader = self.loader.take().unwrap(); ctx.add_texture_loader(self.loader.clone());
ctx.add_texture_loader(Arc::new(VramTextureLoader::new(loader)));
} }
fn show(&mut self, ctx: &Context) { fn show(&mut self, ctx: &Context) {
self.loader.begin_pass();
CentralPanel::default().show(ctx, |ui| { CentralPanel::default().show(ctx, |ui| {
ui.horizontal_top(|ui| { ui.horizontal_top(|ui| {
StripBuilder::new(ui) StripBuilder::new(ui)
@ -219,7 +219,7 @@ fn parse_cell(cell: u16) -> (usize, bool, bool, usize) {
(char_index, vflip, hflip, palette_index) (char_index, vflip, hflip, palette_index)
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
enum BgMapResource { enum BgMapResource {
BgMap { index: usize, generic_palette: bool }, BgMap { index: usize, generic_palette: bool },
Cell { index: usize, generic_palette: bool }, Cell { index: usize, generic_palette: bool },

View File

@ -77,7 +77,7 @@ impl Display for VramPalette {
pub struct CharacterDataWindow { pub struct CharacterDataWindow {
sim_id: SimId, sim_id: SimId,
loader: Option<CharDataLoader>, loader: Arc<VramTextureLoader<CharDataLoader>>,
brightness: MemoryView, brightness: MemoryView,
palettes: MemoryView, palettes: MemoryView,
palette: VramPalette, palette: VramPalette,
@ -91,7 +91,7 @@ impl CharacterDataWindow {
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self { pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
Self { Self {
sim_id, sim_id,
loader: Some(CharDataLoader::new(sim_id, memory)), loader: Arc::new(VramTextureLoader::new(CharDataLoader::new(sim_id, memory))),
brightness: memory.view(sim_id, 0x0005f824, 8), brightness: memory.view(sim_id, 0x0005f824, 8),
palettes: memory.view(sim_id, 0x0005f860, 16), palettes: memory.view(sim_id, 0x0005f860, 16),
palette: VramPalette::Generic, palette: VramPalette::Generic,
@ -250,11 +250,11 @@ impl AppWindow for CharacterDataWindow {
} }
fn on_init(&mut self, ctx: &Context, _render_state: &egui_wgpu::RenderState) { fn on_init(&mut self, ctx: &Context, _render_state: &egui_wgpu::RenderState) {
let loader = self.loader.take().unwrap(); ctx.add_texture_loader(self.loader.clone());
ctx.add_texture_loader(Arc::new(VramTextureLoader::new(loader)));
} }
fn show(&mut self, ctx: &Context) { fn show(&mut self, ctx: &Context) {
self.loader.begin_pass();
CentralPanel::default().show(ctx, |ui| { CentralPanel::default().show(ctx, |ui| {
ui.horizontal_top(|ui| { ui.horizontal_top(|ui| {
StripBuilder::new(ui) StripBuilder::new(ui)
@ -273,7 +273,7 @@ impl AppWindow for CharacterDataWindow {
} }
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
enum CharDataResource { enum CharDataResource {
Character { palette: VramPalette, index: usize }, Character { palette: VramPalette, index: usize },
CharacterData { palette: VramPalette }, CharacterData { palette: VramPalette },