debug_ui: Render bitmap in new 'Bitmap' display object tab

This lets us view a bitmap directly from the debug ui.
We cache an egui texture handle on the `BitmapData` itself,
and invalidate it whenever the BitmapData gets changed.
This commit is contained in:
Aaron Hill 2024-05-14 19:27:42 -04:00
parent 4d2b2893ae
commit 932f2d410b
2 changed files with 53 additions and 3 deletions

View File

@ -226,6 +226,12 @@ pub struct BitmapData<'gc> {
#[collect(require_static)]
dirty_state: DirtyState,
/// Holds an egui texture handle, used for rendering this Bitmap in the debug ui.
/// This is automatically set to `None` when the texture is updated (either from
/// marking the CPU side dirty, or from performing a GPU -> CPU sync).
#[cfg(feature = "egui")]
pub egui_texture: std::cell::RefCell<Option<egui::TextureHandle>>,
}
#[derive(Clone, Debug)]
@ -306,6 +312,8 @@ mod wrapper {
avm2_object: None,
display_objects: vec![],
dirty_state: DirtyState::Clean,
#[cfg(feature = "egui")]
egui_texture: Default::default(),
},
))
}
@ -328,6 +336,8 @@ mod wrapper {
display_objects: vec![],
// We have no GPU texture, so there's no need to mark as dirty
dirty_state: DirtyState::Clean,
#[cfg(feature = "egui")]
egui_texture: Default::default(),
}
}
@ -349,7 +359,9 @@ mod wrapper {
}),
)
.expect("Failed to sync BitmapData");
write.dirty_state = DirtyState::Clean
write.dirty_state = DirtyState::Clean;
#[cfg(feature = "egui")]
write.egui_texture.borrow_mut().take();
}
old_state => write.dirty_state = old_state,
}
@ -389,6 +401,8 @@ mod wrapper {
}
DirtyState::CpuModified(_) | DirtyState::Clean => None,
};
#[cfg(feature = "egui")]
write.egui_texture.borrow_mut().take();
(self.0, dirty_rect)
}
@ -561,6 +575,8 @@ impl<'gc> BitmapData<'gc> {
avm2_object: None,
display_objects: vec![],
dirty_state: DirtyState::Clean,
#[cfg(feature = "egui")]
egui_texture: Default::default(),
}
}
@ -580,6 +596,8 @@ impl<'gc> BitmapData<'gc> {
disposed: false,
dirty_state: DirtyState::Clean,
display_objects: vec![],
#[cfg(feature = "egui")]
egui_texture: Default::default(),
}
}
@ -632,6 +650,10 @@ impl<'gc> BitmapData<'gc> {
pub fn set_cpu_dirty(&mut self, gc_context: &Mutation<'gc>, region: PixelRegion) {
debug_assert!(region.x_max <= self.width);
debug_assert!(region.y_max <= self.height);
#[cfg(feature = "egui")]
self.egui_texture.borrow_mut().take();
let inform_changes = match &mut self.dirty_state {
DirtyState::CpuModified(old_region) => {
old_region.union(region);

View File

@ -10,7 +10,7 @@ use crate::debug_ui::handle::{AVM1ObjectHandle, AVM2ObjectHandle, DisplayObjectH
use crate::debug_ui::movie::open_movie_button;
use crate::debug_ui::Message;
use crate::display_object::{
DisplayObject, EditText, InteractiveObject, MovieClip, Stage, TDisplayObject,
Bitmap, DisplayObject, EditText, InteractiveObject, MovieClip, Stage, TDisplayObject,
TDisplayObjectContainer, TInteractiveObject,
};
use crate::focus_tracker::Highlight;
@ -160,6 +160,8 @@ impl DisplayObjectWindow {
self.show_movieclip(ui, context, object)
} else if let DisplayObject::EditText(object) = object {
self.show_edit_text(ui, object)
} else if let DisplayObject::Bitmap(object) = object {
self.show_bitmap(ui, context, object)
} else if let DisplayObject::Stage(object) = object {
self.show_stage(ui, context, object, messages)
}
@ -346,6 +348,29 @@ impl DisplayObjectWindow {
});
}
pub fn show_bitmap<'gc>(
&mut self,
ui: &mut Ui,
context: &mut UpdateContext<'_, 'gc>,
object: Bitmap<'gc>,
) {
let bitmap_data = object.bitmap_data(context.renderer);
let bitmap_data = bitmap_data.read();
let mut egui_texture = bitmap_data.egui_texture.borrow_mut();
let texture = egui_texture.get_or_insert_with(|| {
let image = egui::ColorImage::from_rgba_premultiplied(
[bitmap_data.width() as usize, bitmap_data.height() as usize],
&bitmap_data.pixels_rgba(),
);
ui.ctx().load_texture(
format!("bitmap-{:?}", object.as_ptr()),
image,
Default::default(),
)
});
ui.image((texture.id(), texture.size_vec2()));
}
pub fn show_movieclip<'gc>(
&mut self,
ui: &mut Ui,
@ -979,7 +1004,10 @@ fn summary_color_transform_entry(name: &str, mult: Fixed8, add: i16) -> Option<S
fn has_type_specific_tab(object: DisplayObject) -> bool {
matches!(
object,
DisplayObject::MovieClip(_) | DisplayObject::EditText(_) | DisplayObject::Stage(_)
DisplayObject::MovieClip(_)
| DisplayObject::EditText(_)
| DisplayObject::Bitmap(_)
| DisplayObject::Stage(_)
)
}