From f64df76afaf721e710368649fc6aa191bef98d4b Mon Sep 17 00:00:00 2001
From: TheRawMeatball <therawmeatball@gmail.com>
Date: Sat, 19 Feb 2022 12:00:08 +0300
Subject: [PATCH] Split the renderer

---
 Cargo.lock                                    |  13 ++
 bevy_kayak_renderer/Cargo.toml                |  12 ++
 .../src/camera/camera.rs                      |   0
 .../src/camera/mod.rs                         |   0
 .../src/camera/ortho.rs                       |   0
 bevy_kayak_renderer/src/lib.rs                |  59 +++++++
 bevy_kayak_renderer/src/render/mod.rs         |  93 ++++++++++
 .../src/render/ui_pass.rs                     |   0
 .../src/render/ui_pass_driver.rs              |   0
 bevy_kayak_renderer/src/render/unified/mod.rs |  66 +++++++
 .../src/render/unified/pipeline.rs            |   6 +-
 .../src/render/unified/shader.wgsl            |   0
 bevy_kayak_ui/Cargo.toml                      |   1 +
 bevy_kayak_ui/src/lib.rs                      |  11 +-
 .../src/render/{unified => }/font/extract.rs  |   6 +-
 .../render/{unified => }/font/font_mapping.rs |   0
 .../src/render/{unified => }/font/mod.rs      |   2 +-
 .../src/render/{unified => }/image/extract.rs |  12 +-
 .../{unified => }/image/image_manager.rs      |   0
 .../src/render/{unified => }/image/mod.rs     |   0
 bevy_kayak_ui/src/render/mod.rs               | 166 ++++++++++--------
 .../{unified => }/nine_patch/extract.rs       |   9 +-
 .../render/{unified => }/nine_patch/mod.rs    |   0
 .../src/render/{unified => }/quad/extract.rs  |  22 ++-
 .../src/render/{unified => }/quad/mod.rs      |   0
 bevy_kayak_ui/src/render/unified/mod.rs       | 146 ---------------
 examples/todo/card.rs                         |   1 +
 27 files changed, 377 insertions(+), 248 deletions(-)
 create mode 100644 bevy_kayak_renderer/Cargo.toml
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/camera/camera.rs (100%)
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/camera/mod.rs (100%)
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/camera/ortho.rs (100%)
 create mode 100644 bevy_kayak_renderer/src/lib.rs
 create mode 100644 bevy_kayak_renderer/src/render/mod.rs
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/render/ui_pass.rs (100%)
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/render/ui_pass_driver.rs (100%)
 create mode 100644 bevy_kayak_renderer/src/render/unified/mod.rs
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/render/unified/pipeline.rs (99%)
 rename {bevy_kayak_ui => bevy_kayak_renderer}/src/render/unified/shader.wgsl (100%)
 rename bevy_kayak_ui/src/render/{unified => }/font/extract.rs (97%)
 rename bevy_kayak_ui/src/render/{unified => }/font/font_mapping.rs (100%)
 rename bevy_kayak_ui/src/render/{unified => }/font/mod.rs (95%)
 rename bevy_kayak_ui/src/render/{unified => }/image/extract.rs (79%)
 rename bevy_kayak_ui/src/render/{unified => }/image/image_manager.rs (100%)
 rename bevy_kayak_ui/src/render/{unified => }/image/mod.rs (100%)
 rename bevy_kayak_ui/src/render/{unified => }/nine_patch/extract.rs (99%)
 rename bevy_kayak_ui/src/render/{unified => }/nine_patch/mod.rs (100%)
 rename bevy_kayak_ui/src/render/{unified => }/quad/extract.rs (78%)
 rename bevy_kayak_ui/src/render/{unified => }/quad/mod.rs (100%)
 delete mode 100644 bevy_kayak_ui/src/render/unified/mod.rs

diff --git a/Cargo.lock b/Cargo.lock
index 05a6c75..f042d6e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -442,11 +442,24 @@ dependencies = [
  "ndk-glue 0.5.0",
 ]
 
