diff --git a/src/app.rs b/src/app.rs index cc433f7..db0b309 100644 --- a/src/app.rs +++ b/src/app.rs @@ -19,7 +19,7 @@ use crate::{ config::CliArgs, controller::ControllerManager, emulator::{EmulatorClient, EmulatorCommand, SimId}, - images::ImageProcessor, + images::ImageTextureLoader, input::{MappingProvider, ShortcutProvider}, memory::MemoryClient, persistence::Persistence, @@ -50,7 +50,7 @@ pub struct Application { shortcuts: ShortcutProvider, controllers: ControllerManager, memory: Arc, - images: ImageProcessor, + images: Arc, persistence: Persistence, viewports: HashMap, focused: Option, @@ -78,7 +78,7 @@ impl Application { let shortcuts = ShortcutProvider::new(persistence.clone()); let controllers = ControllerManager::new(client.clone(), &mappings); let memory = Arc::new(MemoryClient::new(client.clone())); - let images = ImageProcessor::new(); + let images = Arc::new(ImageTextureLoader::new()); { let mappings = mappings.clone(); let proxy = proxy.clone(); @@ -126,7 +126,13 @@ impl Application { } self.viewports.insert( viewport_id, - Viewport::new(event_loop, &self.wgpu, self.icon.clone(), window), + Viewport::new( + &self.images, + event_loop, + &self.wgpu, + self.icon.clone(), + window, + ), ); } } @@ -154,23 +160,23 @@ impl ApplicationHandler for Application { self.open(event_loop, Box::new(profiler)); } if self.init_chardata { - let chardata = CharacterDataWindow::new(sim_id, &self.memory, &mut self.images); + let chardata = CharacterDataWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(chardata)); } if self.init_bgmap { - let bgmap = BgMapWindow::new(sim_id, &self.memory, &mut self.images); + let bgmap = BgMapWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(bgmap)); } if self.init_objects { - let objects = ObjectWindow::new(sim_id, &self.memory, &mut self.images); + let objects = ObjectWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(objects)); } if self.init_worlds { - let world = WorldWindow::new(sim_id, &self.memory, &mut self.images); + let world = WorldWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(world)); } if self.init_framebuffers { - let fb = FrameBufferWindow::new(sim_id, &self.memory, &mut self.images); + let fb = FrameBufferWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(fb)); } if self.init_registers { @@ -280,23 +286,23 @@ impl ApplicationHandler for Application { self.open(event_loop, Box::new(about)); } UserEvent::OpenCharacterData(sim_id) => { - let chardata = CharacterDataWindow::new(sim_id, &self.memory, &mut self.images); + let chardata = CharacterDataWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(chardata)); } UserEvent::OpenBgMap(sim_id) => { - let bgmap = BgMapWindow::new(sim_id, &self.memory, &mut self.images); + let bgmap = BgMapWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(bgmap)); } UserEvent::OpenObjects(sim_id) => { - let objects = ObjectWindow::new(sim_id, &self.memory, &mut self.images); + let objects = ObjectWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(objects)); } UserEvent::OpenWorlds(sim_id) => { - let world = WorldWindow::new(sim_id, &self.memory, &mut self.images); + let world = WorldWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(world)); } UserEvent::OpenFrameBuffers(sim_id) => { - let fb = FrameBufferWindow::new(sim_id, &self.memory, &mut self.images); + let fb = FrameBufferWindow::new(sim_id, &self.memory, &self.images); self.open(event_loop, Box::new(fb)); } UserEvent::OpenRegisters(sim_id) => { @@ -422,6 +428,7 @@ struct Viewport { } impl Viewport { pub fn new( + images: &Arc, event_loop: &ActiveEventLoop, wgpu: &WgpuState, icon: Option>, @@ -447,6 +454,7 @@ impl Viewport { s.spacing.scroll = ScrollStyle::thin(); }); egui_extras::install_image_loaders(&ctx); + ctx.add_texture_loader(images.clone()); let wgpu_config = egui_wgpu::WgpuConfiguration { present_mode: wgpu::PresentMode::AutoVsync, @@ -477,7 +485,6 @@ impl Viewport { let render_state = painter.render_state(); let args = InitArgs { - ctx: &ctx, window: &window, render_state: render_state.as_ref().unwrap(), }; diff --git a/src/images.rs b/src/images.rs index 0866d16..7f2a803 100644 --- a/src/images.rs +++ b/src/images.rs @@ -1,5 +1,5 @@ use std::{ - collections::HashMap, + collections::{HashMap, hash_map::Entry}, ops::Deref, sync::{Arc, Mutex, Weak}, thread, @@ -7,17 +7,21 @@ use std::{ }; use egui::{ - Color32, ColorImage, TextureHandle, TextureOptions, + Color32, ColorImage, TextureHandle, epaint::ImageDelta, + generate_loader_id, load::{LoadError, SizedTexture, TextureLoader, TexturePoll}, }; use tokio::{sync::mpsc, time::timeout}; -pub struct ImageProcessor { +use crate::emulator::SimId; + +pub struct ImageTextureLoader { + cache: Mutex>, sender: mpsc::UnboundedSender>, } -impl ImageProcessor { +impl ImageTextureLoader { pub fn new() -> Self { let (sender, receiver) = mpsc::unbounded_channel(); thread::spawn(move || { @@ -26,29 +30,52 @@ impl ImageProcessor { .build() .unwrap() .block_on(async move { - let mut worker = ImageProcessorWorker { + let mut worker = ImageTextureLoaderWorker { receiver, renderers: vec![], }; worker.run().await }) }); - Self { sender } + Self { + cache: Mutex::new(HashMap::new()), + sender, + } } pub fn add + 'static>( &self, + sim_id: SimId, renderer: R, params: R::Params, - ) -> ([ImageHandle; N], ImageParams) { + ) -> ImageParams { let states = renderer.sizes().map(ImageState::new); - let handles = states.clone().map(|state| ImageHandle { - size: state.size.map(|i| i as f32), - data: state.sink, - }); let images = renderer .sizes() .map(|[width, height]| ImageBuffer::new(width, height)); + { + let mut cache = self.cache.lock().unwrap(); + for (name, state) in renderer.names().into_iter().zip(&states) { + let url = format!("vip://{sim_id}/{name}"); + let entry = ImageEntry { + size: state.size.map(|i| i as f32), + data: Arc::downgrade(&state.sink), + texture: None, + }; + match cache.entry(url) { + Entry::Vacant(e) => { + e.insert(entry); + } + Entry::Occupied(mut e) => { + // Only overwrite an old entry if that entry is dead. + // Otherwise, we clear out the reference to a texture actually in use. + if e.get().data.strong_count() == 0 { + e.insert(entry); + } + } + } + } + } let sink = Arc::new(Mutex::new(params.clone())); let _ = self.sender.send(Box::new(ImageRendererWrapper { renderer, @@ -56,20 +83,87 @@ impl ImageProcessor { images, states, })); - let params = ImageParams { + ImageParams { value: params, sink, - }; - (handles, params) + } } } -struct ImageProcessorWorker { +impl TextureLoader for ImageTextureLoader { + fn id(&self) -> &str { + generate_loader_id!(ImageTextureLoader2) + } + + fn load( + &self, + ctx: &egui::Context, + uri: &str, + texture_options: egui::TextureOptions, + _size_hint: egui::SizeHint, + ) -> Result { + let mut cache = self.cache.lock().unwrap(); + let Some(entry) = cache.get_mut(uri) else { + return Err(LoadError::NotSupported); + }; + if texture_options != egui::TextureOptions::NEAREST { + return Err(LoadError::Loading( + "Only TextureOptions::NEAREST are supported".into(), + )); + } + let Some(data) = entry.data.upgrade() else { + cache.remove(uri); + return Err(LoadError::Loading("Backing loader no longer exists".into())); + }; + match (data.lock().unwrap().take(), entry.texture.as_ref()) { + (Some(update), Some(handle)) => { + let delta = ImageDelta::full(update, texture_options); + ctx.tex_manager().write().set(handle.id(), delta); + let texture = SizedTexture::new(handle, entry.size); + Ok(TexturePoll::Ready { texture }) + } + (Some(update), None) => { + let handle = ctx.load_texture(uri, update, texture_options); + let texture = SizedTexture::new(&handle, entry.size); + entry.texture.replace(handle); + Ok(TexturePoll::Ready { texture }) + } + (None, Some(handle)) => { + let texture = SizedTexture::new(handle, entry.size); + Ok(TexturePoll::Ready { texture }) + } + (None, None) => { + let size = entry.size.into(); + Ok(TexturePoll::Pending { size: Some(size) }) + } + } + } + + fn forget(&self, uri: &str) { + let _ = uri; + } + + fn forget_all(&self) {} + + fn byte_size(&self) -> usize { + self.cache + .lock() + .unwrap() + .values() + .map(|entry| { + let [width, height] = entry.size; + width as usize * height as usize * 4 + }) + .sum() + } +} + +struct ImageTextureLoaderWorker { receiver: mpsc::UnboundedReceiver>, renderers: Vec>, } -impl ImageProcessorWorker { +impl ImageTextureLoaderWorker { async fn run(&mut self) { loop { if self.renderers.is_empty() { @@ -142,16 +236,10 @@ impl ImageBuffer { } } -#[derive(Clone)] -pub struct ImageHandle { +struct ImageEntry { size: [f32; 2], - data: Arc>>>, -} - -impl ImageHandle { - fn pull(&mut self) -> Option> { - self.data.lock().unwrap().take() - } + data: Weak>>>, + texture: Option, } pub struct ImageParams { @@ -178,6 +266,7 @@ impl ImageParams { pub trait ImageRenderer: Send { type Params: Clone + Send; + fn names(&self) -> [&str; N]; fn sizes(&self) -> [[usize; 2]; N]; fn render(&mut self, params: &Self::Params, images: &mut [ImageBuffer; N]); } @@ -246,83 +335,3 @@ impl + Send> ImageRendererImpl for ImageRend Ok(()) } } - -pub struct ImageTextureLoader { - cache: Mutex)>>, -} - -impl ImageTextureLoader { - pub fn new(renderers: impl IntoIterator) -> Self { - let mut cache = HashMap::new(); - for (key, image) in renderers { - cache.insert(key, (image, None)); - } - Self { - cache: Mutex::new(cache), - } - } -} - -impl TextureLoader for ImageTextureLoader { - fn id(&self) -> &str { - concat!(module_path!(), "ImageTextureLoader") - } - - fn load( - &self, - ctx: &egui::Context, - uri: &str, - texture_options: TextureOptions, - _size_hint: egui::SizeHint, - ) -> Result { - let mut cache = self.cache.lock().unwrap(); - let Some((image, maybe_handle)) = cache.get_mut(uri) else { - return Err(LoadError::NotSupported); - }; - if texture_options != TextureOptions::NEAREST { - return Err(LoadError::Loading( - "Only TextureOptions::NEAREST are supported".into(), - )); - } - match (image.pull(), maybe_handle.as_ref()) { - (Some(update), Some(handle)) => { - let delta = ImageDelta::full(update, texture_options); - ctx.tex_manager().write().set(handle.id(), delta); - let texture = SizedTexture::new(handle, image.size); - Ok(TexturePoll::Ready { texture }) - } - (Some(update), None) => { - let handle = ctx.load_texture(uri, update, texture_options); - let texture = SizedTexture::new(&handle, image.size); - maybe_handle.replace(handle); - Ok(TexturePoll::Ready { texture }) - } - (None, Some(handle)) => { - let texture = SizedTexture::new(handle, image.size); - Ok(TexturePoll::Ready { texture }) - } - (None, None) => { - let size = image.size.into(); - Ok(TexturePoll::Pending { size: Some(size) }) - } - } - } - - fn forget(&self, uri: &str) { - let _ = uri; - } - - fn forget_all(&self) {} - - fn byte_size(&self) -> usize { - self.cache - .lock() - .unwrap() - .values() - .map(|(image, _)| { - let [width, height] = image.size; - width as usize * height as usize * 4 - }) - .sum() - } -} diff --git a/src/window.rs b/src/window.rs index 9b10a55..6923d07 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,7 +1,7 @@ use std::sync::Arc; pub use about::AboutWindow; -use egui::{Context, Ui, ViewportBuilder, ViewportId}; +use egui::{Ui, ViewportBuilder, ViewportId}; pub use game::GameWindow; pub use game_screen::DisplayMode; pub use gdb::GdbServerWindow; @@ -32,6 +32,9 @@ pub trait AppWindow { fn sim_id(&self) -> SimId { SimId::Player1 } + fn image_url(&self, name: &str) -> String { + format!("vip://{}/{name}", self.sim_id()) + } fn initial_viewport(&self) -> ViewportBuilder; fn show(&mut self, ui: &mut Ui); fn on_init(&mut self, args: InitArgs) { @@ -49,7 +52,6 @@ pub trait AppWindow { } pub struct InitArgs<'a> { - pub ctx: &'a Context, pub window: &'a Arc, pub render_state: &'a egui_wgpu::RenderState, } diff --git a/src/window/vip/bgmap.rs b/src/window/vip/bgmap.rs index 33a8927..d122ef2 100644 --- a/src/window/vip/bgmap.rs +++ b/src/window/vip/bgmap.rs @@ -8,10 +8,10 @@ use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use crate::{ emulator::SimId, - images::{ImageBuffer, ImageParams, ImageProcessor, ImageRenderer, ImageTextureLoader}, + images::{ImageBuffer, ImageParams, ImageRenderer, ImageTextureLoader}, memory::{MemoryClient, MemoryView}, window::{ - AppWindow, InitArgs, + AppWindow, utils::{NumberEdit, UiExt}, }, }; @@ -20,7 +20,6 @@ use super::utils::{self, CellData, CharacterGrid}; pub struct BgMapWindow { sim_id: SimId, - loader: Arc, memory: Arc, bgmaps: MemoryView, cell_index: usize, @@ -31,14 +30,11 @@ pub struct BgMapWindow { } impl BgMapWindow { - pub fn new(sim_id: SimId, memory: &Arc, images: &mut ImageProcessor) -> Self { + pub fn new(sim_id: SimId, memory: &Arc, images: &ImageTextureLoader) -> Self { let renderer = BgMapRenderer::new(sim_id, memory); - let ([cell, bgmap], params) = images.add(renderer, BgMapParams::default()); - let loader = - ImageTextureLoader::new([("vip://cell".into(), cell), ("vip://bgmap".into(), bgmap)]); + let params = images.add(sim_id, renderer, BgMapParams::default()); Self { sim_id, - loader: Arc::new(loader), memory: memory.clone(), bgmaps: memory.watch(sim_id, 0x00020000, 0x20000), cell_index: params.cell_index, @@ -90,7 +86,7 @@ impl BgMapWindow { }); }); }); - let image = Image::new("vip://cell") + let image = Image::new(self.image_url("bgmap-cell")) .maintain_aspect_ratio(true) .texture_options(TextureOptions::NEAREST); ui.add(image); @@ -162,7 +158,7 @@ impl BgMapWindow { } fn show_bgmap(&mut self, ui: &mut Ui) { - let grid = CharacterGrid::new("vip://bgmap") + let grid = CharacterGrid::new(self.image_url("bgmap")) .with_scale(self.scale) .with_grid(self.show_grid) .with_selected(self.cell_index % 4096); @@ -187,10 +183,6 @@ impl AppWindow for BgMapWindow { .with_inner_size((640.0, 480.0)) } - fn on_init(&mut self, args: InitArgs) { - args.ctx.add_texture_loader(self.loader.clone()); - } - fn show(&mut self, ui: &mut Ui) { CentralPanel::default().show_inside(ui, |ui| { ui.horizontal_top(|ui| { @@ -300,6 +292,10 @@ impl BgMapRenderer { impl ImageRenderer<2> for BgMapRenderer { type Params = BgMapParams; + fn names(&self) -> [&str; 2] { + ["bgmap-cell", "bgmap"] + } + fn sizes(&self) -> [[usize; 2]; 2] { [[8, 8], [8 * 64, 8 * 64]] } diff --git a/src/window/vip/chardata.rs b/src/window/vip/chardata.rs index 15fcb70..660a00c 100644 --- a/src/window/vip/chardata.rs +++ b/src/window/vip/chardata.rs @@ -1,4 +1,4 @@ -use std::{fmt::Display, sync::Arc}; +use std::fmt::Display; use egui::{ Align, CentralPanel, Color32, ComboBox, Image, ScrollArea, Slider, TextEdit, TextureOptions, @@ -9,10 +9,10 @@ use serde::{Deserialize, Serialize}; use crate::{ emulator::SimId, - images::{ImageBuffer, ImageParams, ImageProcessor, ImageRenderer, ImageTextureLoader}, + images::{ImageBuffer, ImageParams, ImageRenderer, ImageTextureLoader}, memory::{MemoryClient, MemoryView}, window::{ - AppWindow, InitArgs, + AppWindow, utils::{NumberEdit, UiExt as _}, }, }; @@ -81,7 +81,6 @@ impl Display for Palette { pub struct CharacterDataWindow { sim_id: SimId, - loader: Arc, brightness: MemoryView, palettes: MemoryView, palette: Palette, @@ -92,16 +91,11 @@ pub struct CharacterDataWindow { } impl CharacterDataWindow { - pub fn new(sim_id: SimId, memory: &MemoryClient, images: &mut ImageProcessor) -> Self { + pub fn new(sim_id: SimId, memory: &MemoryClient, images: &ImageTextureLoader) -> Self { let renderer = CharDataRenderer::new(sim_id, memory); - let ([char, chardata], params) = images.add(renderer, CharDataParams::default()); - let loader = ImageTextureLoader::new([ - ("vip://char".into(), char), - ("vip://chardata".into(), chardata), - ]); + let params = images.add(sim_id, renderer, CharDataParams::default()); Self { sim_id, - loader: Arc::new(loader), brightness: memory.watch(sim_id, 0x0005f824, 8), palettes: memory.watch(sim_id, 0x0005f860, 16), palette: params.palette, @@ -160,7 +154,7 @@ impl CharacterDataWindow { }); }); }); - let image = Image::new("vip://char") + let image = Image::new(self.image_url("chardata-char")) .maintain_aspect_ratio(true) .texture_options(TextureOptions::NEAREST); ui.add(image); @@ -226,7 +220,7 @@ impl CharacterDataWindow { } fn show_chardata(&mut self, ui: &mut Ui) { - let grid = CharacterGrid::new("vip://chardata") + let grid = CharacterGrid::new(self.image_url("chardata")) .with_scale(self.scale) .with_grid(self.show_grid) .with_selected(self.index); @@ -251,10 +245,6 @@ impl AppWindow for CharacterDataWindow { .with_inner_size((640.0, 480.0)) } - fn on_init(&mut self, args: InitArgs) { - args.ctx.add_texture_loader(self.loader.clone()); - } - fn show(&mut self, ui: &mut Ui) { CentralPanel::default().show_inside(ui, |ui| { ui.horizontal_top(|ui| { @@ -289,6 +279,10 @@ struct CharDataRenderer { impl ImageRenderer<2> for CharDataRenderer { type Params = CharDataParams; + fn names(&self) -> [&str; 2] { + ["chardata-char", "chardata"] + } + fn sizes(&self) -> [[usize; 2]; 2] { [[8, 8], [16 * 8, 128 * 8]] } diff --git a/src/window/vip/framebuffer.rs b/src/window/vip/framebuffer.rs index 328512d..23cf9d5 100644 --- a/src/window/vip/framebuffer.rs +++ b/src/window/vip/framebuffer.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use egui::{ Align, CentralPanel, Color32, Image, ScrollArea, Slider, TextEdit, TextureOptions, Ui, ViewportBuilder, ViewportId, @@ -8,10 +6,10 @@ use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use crate::{ emulator::SimId, - images::{ImageBuffer, ImageParams, ImageProcessor, ImageRenderer, ImageTextureLoader}, + images::{ImageBuffer, ImageParams, ImageRenderer, ImageTextureLoader}, memory::{MemoryClient, MemoryView}, window::{ - AppWindow, InitArgs, + AppWindow, utils::{NumberEdit, UiExt as _}, }, }; @@ -20,7 +18,6 @@ use super::utils; pub struct FrameBufferWindow { sim_id: SimId, - loader: Arc, index: usize, left: bool, right: bool, @@ -30,7 +27,7 @@ pub struct FrameBufferWindow { } impl FrameBufferWindow { - pub fn new(sim_id: SimId, memory: &MemoryClient, images: &mut ImageProcessor) -> Self { + pub fn new(sim_id: SimId, memory: &MemoryClient, images: &ImageTextureLoader) -> Self { let initial_params = FrameBufferParams { index: 0, left: true, @@ -40,11 +37,9 @@ impl FrameBufferWindow { right_color: Color32::from_rgb(0x00, 0xc6, 0xf0), }; let renderer = FrameBufferRenderer::new(sim_id, memory); - let ([buffer], params) = images.add(renderer, initial_params); - let loader = ImageTextureLoader::new([("vip://buffer".into(), buffer)]); + let params = images.add(sim_id, renderer, initial_params); Self { sim_id, - loader: Arc::new(loader), index: params.index, left: params.left, right: params.right, @@ -131,7 +126,7 @@ impl FrameBufferWindow { } fn show_buffers(&mut self, ui: &mut Ui) { - let image = Image::new("vip://buffer") + let image = Image::new(self.image_url("buffer")) .fit_to_original_size(self.scale) .texture_options(TextureOptions::NEAREST); ui.add(image); @@ -153,10 +148,6 @@ impl AppWindow for FrameBufferWindow { .with_inner_size((640.0, 480.0)) } - fn on_init(&mut self, args: InitArgs) { - args.ctx.add_texture_loader(self.loader.clone()); - } - fn show(&mut self, ui: &mut Ui) { CentralPanel::default().show_inside(ui, |ui| { ui.horizontal_top(|ui| { @@ -208,6 +199,10 @@ impl FrameBufferRenderer { impl ImageRenderer<1> for FrameBufferRenderer { type Params = FrameBufferParams; + fn names(&self) -> [&str; 1] { + ["buffer"] + } + fn sizes(&self) -> [[usize; 2]; 1] { [[384, 224]] } diff --git a/src/window/vip/object.rs b/src/window/vip/object.rs index 71065cf..bf9972d 100644 --- a/src/window/vip/object.rs +++ b/src/window/vip/object.rs @@ -8,10 +8,10 @@ use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use crate::{ emulator::SimId, - images::{ImageBuffer, ImageParams, ImageProcessor, ImageRenderer, ImageTextureLoader}, + images::{ImageBuffer, ImageParams, ImageRenderer, ImageTextureLoader}, memory::{MemoryClient, MemoryView}, window::{ - AppWindow, InitArgs, + AppWindow, utils::{NumberEdit, UiExt as _}, }, }; @@ -20,7 +20,6 @@ use super::utils::{self, Object}; pub struct ObjectWindow { sim_id: SimId, - loader: Arc, memory: Arc, objects: MemoryView, index: usize, @@ -30,7 +29,7 @@ pub struct ObjectWindow { } impl ObjectWindow { - pub fn new(sim_id: SimId, memory: &Arc, images: &mut ImageProcessor) -> Self { + pub fn new(sim_id: SimId, memory: &Arc, images: &ImageTextureLoader) -> Self { let initial_params = ObjectParams { index: 0, generic_palette: false, @@ -38,12 +37,9 @@ impl ObjectWindow { right_color: Color32::from_rgb(0x00, 0xc6, 0xf0), }; let renderer = ObjectRenderer::new(sim_id, memory); - let ([zoom, full], params) = images.add(renderer, initial_params); - let loader = - ImageTextureLoader::new([("vip://zoom".into(), zoom), ("vip://full".into(), full)]); + let params = images.add(sim_id, renderer, initial_params); Self { sim_id, - loader: Arc::new(loader), memory: memory.clone(), objects: memory.watch(sim_id, 0x0003e000, 0x2000), index: params.index, @@ -208,10 +204,6 @@ impl AppWindow for ObjectWindow { .with_inner_size((640.0, 500.0)) } - fn on_init(&mut self, args: InitArgs) { - args.ctx.add_texture_loader(self.loader.clone()); - } - fn show(&mut self, ui: &mut Ui) { CentralPanel::default().show_inside(ui, |ui| { ui.horizontal_top(|ui| { @@ -327,6 +319,10 @@ impl ObjectRenderer { impl ImageRenderer<2> for ObjectRenderer { type Params = ObjectParams; + fn names(&self) -> [&str; 2] { + ["object-zoom", "object-full"] + } + fn sizes(&self) -> [[usize; 2]; 2] { [[8, 8], [384, 224]] } diff --git a/src/window/vip/world.rs b/src/window/vip/world.rs index 50373d4..511bdbd 100644 --- a/src/window/vip/world.rs +++ b/src/window/vip/world.rs @@ -14,10 +14,10 @@ use num_traits::{FromPrimitive, ToPrimitive}; use crate::{ emulator::SimId, - images::{ImageBuffer, ImageParams, ImageProcessor, ImageRenderer, ImageTextureLoader}, + images::{ImageBuffer, ImageParams, ImageRenderer, ImageTextureLoader}, memory::{MemoryClient, MemoryRef, MemoryView}, window::{ - AppWindow, InitArgs, + AppWindow, utils::{NumberEdit, UiExt as _}, }, }; @@ -26,7 +26,6 @@ use super::utils::{self, CellData, Object, shade}; pub struct WorldWindow { sim_id: SimId, - loader: Arc, memory: Arc, worlds: MemoryView, bgmaps: MemoryView, @@ -39,7 +38,7 @@ pub struct WorldWindow { } impl WorldWindow { - pub fn new(sim_id: SimId, memory: &Arc, images: &mut ImageProcessor) -> Self { + pub fn new(sim_id: SimId, memory: &Arc, images: &ImageTextureLoader) -> Self { let initial_params = WorldParams { index: 31, generic_palette: false, @@ -47,11 +46,9 @@ impl WorldWindow { right_color: Color32::from_rgb(0x00, 0xc6, 0xf0), }; let renderer = WorldRenderer::new(sim_id, memory); - let ([world], params) = images.add(renderer, initial_params); - let loader = ImageTextureLoader::new([("vip://world".into(), world)]); + let params = images.add(sim_id, renderer, initial_params); Self { sim_id, - loader: Arc::new(loader), memory: memory.clone(), worlds: memory.watch(sim_id, 0x0003d800, 0x400), bgmaps: memory.watch(sim_id, 0x00020000, 0x20000), @@ -426,7 +423,7 @@ impl WorldWindow { } fn show_world(&mut self, ui: &mut Ui) { - let image = Image::new("vip://world") + let image = Image::new(self.image_url("world")) .fit_to_original_size(self.scale) .texture_options(TextureOptions::NEAREST); let res = ui.add(image); @@ -523,10 +520,6 @@ impl AppWindow for WorldWindow { .with_inner_size((640.0, 520.0)) } - fn on_init(&mut self, args: InitArgs) { - args.ctx.add_texture_loader(self.loader.clone()); - } - fn show(&mut self, ui: &mut Ui) { CentralPanel::default().show_inside(ui, |ui| { ui.horizontal_top(|ui| { @@ -779,6 +772,10 @@ impl WorldRenderer { impl ImageRenderer<1> for WorldRenderer { type Params = WorldParams; + fn names(&self) -> [&str; 1] { + ["world"] + } + fn sizes(&self) -> [[usize; 2]; 1] { [[384, 224]] }