/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann SPDX-License-Identifier: GPL-3.0-only This file is also available under commercial licensing terms. Please contact info@sixtyfps.io for more information. LICENSE END */ #![warn(missing_docs)] //! module for rendering the tree of items use super::graphics::{ Frame, GraphicsBackend, GraphicsWindow, RenderingCache, RenderingPrimitivesBuilder, }; use super::items::ItemRef; use crate::eventloop::ComponentWindow; use crate::item_tree::ItemVisitorResult; use cgmath::{Matrix4, SquareMatrix, Vector3}; use std::cell::{Cell, RefCell}; /// This structure must be present in items that are Rendered and contains information. /// Used by the backend. #[derive(Default, Debug)] #[repr(C)] pub struct CachedRenderingData { /// Used and modified by the backend, should be initialized to 0 by the user code pub(crate) cache_index: Cell, /// Set to false initially and when changes happen that require updating the cache pub(crate) cache_ok: Cell, } impl CachedRenderingData { pub(crate) fn ensure_up_to_date( &self, cache: &RefCell>, item: core::pin::Pin, rendering_primitives_builder: &mut Backend::RenderingPrimitivesBuilder, window: &std::rc::Rc>, ) { let update_fn = || { rendering_primitives_builder .create(item.as_ref().rendering_primitive(&ComponentWindow::new(window.clone()))) }; if self.cache_ok.get() { let index = self.cache_index.get(); let mut cache = cache.borrow_mut(); let existing_entry = cache.get_mut(index).unwrap(); if existing_entry.dependency_tracker.is_dirty() { existing_entry.primitive = existing_entry.dependency_tracker.as_ref().evaluate(update_fn) } } else { self.cache_index.set( cache .borrow_mut() .insert(crate::graphics::TrackingRenderingPrimitive::new(update_fn)), ); self.cache_ok.set(true); } } fn release(&self, cache: &RefCell>) { if self.cache_ok.get() { let index = self.cache_index.get(); cache.borrow_mut().remove(index); } } } pub(crate) fn update_item_rendering_data( item: core::pin::Pin, rendering_cache: &RefCell>, rendering_primitives_builder: &mut Backend::RenderingPrimitivesBuilder, window: &std::rc::Rc>, ) { let rendering_data = item.cached_rendering_data_offset(); rendering_data.ensure_up_to_date(rendering_cache, item, rendering_primitives_builder, window); } pub(crate) fn render_component_items( component: crate::component::ComponentRefPin, frame: &mut Backend::Frame, rendering_cache: &RenderingCache, window: &std::rc::Rc>, ) { let transform = Matrix4::identity(); let window = ComponentWindow::new(window.clone()); crate::item_tree::visit_items( component, crate::item_tree::TraversalOrder::BackToFront, |_, item, transform| { let origin = item.as_ref().geometry().origin; let transform = transform * Matrix4::from_translation(Vector3::new(origin.x, origin.y, 0.)); let cached_rendering_data = item.cached_rendering_data_offset(); if cached_rendering_data.cache_ok.get() { let primitive = &rendering_cache .get(cached_rendering_data.cache_index.get()) .unwrap() .primitive; frame.render_primitive( &primitive, &transform, item.as_ref().rendering_variables(&window), ); } ItemVisitorResult::Continue(transform) }, transform, ); } pub(crate) fn free_item_rendering_data( component: crate::component::ComponentRefPin, rendering_cache: &RefCell>, ) { crate::item_tree::visit_items( component, crate::item_tree::TraversalOrder::FrontToBack, |_, item, _| { let cached_rendering_data = item.cached_rendering_data_offset(); cached_rendering_data.release(rendering_cache); ItemVisitorResult::Continue(()) }, (), ); }