use std::sync::Arc;
use uzor::framework::app::{App, NoPanel};
use uzor::layout::docking::DockPanel;
pub use uzor_render_hub::CachedOverlayJob;
pub struct Scene3DFrame {
pub scene: uzor_urx_3d::Scene3D,
pub camera: uzor_urx_3d::PerspectiveCamera,
pub cached_overlay: Option<CachedOverlayJob>,
pub overlay: Option<Box<dyn FnMut(&mut dyn uzor::render::RenderContext)>>,
}
pub struct RetainedScene3DFrame {
pub scene: Arc<uzor_urx_3d::Scene3D>,
pub camera: uzor_urx_3d::PerspectiveCamera,
pub cached_overlay: Option<CachedOverlayJob>,
pub overlay: Option<Box<dyn FnMut(&mut dyn uzor::render::RenderContext)>>,
}
pub(crate) enum Scene3DFrameSubmission {
Owned(Scene3DFrame),
Retained(RetainedScene3DFrame),
}
impl Scene3DFrameSubmission {
pub(crate) fn scene(&self) -> &uzor_urx_3d::Scene3D {
match self {
Self::Owned(frame) => &frame.scene,
Self::Retained(frame) => frame.scene.as_ref(),
}
}
pub(crate) fn camera(&self) -> uzor_urx_3d::PerspectiveCamera {
match self {
Self::Owned(frame) => frame.camera,
Self::Retained(frame) => frame.camera,
}
}
pub(crate) fn take_cached_overlay(&mut self) -> Option<CachedOverlayJob> {
match self {
Self::Owned(frame) => frame.cached_overlay.take(),
Self::Retained(frame) => frame.cached_overlay.take(),
}
}
pub(crate) fn take_overlay(
&mut self,
) -> Option<Box<dyn FnMut(&mut dyn uzor::render::RenderContext)>> {
match self {
Self::Owned(frame) => frame.overlay.take(),
Self::Retained(frame) => frame.overlay.take(),
}
}
}
pub(crate) fn scene3d_frame_submission<A, P>(
app: &mut A,
surf_w: u32,
surf_h: u32,
) -> Option<Scene3DFrameSubmission>
where
A: Scene3DApp<P>,
P: DockPanel,
{
app.retained_scene3d(surf_w, surf_h)
.map(Scene3DFrameSubmission::Retained)
.or_else(|| {
app.scene3d(surf_w, surf_h)
.map(Scene3DFrameSubmission::Owned)
})
}
pub trait Scene3DApp<P: DockPanel = NoPanel>: App<P> {
fn scene3d(&mut self, _surf_w: u32, _surf_h: u32) -> Option<Scene3DFrame> {
None
}
fn retained_scene3d(
&mut self,
_surf_w: u32,
_surf_h: u32,
) -> Option<RetainedScene3DFrame> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use uzor::framework::multi_window::WindowCtx;
use uzor_urx_3d::{PerspectiveCamera, Scene3D, Vec3};
struct RetainedApp {
scene: Arc<Scene3D>,
}
impl App<NoPanel> for RetainedApp {
fn ui(&mut self, _win: &mut WindowCtx<'_, NoPanel>) {}
}
impl Scene3DApp for RetainedApp {
fn retained_scene3d(
&mut self,
_surf_w: u32,
_surf_h: u32,
) -> Option<RetainedScene3DFrame> {
Some(RetainedScene3DFrame {
scene: Arc::clone(&self.scene),
camera: camera(),
cached_overlay: None,
overlay: None,
})
}
}
struct OwnedApp;
impl App<NoPanel> for OwnedApp {
fn ui(&mut self, _win: &mut WindowCtx<'_, NoPanel>) {}
}
impl Scene3DApp for OwnedApp {
fn scene3d(&mut self, _surf_w: u32, _surf_h: u32) -> Option<Scene3DFrame> {
let mut scene = Scene3D::new();
scene.clear_color = [0.1, 0.2, 0.3, 1.0];
Some(Scene3DFrame {
scene,
camera: camera(),
cached_overlay: None,
overlay: None,
})
}
}
fn camera() -> PerspectiveCamera {
PerspectiveCamera::new(Vec3::new(0.0, 0.0, 2.0), Vec3::ZERO, 1.0)
}
#[test]
fn retained_frames_borrow_the_same_arc_scene_across_frames() {
let retained = Arc::new(Scene3D::new());
let mut app = RetainedApp { scene: Arc::clone(&retained) };
let first = scene3d_frame_submission(&mut app, 1280, 720)
.expect("retained hook must produce the first frame");
let second = scene3d_frame_submission(&mut app, 1280, 720)
.expect("retained hook must produce the second frame");
assert!(std::ptr::eq(first.scene(), retained.as_ref()));
assert!(std::ptr::eq(second.scene(), retained.as_ref()));
assert_eq!(Arc::strong_count(&retained), 4);
}
#[test]
fn owned_scene3d_frame_struct_literal_remains_compatible() {
let mut app = OwnedApp;
let submission = scene3d_frame_submission(&mut app, 1280, 720)
.expect("owned hook must remain the fallback");
assert_eq!(submission.scene().clear_color, [0.1, 0.2, 0.3, 1.0]);
}
}