VIP inspection tooling #4
30
src/vram.rs
30
src/vram.rs
|
@ -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() {
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
Loading…
Reference in New Issue