+[[package]]
+name = "bevy_kayak_renderer"
+version = "0.0.1"
+dependencies = [
+ "bevy",
+ "bytemuck",
+ "kayak_font",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+]
+
 [[package]]
 name = "bevy_kayak_ui"
 version = "0.0.1"
 dependencies = [
  "bevy",
+ "bevy_kayak_renderer",
  "bytemuck",
  "kayak_core",
  "kayak_font",
diff --git a/bevy_kayak_renderer/Cargo.toml b/bevy_kayak_renderer/Cargo.toml
new file mode 100644
index 0000000..2b3c221
--- /dev/null
+++ b/bevy_kayak_renderer/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "bevy_kayak_renderer"
+version = "0.0.1"
+edition = "2021"
+
+[dependencies]
+bytemuck = "1.7.2"
+bevy = { version = "0.6.0" }
+kayak_font = { path = "../kayak_font" }
+serde = "1.0"
+serde_json = "1.0"
+serde_path_to_error = "0.1"
diff --git a/bevy_kayak_ui/src/camera/camera.rs b/bevy_kayak_renderer/src/camera/camera.rs
similarity index 100%
rename from bevy_kayak_ui/src/camera/camera.rs
rename to bevy_kayak_renderer/src/camera/camera.rs
diff --git a/bevy_kayak_ui/src/camera/mod.rs b/bevy_kayak_renderer/src/camera/mod.rs
similarity index 100%
rename from bevy_kayak_ui/src/camera/mod.rs
rename to bevy_kayak_renderer/src/camera/mod.rs
diff --git a/bevy_kayak_ui/src/camera/ortho.rs b/bevy_kayak_renderer/src/camera/ortho.rs
similarity index 100%
rename from bevy_kayak_ui/src/camera/ortho.rs
rename to bevy_kayak_renderer/src/camera/ortho.rs
diff --git a/bevy_kayak_renderer/src/lib.rs b/bevy_kayak_renderer/src/lib.rs
new file mode 100644
index 0000000..7ce038a
--- /dev/null
+++ b/bevy_kayak_renderer/src/lib.rs
@@ -0,0 +1,59 @@
+use bevy::{
+    prelude::*,
+    window::{WindowCreated, WindowResized},
+};
+
+pub mod camera;
+pub mod render;
+
+pub use camera::*;
+
+#[derive(Default)]
+pub struct BevyKayakRendererPlugin;
+
+impl Plugin for BevyKayakRendererPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        app.add_system(update_window_size)
+            .init_resource::<WindowSize>()
+            .add_plugin(render::BevyKayakUIRenderPlugin)
+            .add_plugin(camera::KayakUICameraPlugin);
+    }
+}
+
+/// Tracks the bevy window size.
+#[derive(Default, Debug, Clone, Copy, PartialEq)]
+pub struct WindowSize(pub f32, pub f32);
+
+fn update_window_size(
+    mut window_resized_events: EventReader<WindowResized>,
+    mut window_created_events: EventReader<WindowCreated>,
+) {
+    let mut changed_window_ids = Vec::new();
+    // handle resize events. latest events are handled first because we only want to resize each
+    // window once
+    for event in window_resized_events.iter().rev() {
+        if changed_window_ids.contains(&event.id) {
+            continue;
+        }
+
+        changed_window_ids.push(event.id);
+    }
+
+    // handle resize events. latest events are handled first because we only want to resize each
+    // window once
+    for event in window_created_events.iter().rev() {
+        if changed_window_ids.contains(&event.id) {
+            continue;
+        }
+
+        changed_window_ids.push(event.id);
+    }
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct Corner<T> {
+    pub top_left: T,
+    pub top_right: T,
+    pub bottom_left: T,
+    pub bottom_right: T,
+}
diff --git a/bevy_kayak_renderer/src/render/mod.rs b/bevy_kayak_renderer/src/render/mod.rs
new file mode 100644
index 0000000..0a3babe
--- /dev/null
+++ b/bevy_kayak_renderer/src/render/mod.rs
@@ -0,0 +1,93 @@
+use bevy::{
+    core_pipeline::node::MAIN_PASS_DRIVER,
+    prelude::{Commands, Plugin, Res},
+    render::{
+        camera::ActiveCameras,
+        render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
+        render_phase::{DrawFunctions, RenderPhase},
+        RenderApp, RenderStage,
+    },
+};
+
+use crate::{
+    render::{
+        ui_pass::MainPassUINode, ui_pass_driver::UIPassDriverNode, unified::UnifiedRenderPlugin,
+    },
+    UICameraBundle,
+};
+
+use self::ui_pass::TransparentUI;
+
+mod ui_pass;
+mod ui_pass_driver;
+pub mod unified;
+
+pub mod node {
+    pub const UI_PASS_DEPENDENCIES: &str = "kayak_ui_pass_dependencies";
+    pub const UI_PASS_DRIVER: &str = "kayak_ui_pass_driver";
+}
+
+pub mod draw_ui_graph {
+    pub const NAME: &str = "kayak_draw_ui";
+    pub mod input {
+        pub const VIEW_ENTITY: &str = "kayak_view_entity";
+    }
+    pub mod node {
+        pub const MAIN_PASS: &str = "kayak_ui_pass";
+    }
+}
+
+pub struct BevyKayakUIRenderPlugin;
+
+impl Plugin for BevyKayakUIRenderPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        let render_app = app.sub_app_mut(RenderApp);
+        render_app
+            .init_resource::<DrawFunctions<TransparentUI>>()
+            .add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases);
+        // .add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<TransparentUI>);
+
+        let pass_node_ui = MainPassUINode::new(&mut render_app.world);
+        let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
+
+        let mut draw_ui_graph = RenderGraph::default();
+        draw_ui_graph.add_node(draw_ui_graph::node::MAIN_PASS, pass_node_ui);
+        let input_node_id = draw_ui_graph.set_input(vec![SlotInfo::new(
+            draw_ui_graph::input::VIEW_ENTITY,
+            SlotType::Entity,
+        )]);
+        draw_ui_graph
+            .add_slot_edge(
+                input_node_id,
+                draw_ui_graph::input::VIEW_ENTITY,
+                draw_ui_graph::node::MAIN_PASS,
+                MainPassUINode::IN_VIEW,
+            )
+            .unwrap();
+        graph.add_sub_graph(draw_ui_graph::NAME, draw_ui_graph);
+
+        graph.add_node(node::UI_PASS_DEPENDENCIES, EmptyNode);
+        graph.add_node(node::UI_PASS_DRIVER, UIPassDriverNode);
+        graph
+            .add_node_edge(node::UI_PASS_DEPENDENCIES, node::UI_PASS_DRIVER)
+            .unwrap();
+        graph
+            .add_node_edge(MAIN_PASS_DRIVER, node::UI_PASS_DRIVER)
+            .unwrap();
+
+        app.add_plugin(UnifiedRenderPlugin);
+    }
+}
+
+pub fn extract_core_pipeline_camera_phases(
+    mut commands: Commands,
+    active_cameras: Res<ActiveCameras>,
+) {
+    if let Some(camera_2d) = active_cameras.get(UICameraBundle::UI_CAMERA) {
+        if let Some(entity) = camera_2d.entity {
+            commands
+                .get_or_spawn(entity)
+                .insert(RenderPhase::<TransparentUI>::default());
+        }
+    }
+}
diff --git a/bevy_kayak_ui/src/render/ui_pass.rs b/bevy_kayak_renderer/src/render/ui_pass.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/ui_pass.rs
rename to bevy_kayak_renderer/src/render/ui_pass.rs
diff --git a/bevy_kayak_ui/src/render/ui_pass_driver.rs b/bevy_kayak_renderer/src/render/ui_pass_driver.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/ui_pass_driver.rs
rename to bevy_kayak_renderer/src/render/ui_pass_driver.rs
diff --git a/bevy_kayak_renderer/src/render/unified/mod.rs b/bevy_kayak_renderer/src/render/unified/mod.rs
new file mode 100644
index 0000000..c0333d5
--- /dev/null
+++ b/bevy_kayak_renderer/src/render/unified/mod.rs
@@ -0,0 +1,66 @@
+use bevy::{
+    prelude::{Assets, Commands, HandleUntyped, Plugin, Res},
+    reflect::TypeUuid,
+    render::{render_phase::DrawFunctions, render_resource::Shader, RenderApp, RenderStage},
+    window::Windows,
+};
+
+use crate::{
+    render::{
+        ui_pass::TransparentUI,
+        unified::pipeline::{DrawUI, QuadMeta, UnifiedPipeline},
+    },
+    WindowSize,
+};
+
+use self::pipeline::ImageBindGroups;
+
+pub mod pipeline;
+
+pub const UNIFIED_SHADER_HANDLE: HandleUntyped =
+    HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7604018236855288450);
+
+pub struct UnifiedRenderPlugin;
+
+impl Plugin for UnifiedRenderPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
+        let unified_shader = Shader::from_wgsl(include_str!("shader.wgsl"));
+        shaders.set_untracked(UNIFIED_SHADER_HANDLE, unified_shader);
+
+        let render_app = app.sub_app_mut(RenderApp);
+        render_app
+            .init_resource::<ImageBindGroups>()
+            .init_resource::<UnifiedPipeline>()
+            .init_resource::<QuadMeta>()
+            .add_system_to_stage(RenderStage::Extract, extract_baseline)
+            .add_system_to_stage(RenderStage::Prepare, pipeline::prepare_quads)
+            .add_system_to_stage(RenderStage::Queue, pipeline::queue_quads);
+
+        let draw_quad = DrawUI::new(&mut render_app.world);
+
+        render_app
+            .world
+            .get_resource::<DrawFunctions<TransparentUI>>()
+            .unwrap()
+            .write()
+            .add(draw_quad);
+    }
+}
+
+pub struct Dpi(f32);
+
+pub fn extract_baseline(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    window_size: Res<WindowSize>,
+) {
+    let dpi = if let Some(window) = windows.get_primary() {
+        window.scale_factor() as f32
+    } else {
+        1.0
+    };
+
+    commands.insert_resource(*window_size);
+    commands.insert_resource(Dpi(dpi));
+}
diff --git a/bevy_kayak_ui/src/render/unified/pipeline.rs b/bevy_kayak_renderer/src/render/unified/pipeline.rs
similarity index 99%
rename from bevy_kayak_ui/src/render/unified/pipeline.rs
rename to bevy_kayak_renderer/src/render/unified/pipeline.rs
index fafd3f3..0706692 100644
--- a/bevy_kayak_ui/src/render/unified/pipeline.rs
+++ b/bevy_kayak_renderer/src/render/unified/pipeline.rs
@@ -38,8 +38,8 @@ use kayak_font::{
 };
 
 use super::{Dpi, UNIFIED_SHADER_HANDLE};
