From e9a55fc094bd6a6249df7aadaf40225af0e02931 Mon Sep 17 00:00:00 2001
From: John Mitchell <6656977+StarArawn@users.noreply.github.com>
Date: Wed, 14 Dec 2022 16:18:38 -0500
Subject: [PATCH] Working on fixes to how context is managed.

---
 src/context.rs                 | 35 +++++++++------
 src/lib.rs                     |  1 +
 src/render/extract.rs          | 80 ++++++++++++++++++++++++++++------
 src/render/mod.rs              |  4 +-
 src/render/ui_pass.rs          | 34 +++++++++++----
 src/render/unified/pipeline.rs | 52 +++++++++-------------
 src/widget_context.rs          | 15 +++++++
 7 files changed, 150 insertions(+), 71 deletions(-)

diff --git a/src/context.rs b/src/context.rs
index ac9b8b8..653b035 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -90,17 +90,18 @@ pub struct KayakRootContext {
     pub(crate) order_tree: Arc<RwLock<Tree>>,
     pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>,
     pub(crate) uninitilized_systems: HashSet<String>,
+    pub camera_entity: Entity,
 }
 
 impl Default for KayakRootContext {
     fn default() -> Self {
-        Self::new()
+        Self::new(Entity::from_raw(0))
     }
 }
 
 impl KayakRootContext {
     /// Creates a new widget context.
-    pub fn new() -> Self {
+    pub fn new(camera_entity: Entity) -> Self {
         Self {
             tree: Arc::new(RwLock::new(Tree::default())),
             layout_cache: Arc::new(RwLock::new(LayoutCache::default())),
@@ -115,6 +116,7 @@ impl KayakRootContext {
             index: Default::default(),
             order_tree: Default::default(),
             uninitilized_systems: Default::default(),
+            camera_entity,
         }
     }
 
@@ -483,7 +485,7 @@ fn update_widgets_sys(world: &mut World) {
         world,
     );
 
-    for (camera_entity, mut context) in context_data.drain(..) {
+    for (entity, mut context) in context_data.drain(..) {
         for system_id in context.uninitilized_systems.drain() {
             if let Some(system) = context.systems.get_mut(&system_id) {
                 system.0.initialize(world);
@@ -516,7 +518,7 @@ fn update_widgets_sys(world: &mut World) {
 
         // dbg!("Updating widgets!");
         update_widgets(
-            camera_entity,
+            context.camera_entity,
             world,
             &context.tree,
             &context.layout_cache,
@@ -564,7 +566,7 @@ fn update_widgets_sys(world: &mut World) {
             indices.clear();
         }
 
-        world.entity_mut(camera_entity).insert(context);
+        world.entity_mut(entity).insert(context);
     }
 }
 
@@ -586,14 +588,14 @@ fn update_widgets(
 ) {
     for entity in widgets.iter() {
         // A small hack to add parents to widgets
-        let mut command_queue = CommandQueue::default();
-        {
-            let mut commands = Commands::new(&mut command_queue, &world);
-            if let Some(mut entity_commands) = commands.get_entity(entity.0) {
-                entity_commands.set_parent(camera_entity);
-            }
-        }
-        command_queue.apply(world);
+        // let mut command_queue = CommandQueue::default();
+        // {
+        //     let mut commands = Commands::new(&mut command_queue, &world);
+        //     if let Some(mut entity_commands) = commands.get_entity(entity.0) {
+        //         entity_commands.set_parent(camera_entity);
+        //     }
+        // }
+        // command_queue.apply(world);
 
         if let Some(entity_ref) = world.get_entity(entity.0) {
             if let Some(widget_type) = entity_ref.get::<WidgetName>() {
@@ -808,6 +810,7 @@ fn update_widget(
         let new_tick = widget_update_system.get_last_change_tick();
         new_ticks.insert(widget_type.clone(), new_tick);
         widget_update_system.set_last_change_tick(old_tick);
+        widget_update_system.apply_buffers(world);
 
         if should_rerender {
             if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() {
@@ -912,7 +915,7 @@ fn update_widget(
     // Mark node as needing a recalculation of rendering/layout.
     commands.entity(entity.0).insert(DirtyNode);
 
-    for (_index, changed_entity, _parent, changes) in diff.changes.iter() {
+    for (_index, changed_entity, parent, changes) in diff.changes.iter() {
         if changes.iter().any(|change| *change == Change::Deleted) {
             // commands.entity(changed_entity.0).despawn();
             // commands.entity(changed_entity.0).remove::<DirtyNode>();
@@ -921,6 +924,10 @@ fn update_widget(
         if changes.iter().any(|change| *change == Change::Inserted) {
             if let Some(mut entity_commands) = commands.get_entity(changed_entity.0) {
                 entity_commands.insert(Mounted);
+                entity_commands.set_parent(parent.0);
+            }
+            if let Some(mut parent_commands) = commands.get_entity(parent.0) {
+                parent_commands.add_child(changed_entity.0);
             }
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index d6e61f6..3f478f4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -62,6 +62,7 @@ pub mod prelude {
     pub use crate::widgets;
     pub use kayak_font::Alignment;
     pub use kayak_ui_macros::{constructor, rsx};
+    pub use crate::render::draw_ui_graph;
 }
 
 pub use focus_tree::Focusable;
diff --git a/src/render/extract.rs b/src/render/extract.rs
index e0a4ded..3b2a2f6 100644
--- a/src/render/extract.rs
+++ b/src/render/extract.rs
@@ -2,12 +2,12 @@ use crate::{
     context::{KayakRootContext, WidgetName},
     node::Node,
     render_primitive::RenderPrimitive,
-    styles::Corner,
+    styles::Corner, CameraUIKayak,
 };
 use bevy::{
-    prelude::{Assets, Camera, Color, Commands, Entity, Image, Plugin, Query, Rect, Res, Vec2},
-    render::{Extract, RenderApp, RenderStage},
-    window::Windows,
+    prelude::*,
+    render::{Extract, RenderApp, RenderStage, view::ExtractedView, render_phase::RenderPhase},
+    window::Windows, ui::TransparentUi,
 };
 use kayak_font::KayakFont;
 
@@ -26,33 +26,40 @@ impl Plugin for BevyKayakUIExtractPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
         let render_app = app.sub_app_mut(RenderApp);
         render_app.add_system_to_stage(RenderStage::Extract, extract);
+        render_app.add_system_to_stage(RenderStage::Extract, extract_default_ui_camera_view::<Camera2d>);
+        render_app.add_system_to_stage(RenderStage::Extract, extract_default_ui_camera_view::<Camera3d>);
     }
 }
 
 pub fn extract(
     mut commands: Commands,
-    context_query: Extract<Query<(Entity, &KayakRootContext, &Camera)>>,
+    context_query: Extract<Query<(Entity, &KayakRootContext)>>,
     fonts: Extract<Res<Assets<KayakFont>>>,
     font_mapping: Extract<Res<FontMapping>>,
     node_query: Extract<Query<&Node>>,
     widget_names: Extract<Query<&WidgetName>>,
     images: Extract<Res<Assets<Image>>>,
     windows: Extract<Res<Windows>>,
+    cameras: Extract<Query<&Camera>>,
 ) {
     let mut render_primitives = Vec::new();
-    for (entity, context, camera) in context_query.iter() {
-        let dpi = match &camera.target {
-            bevy::render::camera::RenderTarget::Window(window_id) => {
-                if let Some(window) = windows.get(*window_id) {
-                    window.scale_factor() as f32
-                } else {
-                    1.0
+    for (_, context) in context_query.iter() {
+        let dpi = if let Ok(camera) = cameras.get(context.camera_entity) {
+            match &camera.target {
+                bevy::render::camera::RenderTarget::Window(window_id) => {
+                    if let Some(window) = windows.get(*window_id) {
+                        window.scale_factor() as f32
+                    } else {
+                        1.0
+                    }
                 }
+                _ => 1.0,
             }
-            _ => 1.0,
+        } else {
+            1.0
         };
         let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names);
-        render_primitives.extend(new_render_primitives.drain(..).map(|r| (entity, dpi, r)));
+        render_primitives.extend(new_render_primitives.drain(..).map(|r| (context.camera_entity, dpi, r)));
     }
 
     let mut extracted_quads = Vec::new();
@@ -120,3 +127,48 @@ pub fn extract(
     // dbg!(&extracted_quads);
     commands.spawn_batch(extracted_quads);
 }
+
+#[derive(Component)]
+pub struct DefaultCameraView(pub Entity);
+
+
+const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1;
+
+pub fn extract_default_ui_camera_view<T: Component>(
+    mut commands: Commands,
+    query: Extract<Query<(Entity, &Camera, &CameraUIKayak), With<T>>>,
+) {
+    for (entity, camera, camera_ui) in &query {
+
+        if let (Some(logical_size), Some((physical_origin, _)), Some(physical_size)) = (
+            camera.logical_viewport_size(),
+            camera.physical_viewport_rect(),
+            camera.physical_viewport_size(),
+        ) {
+            // use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection
+            let projection_matrix =
+                Mat4::orthographic_rh(0.0, logical_size.x, logical_size.y, 0.0, 0.0, 1000.0);
+            let default_camera_view = commands
+                .spawn(ExtractedView {
+                    projection: projection_matrix,
+                    transform: GlobalTransform::from_xyz(
+                        0.0,
+                        0.0,
+                        1000.0 + UI_CAMERA_TRANSFORM_OFFSET,
+                    ),
+                    hdr: camera.hdr,
+                    viewport: UVec4::new(
+                        physical_origin.x,
+                        physical_origin.y,
+                        physical_size.x,
+                        physical_size.y,
+                    ),
+                })
+                .id();
+            commands.get_or_spawn(entity).insert((
+                DefaultCameraView(default_camera_view),
+                RenderPhase::<TransparentUi>::default(),
+            ));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/render/mod.rs b/src/render/mod.rs
index 67dbf1c..422a2c0 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -94,7 +94,7 @@ impl Plugin for BevyKayakUIRenderPlugin {
                 .unwrap();
             graph_2d
                 .add_node_edge(
-                    bevy::core_pipeline::core_2d::graph::node::TONEMAPPING,
+                    bevy::core_pipeline::core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
                     draw_ui_graph::node::MAIN_PASS,
                 )
                 .unwrap();
@@ -120,7 +120,7 @@ impl Plugin for BevyKayakUIRenderPlugin {
                 .unwrap();
             graph_3d
                 .add_node_edge(
-                    bevy::core_pipeline::core_3d::graph::node::TONEMAPPING,
+                    bevy::core_pipeline::core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
                     draw_ui_graph::node::MAIN_PASS,
                 )
                 .unwrap();
diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs
index 70ed333..d9310bf 100644
--- a/src/render/ui_pass.rs
+++ b/src/render/ui_pass.rs
@@ -14,6 +14,8 @@ use bevy::utils::FloatOrd;
 
 use crate::CameraUIKayak;
 
+use super::extract::DefaultCameraView;
+
 pub struct TransparentUI {
     pub sort_key: FloatOrd,
     pub entity: Entity,
@@ -44,6 +46,7 @@ pub struct MainPassUINode {
         ),
         With<ExtractedView>,
     >,
+    default_camera_view_query: QueryState<&'static DefaultCameraView>,
 }
 
 impl MainPassUINode {
@@ -52,6 +55,7 @@ impl MainPassUINode {
     pub fn new(world: &mut World) -> Self {
         Self {
             query: world.query_filtered(),
+            default_camera_view_query: world.query(),
         }
     }
 }
@@ -63,6 +67,7 @@ impl Node for MainPassUINode {
 
     fn update(&mut self, world: &mut World) {
         self.query.update_archetypes(world);
+        self.default_camera_view_query.update_archetypes(world);
     }
 
     fn run(
@@ -71,26 +76,37 @@ impl Node for MainPassUINode {
         render_context: &mut RenderContext,
         world: &World,
     ) -> Result<(), NodeRunError> {
-        let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
+        let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?;
         // adapted from bevy itself;
         // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6>
-        let (transparent_phase, target, camera_ui) = match self.query.get_manual(world, view_entity)
+        let (transparent_phase, target, _camera_ui) = match self.query.get_manual(world, input_view_entity)
         {
             Ok(it) => it,
             _ => return Ok(()),
         };
+
+        let view_entity = if let Ok(default_view) = self
+            .default_camera_view_query
+            .get_manual(world, input_view_entity)
+        {
+            default_view.0
+        } else {
+            input_view_entity
+        };
+
         // let clear_color = world.get_resource::<ClearColor>().unwrap();
         {
             let pass_descriptor = RenderPassDescriptor {
                 label: Some("main_transparent_pass_UI"),
                 color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
-                    load: match camera_ui.clear_color {
-                        ClearColorConfig::Default => {
-                            LoadOp::Clear(world.resource::<ClearColor>().0.into())
-                        }
-                        ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
-                        ClearColorConfig::None => LoadOp::Load,
-                    },
+                    // load: match camera_ui.clear_color {
+                    //     ClearColorConfig::Default => {
+                    //         LoadOp::Clear(world.resource::<ClearColor>().0.into())
+                    //     }
+                    //     ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
+                    //     ClearColorConfig::None => LoadOp::Load,
+                    // },
+                    load: LoadOp::Load,
                     store: true,
                 }))],
                 depth_stencil_attachment: None,
diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs
index 4d5aa33..5d2cfc9 100644
--- a/src/render/unified/pipeline.rs
+++ b/src/render/unified/pipeline.rs
@@ -2,6 +2,7 @@ use bevy::prelude::{Msaa, Rect, Resource};
 use bevy::render::render_resource::{
     DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
 };
+use bevy::render::view::{ViewTarget, ExtractedView};
 use bevy::utils::FloatOrd;
 use bevy::{
     ecs::system::{
@@ -67,27 +68,10 @@ impl FontRenderingPipeline for UnifiedPipeline {
     }
 }
 
-bitflags::bitflags! {
-    #[repr(transparent)]
-    pub struct UnifiedPipelineKey: u32 {
-        const NONE                        = 0;
-        const MSAA_RESERVED_BITS          = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
-    }
-}
-
-impl UnifiedPipelineKey {
-    const MSAA_MASK_BITS: u32 = 0b111;
-    const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
-
-    pub fn from_msaa_samples(msaa_samples: u32) -> Self {
-        let msaa_bits =
-            (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
-        Self::from_bits(msaa_bits).unwrap()
-    }
-
-    pub fn msaa_samples(&self) -> u32 {
-        1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
-    }
+#[derive(Debug, Component, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct UnifiedPipelineKey {
+    pub msaa: u32,
+    pub hdr: bool,
 }
 
 impl FromWorld for UnifiedPipeline {
@@ -242,7 +226,7 @@ impl FromWorld for UnifiedPipeline {
 impl SpecializedRenderPipeline for UnifiedPipeline {
     type Key = UnifiedPipelineKey;
 
-    fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor {
+    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
         let vertex_buffer_layout = VertexBufferLayout {
             array_stride: 60,
             step_mode: VertexStepMode::Vertex,
@@ -282,7 +266,11 @@ impl SpecializedRenderPipeline for UnifiedPipeline {
                 shader_defs: vec![],
                 entry_point: "fragment".into(),
                 targets: vec![Some(ColorTargetState {
-                    format: TextureFormat::bevy_default(),
+                    format: if key.hdr {
+                        ViewTarget::TEXTURE_FORMAT_HDR
+                    } else {
+                        TextureFormat::bevy_default()
+                    },
                     blend: Some(BlendState {
                         color: BlendComponent {
                             src_factor: BlendFactor::SrcAlpha,
@@ -315,7 +303,7 @@ impl SpecializedRenderPipeline for UnifiedPipeline {
             },
             depth_stencil: None,
             multisample: MultisampleState {
-                count: 1,
+                count: key.msaa,
                 mask: !0,
                 alpha_to_coverage_enabled: false,
             },
@@ -534,7 +522,7 @@ pub fn queue_quads(
     mut pipelines: ResMut<SpecializedRenderPipelines<UnifiedPipeline>>,
     mut pipeline_cache: ResMut<PipelineCache>,
     mut extracted_sprites: Query<(Entity, &ExtractedQuad)>,
-    mut views: Query<(Entity, &mut RenderPhase<TransparentUI>)>,
+    mut views: Query<(Entity, &mut RenderPhase<TransparentUI>, &ExtractedView)>,
     mut image_bind_groups: ResMut<ImageBindGroups>,
     unified_pipeline: Res<UnifiedPipeline>,
     gpu_images: Res<RenderAssets<Image>>,
@@ -562,15 +550,9 @@ pub fn queue_quads(
             layout: &quad_pipeline.view_layout,
         }));
 
-        let key = UnifiedPipelineKey::from_msaa_samples(msaa.samples);
-        let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key);
-
         let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
-        for (camera_entity, mut transparent_phase) in views.iter_mut() {
+        for (camera_entity, mut transparent_phase, view) in views.iter_mut() {
             for (entity, quad) in extracted_sprites.iter_mut() {
-                if quad.camera_entity != camera_entity {
-                    continue;
-                }
                 if let Some(image_handle) = quad.image.as_ref() {
                     if let Some(gpu_image) = gpu_images.get(image_handle) {
                         image_bind_groups
@@ -596,6 +578,12 @@ pub fn queue_quads(
                             });
                     }
                 }
+                let key = UnifiedPipelineKey {
+                    msaa: 1,
+                    hdr: view.hdr,
+                };
+                let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key);
+
                 transparent_phase.add(TransparentUI {
                     draw_function: draw_quad,
                     pipeline: spec_pipeline,
diff --git a/src/widget_context.rs b/src/widget_context.rs
index c6e64c7..5f5b95e 100644
--- a/src/widget_context.rs
+++ b/src/widget_context.rs
@@ -225,6 +225,21 @@ impl KayakWidgetContext {
         }
     }
 
+    // Despawns a widget entity and it's decedents. This is done in a safe way by keeping entity id's around.
+    pub fn despawn_safe(&self, commands: &mut Commands, entity: Entity) {
+        if let Ok(mut tree) = self.old_tree.write() {
+            let mut down_iter = tree.down_iter();
+            down_iter.current_node = Some(WrappedIndex(entity));
+            for child in down_iter {
+                commands.entity(child.0).despawn();
+                commands.get_or_spawn(child.0);
+            }
+            commands.entity(entity).despawn();
+            commands.get_or_spawn(entity);
+            tree.remove(WrappedIndex(entity));
+        }
+    }
+
     /// Attempts to get the layout rect for the widget with the given ID
     ///
     /// # Arguments
-- 
GitLab