-use crate::{render::ui_pass::TransparentUI, WindowSize};
-use kayak_core::styles::Corner;
+use crate::render::ui_pass::TransparentUI;
+use crate::{Corner, WindowSize};
 
 pub struct UnifiedPipeline {
     view_layout: BindGroupLayout,
@@ -300,7 +300,7 @@ impl FromWorld for UnifiedPipeline {
 
 #[derive(Debug, Bundle)]
 pub struct ExtractQuadBundle {
-    pub(crate) extracted_quad: ExtractedQuad,
+    pub extracted_quad: ExtractedQuad,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq)]
diff --git a/bevy_kayak_ui/src/render/unified/shader.wgsl b/bevy_kayak_renderer/src/render/unified/shader.wgsl
similarity index 100%
rename from bevy_kayak_ui/src/render/unified/shader.wgsl
rename to bevy_kayak_renderer/src/render/unified/shader.wgsl
diff --git a/bevy_kayak_ui/Cargo.toml b/bevy_kayak_ui/Cargo.toml
index b2244c7..1ad00aa 100644
--- a/bevy_kayak_ui/Cargo.toml
+++ b/bevy_kayak_ui/Cargo.toml
@@ -8,6 +8,7 @@ bytemuck = "1.7.2"
 bevy = { version = "0.6.0" }
 kayak_core = { path = "../kayak_core" }
 kayak_font = { path = "../kayak_font" }
+bevy_kayak_renderer = { path = "../bevy_kayak_renderer" }
 serde = "1.0"
 serde_json = "1.0"
 serde_path_to_error = "0.1"
diff --git a/bevy_kayak_ui/src/lib.rs b/bevy_kayak_ui/src/lib.rs
index 6d60521..9e8852e 100644
--- a/bevy_kayak_ui/src/lib.rs
+++ b/bevy_kayak_ui/src/lib.rs
@@ -7,17 +7,16 @@ use bevy::{
 };
 
 mod bevy_context;
-mod camera;
 mod cursor;
 mod key;
 mod render;
 
 use crate::cursor::convert_cursor_icon;
 pub use bevy_context::BevyContext;
-pub use camera::*;
+pub use bevy_kayak_renderer::camera::*;
 use kayak_core::{bind, Binding, InputEvent, MutableBound};
-pub use render::unified::font::FontMapping;
-pub use render::unified::image::ImageManager;
+pub use render::font::FontMapping;
+pub use render::image::ImageManager;
 
 #[derive(Default)]
 pub struct BevyKayakUIPlugin;
@@ -25,8 +24,8 @@ pub struct BevyKayakUIPlugin;
 impl Plugin for BevyKayakUIPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
         app.insert_resource(bind(WindowSize::default()))
-            .add_plugin(render::BevyKayakUIRenderPlugin)
-            .add_plugin(camera::KayakUICameraPlugin)
+            .add_plugin(bevy_kayak_renderer::BevyKayakRendererPlugin)
+            .add_plugin(render::BevyKayakUIExtractPlugin)
             .add_system(update_window_size)
             .add_system(process_events.exclusive_system())
             .add_system(update.exclusive_system());
diff --git a/bevy_kayak_ui/src/render/unified/font/extract.rs b/bevy_kayak_ui/src/render/font/extract.rs
similarity index 97%
rename from bevy_kayak_ui/src/render/unified/font/extract.rs
rename to bevy_kayak_ui/src/render/font/extract.rs
index 8f0fa13..23d37fa 100644
--- a/bevy_kayak_ui/src/render/unified/font/extract.rs
+++ b/bevy_kayak_ui/src/render/font/extract.rs
@@ -4,12 +4,12 @@ use bevy::{
     sprite::Rect,
 };
 use kayak_core::render_primitive::RenderPrimitive;
-use kayak_core::styles::Corner;
 use kayak_font::{Alignment, CoordinateSystem, KayakFont};
 
-use crate::{
+use crate::to_bevy_color;
+use bevy_kayak_renderer::{
     render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
-    to_bevy_color,
+    Corner,
 };
 
 use super::font_mapping::FontMapping;
diff --git a/bevy_kayak_ui/src/render/unified/font/font_mapping.rs b/bevy_kayak_ui/src/render/font/font_mapping.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/unified/font/font_mapping.rs
rename to bevy_kayak_ui/src/render/font/font_mapping.rs
diff --git a/bevy_kayak_ui/src/render/unified/font/mod.rs b/bevy_kayak_ui/src/render/font/mod.rs
similarity index 95%
rename from bevy_kayak_ui/src/render/unified/font/mod.rs
rename to bevy_kayak_ui/src/render/font/mod.rs
index 5bbe8e6..f04a26d 100644
--- a/bevy_kayak_ui/src/render/unified/font/mod.rs
+++ b/bevy_kayak_ui/src/render/font/mod.rs
@@ -17,7 +17,7 @@ mod font_mapping;
 
 use crate::BevyContext;
 
-use super::pipeline::UnifiedPipeline;
+use bevy_kayak_renderer::render::unified::pipeline::UnifiedPipeline;
 pub use extract::extract_texts;
 pub use font_mapping::*;
 
diff --git a/bevy_kayak_ui/src/render/unified/image/extract.rs b/bevy_kayak_ui/src/render/image/extract.rs
similarity index 79%
rename from bevy_kayak_ui/src/render/unified/image/extract.rs
rename to bevy_kayak_ui/src/render/image/extract.rs
index c6b6e21..c50af05 100644
--- a/bevy_kayak_ui/src/render/unified/image/extract.rs
+++ b/bevy_kayak_ui/src/render/image/extract.rs
@@ -1,9 +1,10 @@
 use bevy::{math::Vec2, prelude::Res, render::color::Color, sprite::Rect};
 use kayak_core::render_primitive::RenderPrimitive;
 
-use crate::{
+use crate::ImageManager;
+use bevy_kayak_renderer::{
     render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
-    ImageManager,
+    Corner,
 };
 
 pub fn extract_images(
@@ -33,7 +34,12 @@ pub fn extract_images(
             font_handle: None,
             quad_type: UIQuadType::Image,
             type_index: 0,
-            border_radius,
+            border_radius: Corner {
+                top_left: border_radius.top_left,
+                top_right: border_radius.top_right,
+                bottom_left: border_radius.bottom_left,
+                bottom_right: border_radius.bottom_right,
+            },
             image: image_manager
                 .get_handle(handle)
                 .and_then(|a| Some(a.clone_weak())),
diff --git a/bevy_kayak_ui/src/render/unified/image/image_manager.rs b/bevy_kayak_ui/src/render/image/image_manager.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/unified/image/image_manager.rs
rename to bevy_kayak_ui/src/render/image/image_manager.rs
diff --git a/bevy_kayak_ui/src/render/unified/image/mod.rs b/bevy_kayak_ui/src/render/image/mod.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/unified/image/mod.rs
rename to bevy_kayak_ui/src/render/image/mod.rs
diff --git a/bevy_kayak_ui/src/render/mod.rs b/bevy_kayak_ui/src/render/mod.rs
index 0a3babe..4a60b6e 100644
--- a/bevy_kayak_ui/src/render/mod.rs
+++ b/bevy_kayak_ui/src/render/mod.rs
@@ -1,93 +1,109 @@
+use crate::{BevyContext, FontMapping, ImageManager};
 use bevy::{
-    core_pipeline::node::MAIN_PASS_DRIVER,
-    prelude::{Commands, Plugin, Res},
-    render::{
-        camera::ActiveCameras,
-        render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
-        render_phase::{DrawFunctions, RenderPhase},
-        RenderApp, RenderStage,
-    },
+    math::Vec2,
+    prelude::{Assets, Commands, Plugin, Res},
+    render::{color::Color, texture::Image, RenderApp, RenderStage},
+    sprite::Rect,
+    window::Windows,
 };
-
-use crate::{
-    render::{
-        ui_pass::MainPassUINode, ui_pass_driver::UIPassDriverNode, unified::UnifiedRenderPlugin,
-    },
-    UICameraBundle,
+use bevy_kayak_renderer::{
+    render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
+    Corner,
 };
+use kayak_core::render_primitive::RenderPrimitive;
+use kayak_font::KayakFont;
 
-use self::ui_pass::TransparentUI;
+pub mod font;
+pub mod image;
+mod nine_patch;
+mod quad;
 
-mod ui_pass;
-mod ui_pass_driver;
-pub mod unified;
+pub struct BevyKayakUIExtractPlugin;
 
-pub mod node {
-    pub const UI_PASS_DEPENDENCIES: &str = "kayak_ui_pass_dependencies";
-    pub const UI_PASS_DRIVER: &str = "kayak_ui_pass_driver";
-}
+impl Plugin for BevyKayakUIExtractPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        app.add_plugin(font::TextRendererPlugin)
+            .add_plugin(image::ImageRendererPlugin);
 
-pub mod draw_ui_graph {
-    pub const NAME: &str = "kayak_draw_ui";
-    pub mod input {
-        pub const VIEW_ENTITY: &str = "kayak_view_entity";
-    }
-    pub mod node {
-        pub const MAIN_PASS: &str = "kayak_ui_pass";
+        let render_app = app.sub_app_mut(RenderApp);
+        render_app.add_system_to_stage(RenderStage::Extract, extract);
     }
 }
 
-pub struct BevyKayakUIRenderPlugin;
-
-impl Plugin for BevyKayakUIRenderPlugin {
-    fn build(&self, app: &mut bevy::prelude::App) {
-        let render_app = app.sub_app_mut(RenderApp);
-        render_app
-            .init_resource::<DrawFunctions<TransparentUI>>()
-            .add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases);
-        // .add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<TransparentUI>);
+pub fn extract(
+    mut commands: Commands,
+    context: Option<Res<BevyContext>>,
+    fonts: Res<Assets<KayakFont>>,
+    font_mapping: Res<FontMapping>,
+    image_manager: Res<ImageManager>,
+    images: Res<Assets<Image>>,
+    windows: Res<Windows>,
+) {
+    if context.is_none() {
+        return;
+    }
 
-        let pass_node_ui = MainPassUINode::new(&mut render_app.world);
-        let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
+    let context = context.unwrap();
 
-        let mut draw_ui_graph = RenderGraph::default();
-        draw_ui_graph.add_node(draw_ui_graph::node::MAIN_PASS, pass_node_ui);
-        let input_node_id = draw_ui_graph.set_input(vec![SlotInfo::new(
-            draw_ui_graph::input::VIEW_ENTITY,
-            SlotType::Entity,
-        )]);
-        draw_ui_graph
-            .add_slot_edge(
-                input_node_id,
-                draw_ui_graph::input::VIEW_ENTITY,
-                draw_ui_graph::node::MAIN_PASS,
-                MainPassUINode::IN_VIEW,
-            )
-            .unwrap();
-        graph.add_sub_graph(draw_ui_graph::NAME, draw_ui_graph);
+    let render_primitives = if let Ok(context) = context.kayak_context.read() {
+        context.widget_manager.build_render_primitives()
+    } else {
+        vec![]
+    };
 
-        graph.add_node(node::UI_PASS_DEPENDENCIES, EmptyNode);
-        graph.add_node(node::UI_PASS_DRIVER, UIPassDriverNode);
-        graph
-            .add_node_edge(node::UI_PASS_DEPENDENCIES, node::UI_PASS_DRIVER)
-            .unwrap();
-        graph
-            .add_node_edge(MAIN_PASS_DRIVER, node::UI_PASS_DRIVER)
-            .unwrap();
+    // dbg!(&render_primitives);
 
-        app.add_plugin(UnifiedRenderPlugin);
-    }
-}
+    let dpi = if let Some(window) = windows.get_primary() {
+        window.scale_factor() as f32
+    } else {
+        1.0
+    };
 
-pub fn extract_core_pipeline_camera_phases(
-    mut commands: Commands,
-    active_cameras: Res<ActiveCameras>,
-) {
-    if let Some(camera_2d) = active_cameras.get(UICameraBundle::UI_CAMERA) {
-        if let Some(entity) = camera_2d.entity {
-            commands
-                .get_or_spawn(entity)
-                .insert(RenderPhase::<TransparentUI>::default());
+    let mut extracted_quads = Vec::new();
+    for render_primitive in render_primitives {
+        match render_primitive {
+            RenderPrimitive::Text { .. } => {
+                let text_quads = font::extract_texts(&render_primitive, &fonts, &font_mapping, dpi);
+                extracted_quads.extend(text_quads);
+            }
+            RenderPrimitive::Image { .. } => {
+                let image_quads = image::extract_images(&render_primitive, &image_manager, dpi);
+                extracted_quads.extend(image_quads);
+            }
+            RenderPrimitive::Quad { .. } => {
+                let quad_quads = quad::extract_quads(&render_primitive, 1.0);
+                extracted_quads.extend(quad_quads);
+            }
+            RenderPrimitive::NinePatch { .. } => {
+                let nine_patch_quads =
+                    nine_patch::extract_nine_patch(&render_primitive, &image_manager, &images, dpi);
+                extracted_quads.extend(nine_patch_quads);
+            }
+            RenderPrimitive::Clip { layout } => {
+                extracted_quads.push(ExtractQuadBundle {
+                    extracted_quad: ExtractedQuad {
+                        rect: Rect {
+                            min: Vec2::new(layout.posx, layout.posy) * dpi,
+                            max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height)
+                                * dpi,
+                        },
+                        color: Color::default(),
+                        vertex_index: 0,
+                        char_id: 0,
+                        z_index: layout.z_index,
+                        font_handle: None,
+                        quad_type: UIQuadType::Clip,
+                        type_index: 0,
+                        border_radius: Corner::default(),
+                        image: None,
+                        uv_min: None,
+                        uv_max: None,
+                    },
+                });
+            }
+            _ => {}
         }
     }
+
+    commands.spawn_batch(extracted_quads);
 }
diff --git a/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs b/bevy_kayak_ui/src/render/nine_patch/extract.rs
similarity index 99%
rename from bevy_kayak_ui/src/render/unified/nine_patch/extract.rs
rename to bevy_kayak_ui/src/render/nine_patch/extract.rs
index 239a20d..1ed3782 100644
--- a/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs
+++ b/bevy_kayak_ui/src/render/nine_patch/extract.rs
@@ -1,16 +1,15 @@
+use crate::ImageManager;
 use bevy::{
     math::Vec2,
     prelude::{Assets, Res},
     render::{color::Color, texture::Image},
     sprite::Rect,
 };
-use kayak_core::render_primitive::RenderPrimitive;
-use kayak_core::styles::Corner;
-
-use crate::{
+use bevy_kayak_renderer::{
     render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
-    ImageManager,
+    Corner,
 };
+use kayak_core::render_primitive::RenderPrimitive;
 
 pub fn extract_nine_patch(
     render_primitive: &RenderPrimitive,
diff --git a/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs b/bevy_kayak_ui/src/render/nine_patch/mod.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/unified/nine_patch/mod.rs
rename to bevy_kayak_ui/src/render/nine_patch/mod.rs
diff --git a/bevy_kayak_ui/src/render/unified/quad/extract.rs b/bevy_kayak_ui/src/render/quad/extract.rs
similarity index 78%
rename from bevy_kayak_ui/src/render/unified/quad/extract.rs
rename to bevy_kayak_ui/src/render/quad/extract.rs
index b98c8e9..52b2e8a 100644
--- a/bevy_kayak_ui/src/render/unified/quad/extract.rs
+++ b/bevy_kayak_ui/src/render/quad/extract.rs
@@ -1,10 +1,10 @@
+use crate::to_bevy_color;
 use bevy::{math::Vec2, sprite::Rect};
-use kayak_core::render_primitive::RenderPrimitive;
-
-use crate::{
+use bevy_kayak_renderer::{
     render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
-    to_bevy_color,
+    Corner,
 };
+use kayak_core::render_primitive::RenderPrimitive;
 
 pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<ExtractQuadBundle> {
     let (background_color, border_color, layout, border_radius, mut border) = match render_primitive
@@ -45,7 +45,12 @@ pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<Extrac
                 font_handle: None,
                 quad_type: UIQuadType::Quad,
                 type_index: 0,
-                border_radius,
+                border_radius: Corner {
+                    top_left: border_radius.top_left,
+                    top_right: border_radius.top_right,
+                    bottom_left: border_radius.bottom_left,
+                    bottom_right: border_radius.bottom_right,
+                },
                 image: None,
                 uv_max: None,
                 uv_min: None,
@@ -67,7 +72,12 @@ pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<Extrac
                 font_handle: None,
                 quad_type: UIQuadType::Quad,
                 type_index: 0,
-                border_radius,
+                border_radius: Corner {
+                    top_left: border_radius.top_left,
+                    top_right: border_radius.top_right,
+                    bottom_left: border_radius.bottom_left,
+                    bottom_right: border_radius.bottom_right,
+                },
                 image: None,
                 uv_max: None,
                 uv_min: None,
diff --git a/bevy_kayak_ui/src/render/unified/quad/mod.rs b/bevy_kayak_ui/src/render/quad/mod.rs
similarity index 100%
rename from bevy_kayak_ui/src/render/unified/quad/mod.rs
rename to bevy_kayak_ui/src/render/quad/mod.rs
diff --git a/bevy_kayak_ui/src/render/unified/mod.rs b/bevy_kayak_ui/src/render/unified/mod.rs
deleted file mode 100644
index 1616285..0000000
--- a/bevy_kayak_ui/src/render/unified/mod.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-use bevy::{
-    math::Vec2,
-    prelude::{Assets, Commands, HandleUntyped, Plugin, Res},
-    reflect::TypeUuid,
-    render::{
-        color::Color, render_phase::DrawFunctions, render_resource::Shader, texture::Image,
-        RenderApp, RenderStage,
-    },
-    sprite::Rect,
-    window::Windows,
-};
-use kayak_core::{render_primitive::RenderPrimitive, styles::Corner, Binding, Bound};
-use kayak_font::KayakFont;
-
-use crate::{
-    render::{
-        ui_pass::TransparentUI,
-        unified::pipeline::{DrawUI, QuadMeta, UnifiedPipeline},
-    },
-    BevyContext, FontMapping, ImageManager, WindowSize,
-};
-
-use self::pipeline::{ExtractQuadBundle, ExtractedQuad, ImageBindGroups, UIQuadType};
-
-pub mod font;
-pub mod image;
-mod nine_patch;
-mod pipeline;
-mod quad;
-
-pub const UNIFIED_SHADER_HANDLE: HandleUntyped =
-    HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7604018236855288450);
-
-pub struct UnifiedRenderPlugin;
-
-impl Plugin for UnifiedRenderPlugin {
-    fn build(&self, app: &mut bevy::prelude::App) {
-        let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
-        let unified_shader = Shader::from_wgsl(include_str!("shader.wgsl"));
-        shaders.set_untracked(UNIFIED_SHADER_HANDLE, unified_shader);
-
-        app.add_plugin(font::TextRendererPlugin)
-            .add_plugin(image::ImageRendererPlugin);
-
-        let render_app = app.sub_app_mut(RenderApp);
-        render_app
-            .init_resource::<ImageBindGroups>()
-            .init_resource::<UnifiedPipeline>()
-            .init_resource::<QuadMeta>()
-            .add_system_to_stage(RenderStage::Extract, extract)
-            .add_system_to_stage(RenderStage::Prepare, pipeline::prepare_quads)
-            .add_system_to_stage(RenderStage::Queue, pipeline::queue_quads);
-
-        let draw_quad = DrawUI::new(&mut render_app.world);
-
-        render_app
-            .world
-            .get_resource::<DrawFunctions<TransparentUI>>()
-            .unwrap()
-            .write()
-            .add(draw_quad);
-    }
-}
-
-pub struct Dpi(f32);
-
-pub fn extract(
-    mut commands: Commands,
-    context: Option<Res<BevyContext>>,
-    fonts: Res<Assets<KayakFont>>,
-    font_mapping: Res<FontMapping>,
-    image_manager: Res<ImageManager>,
-    images: Res<Assets<Image>>,
-    windows: Res<Windows>,
-    window_size: Res<Binding<WindowSize>>,
-) {
-    if context.is_none() {
-        return;
-    }
-
-    let context = context.unwrap();
-
-    let render_primitives = if let Ok(context) = context.kayak_context.read() {
-        context.widget_manager.build_render_primitives()
-    } else {
-        vec![]
-    };
-
-    // dbg!(&render_primitives);
-
-    let dpi = if let Some(window) = windows.get_primary() {
-        window.scale_factor() as f32
-    } else {
-        1.0
-    };
-
-    let mut extracted_quads = Vec::new();
-    for render_primitive in render_primitives {
-        match render_primitive {
-            RenderPrimitive::Text { .. } => {
-                let text_quads = font::extract_texts(&render_primitive, &fonts, &font_mapping, dpi);
-                extracted_quads.extend(text_quads);
-            }
-            RenderPrimitive::Image { .. } => {
-                let image_quads = image::extract_images(&render_primitive, &image_manager, dpi);
-                extracted_quads.extend(image_quads);
-            }
-            RenderPrimitive::Quad { .. } => {
-                let quad_quads = quad::extract_quads(&render_primitive, 1.0);
-                extracted_quads.extend(quad_quads);
-            }
-            RenderPrimitive::NinePatch { .. } => {
-                let nine_patch_quads =
-                    nine_patch::extract_nine_patch(&render_primitive, &image_manager, &images, dpi);
-                extracted_quads.extend(nine_patch_quads);
-            }
-            RenderPrimitive::Clip { layout } => {
-                extracted_quads.push(ExtractQuadBundle {
-                    extracted_quad: ExtractedQuad {
-                        rect: Rect {
-                            min: Vec2::new(layout.posx, layout.posy) * dpi,
-                            max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height)
-                                * dpi,
-                        },
-                        color: Color::default(),
-                        vertex_index: 0,
-                        char_id: 0,
-                        z_index: layout.z_index,
-                        font_handle: None,
-                        quad_type: UIQuadType::Clip,
-                        type_index: 0,
-                        border_radius: Corner::default(),
-                        image: None,
-                        uv_min: None,
-                        uv_max: None,
-                    },
-                });
-            }
-            _ => {}
-        }
-    }
-
-    commands.insert_resource(window_size.get());
-    commands.insert_resource(Dpi(dpi));
-    commands.spawn_batch(extracted_quads);
-}
diff --git a/examples/todo/card.rs b/examples/todo/card.rs
index 4b1bca6..52f5b9b 100644
--- a/examples/todo/card.rs
+++ b/examples/todo/card.rs
@@ -1,3 +1,4 @@
+use kayak_core::styles::Edge;
 use kayak_ui::core::{
     rsx,
     styles::{LayoutType, Style, StyleProp, Units},
-- 
GitLab