From 6fe22bcdc6f0d3b2670998fe0bc274e6d78fd975 Mon Sep 17 00:00:00 2001 From: Samuel Selleck Date: Thu, 31 Oct 2024 11:21:57 -0700 Subject: [PATCH 1/2] mid refactor - primitives, transform, gradient hooked up --- .../src/core_graphics_c_bridge.rs | 1 + pax-chassis-web/Cargo.toml | 2 +- .../render_backend => shaders}/geometry.wgsl | 68 +-- .../render_backend => shaders}/stencil.wgsl | 0 .../render_backend => shaders}/textures.wgsl | 0 pax-pixels/src/render_backend/data.rs | 2 +- pax-pixels/src/render_backend/mod.rs | 513 +++--------------- pax-pixels/src/render_backend/web_backend.rs | 1 - pax-pixels/src/render_context.rs | 338 ++++++------ .../src/render_context/mesh_renderer.rs | 292 ++++++++++ .../stencil_renderer.rs} | 302 ++++++----- .../texture_renderer.rs} | 223 ++++---- pax-runtime/src/engine/mod.rs | 35 +- 13 files changed, 850 insertions(+), 927 deletions(-) rename pax-pixels/{src/render_backend => shaders}/geometry.wgsl (68%) rename pax-pixels/{src/render_backend => shaders}/stencil.wgsl (100%) rename pax-pixels/{src/render_backend => shaders}/textures.wgsl (100%) delete mode 100644 pax-pixels/src/render_backend/web_backend.rs create mode 100644 pax-pixels/src/render_context/mesh_renderer.rs rename pax-pixels/src/{render_backend/stencil.rs => render_context/stencil_renderer.rs} (50%) rename pax-pixels/src/{render_backend/texture.rs => render_context/texture_renderer.rs} (55%) diff --git a/pax-chassis-common/src/core_graphics_c_bridge.rs b/pax-chassis-common/src/core_graphics_c_bridge.rs index bfc4e9a67..9534a9b68 100644 --- a/pax-chassis-common/src/core_graphics_c_bridge.rs +++ b/pax-chassis-common/src/core_graphics_c_bridge.rs @@ -128,6 +128,7 @@ pub extern "C" fn pax_tick( ( CoreGraphicsContext::new_y_up(ctx, height as f64, None), Box::new(|| (/* clear screen here */)), + Box::new(|| (/* resize render texture backend here */)), ) }); diff --git a/pax-chassis-web/Cargo.toml b/pax-chassis-web/Cargo.toml index 0ae77e3ad..804dc54b9 100644 --- a/pax-chassis-web/Cargo.toml +++ b/pax-chassis-web/Cargo.toml @@ -13,7 +13,7 @@ include = ["/src", "/interface/public"] crate-type = ["cdylib", "rlib"] [features] -default = ["console_error_panic_hook"] +default = ["console_error_panic_hook", "gpu"] designtime = ["dep:pax-designtime", "pax-runtime/designtime"] gpu = [] diff --git a/pax-pixels/src/render_backend/geometry.wgsl b/pax-pixels/shaders/geometry.wgsl similarity index 68% rename from pax-pixels/src/render_backend/geometry.wgsl rename to pax-pixels/shaders/geometry.wgsl index 8e8e9495e..3e15cbf2e 100644 --- a/pax-pixels/src/render_backend/geometry.wgsl +++ b/pax-pixels/shaders/geometry.wgsl @@ -4,18 +4,6 @@ struct Globals { _pad2: u32, }; -struct Primitive { - fill_id_and_type: u32, - z_index: i32, - clipping_id: u32, //not used atm - transform_id: u32, -}; - -struct Primitives { - primitives: array, -}; - - struct Transform { // OBS transform: mat3x2 has different alignment on WebGL vs for // example metal (don't change this unless you know what you are doing) @@ -29,16 +17,8 @@ struct Transform { _pad2: u32, } -struct Transforms { - transforms: array, -} - - -struct Colors { - colors: array, 512>, -} - -struct Gradient { +// TODO change type to support radius/solid etc +struct MeshMetadata { colors: array, 8>, stops_set1: vec4, stops_set2: vec4, @@ -47,19 +27,14 @@ struct Gradient { off_axis: vec2, stop_count: u32, type_id: u32, - _pad: array, 4>, -} - -struct Gradients { - gradients: array, + _pad0: array, 4>, } @group(0) @binding(0) var globals: Globals; -@group(0) @binding(1) var u_primitives: Primitives; -@group(0) @binding(2) var transforms: Transforms; -@group(0) @binding(3) var colors: Colors; -@group(0) @binding(4) var gradients: Gradients; +@group(0) @binding(1) var transform: Transform; + +@group(1) @binding(0) var mesh_metadata: MeshMetadata; struct GpuVertex { @location(0) position: vec2, @@ -69,7 +44,6 @@ struct GpuVertex { struct VertexOutput { @builtin(position) clip_position: vec4, - @location(0) @interpolate(flat) prim_id: u32, }; @vertex @@ -78,22 +52,16 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; var p = model.position; - // apply transform - let primitive = u_primitives.primitives[model.prim_id]; - let m = transforms.transforms[primitive.transform_id]; - + let m = transform; let t_p_x = p.x * m.xx + p.y * m.yx + m.zx; let t_p_y = p.x * m.xy + p.y * m.yy + m.zy; - var pos = vec2(t_p_x, t_p_y); - pos /= globals.resolution; pos *= 2.0; pos -= 1.0; pos.y *= -1.0; - out.prim_id = model.prim_id; out.clip_position = vec4(pos, 0.0, 1.0); return out; } @@ -101,32 +69,28 @@ fn vs_main( // Fragment shader @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - - let primitive = u_primitives.primitives[in.prim_id]; - //color/gradient - let fill_id_and_type = primitive.fill_id_and_type; - //clipping rectangle - let fill_id = fill_id_and_type & 0xFFFFu; - let fill_type = fill_id_and_type >> 16u; + let type_id = mesh_metadata.type_id; var color: vec4; - if fill_type == 0u { - color = colors.colors[fill_id]; + if type_id == 0u { + color = mesh_metadata.colors[0]; } else { let p = in.clip_position.xy; - color = gradient(fill_id, p); + color = gradient(p); } return color; } -fn gradient(fill_id: u32, coord: vec2) -> vec4 { - let gradient = gradients.gradients[fill_id]; - +fn gradient(coord: vec2) -> vec4 { + let gradient = mesh_metadata; // Calculate color space position let g_p = gradient.position * f32(globals.dpr); let g_a = gradient.main_axis * f32(globals.dpr); let p_t = coord - g_p; + + // TODO check type id here, and calculate color_space (0.0 to 1.0) using + // distance instead of projection to do radial gradients! let m_a_l = length(g_a); let n = g_a / m_a_l; let color_space = dot(p_t, n); diff --git a/pax-pixels/src/render_backend/stencil.wgsl b/pax-pixels/shaders/stencil.wgsl similarity index 100% rename from pax-pixels/src/render_backend/stencil.wgsl rename to pax-pixels/shaders/stencil.wgsl diff --git a/pax-pixels/src/render_backend/textures.wgsl b/pax-pixels/shaders/textures.wgsl similarity index 100% rename from pax-pixels/src/render_backend/textures.wgsl rename to pax-pixels/shaders/textures.wgsl diff --git a/pax-pixels/src/render_backend/data.rs b/pax-pixels/src/render_backend/data.rs index 736c98361..f94ce0ba2 100644 --- a/pax-pixels/src/render_backend/data.rs +++ b/pax-pixels/src/render_backend/data.rs @@ -32,7 +32,7 @@ const MAX_GRADIENT_STOPS: usize = 8; #[repr(C)] #[derive(Debug, Default, Copy, Clone, Pod, Zeroable)] -pub(crate) struct GpuGradient { +pub(crate) struct GpuMeshMetadata { pub colors: [[f32; 4]; MAX_GRADIENT_STOPS], pub stops: [f32; MAX_GRADIENT_STOPS], pub position: [f32; 2], diff --git a/pax-pixels/src/render_backend/mod.rs b/pax-pixels/src/render_backend/mod.rs index 3e67b5724..37fd6e216 100644 --- a/pax-pixels/src/render_backend/mod.rs +++ b/pax-pixels/src/render_backend/mod.rs @@ -1,37 +1,24 @@ use anyhow::anyhow; -use bytemuck::Pod; use lyon::lyon_tessellation::VertexBuffers; use wgpu::{ - util::DeviceExt, BindGroup, BindGroupLayout, BufferUsages, CompositeAlphaMode, Device, - IndexFormat, PresentMode, RenderPipeline, SurfaceConfiguration, SurfaceTexture, TextureFormat, - TextureUsages, TextureView, + util::DeviceExt, BufferUsages, CompositeAlphaMode, PresentMode, SurfaceConfiguration, + SurfaceTexture, TextureFormat, TextureUsages, TextureView, }; pub mod data; mod gpu_resources; -pub mod stencil; -mod texture; -use data::{GpuGlobals, GpuPrimitive, GpuVertex}; +use data::{GpuGlobals, GpuVertex}; -use crate::{render_backend::texture::TextureRenderer, Box2D, Transform2D}; +use crate::{Fill, Transform2D}; -use self::{ - data::{GpuColor, GpuGradient, GpuTransform}, - stencil::StencilRenderer, -}; +use self::data::GpuTransform; pub struct RenderConfig { pub debug: bool, - index_buffer_size: u64, - vertex_buffer_size: u64, - primitive_buffer_size: u64, - colors_buffer_size: u64, - gradients_buffer_size: u64, - transforms_buffer_size: u64, - pub initial_width: u32, - pub initial_height: u32, + pub width: u32, + pub height: u32, pub initial_dpr: u32, } @@ -39,14 +26,8 @@ impl RenderConfig { pub fn new(_debug: bool, width: u32, height: u32, dpr: u32) -> Self { Self { debug: false, - index_buffer_size: 2 << 12, - vertex_buffer_size: 2 << 12, - primitive_buffer_size: 512, - colors_buffer_size: 512, - gradients_buffer_size: 64, - transforms_buffer_size: 64, - initial_width: width, - initial_height: height, + width, + height, initial_dpr: dpr, } } @@ -54,32 +35,19 @@ impl RenderConfig { pub struct RenderBackend<'w> { //configuration - config: RenderConfig, - pub(crate) globals: GpuGlobals, + pub config: RenderConfig, //gpu pipeline references _adapter: wgpu::Adapter, - device: wgpu::Device, - queue: wgpu::Queue, + pub device: wgpu::Device, + pub queue: wgpu::Queue, surface: wgpu::Surface<'w>, - surface_config: SurfaceConfiguration, - pipeline: RenderPipeline, - bind_group: BindGroup, - - //buffers - globals_buffer: wgpu::Buffer, - vertex_buffer: wgpu::Buffer, - index_buffer: wgpu::Buffer, - primitive_buffer: wgpu::Buffer, - transforms_buffer: wgpu::Buffer, - colors_buffer: wgpu::Buffer, - gradients_buffer: wgpu::Buffer, - - index_count: u64, + pub surface_config: SurfaceConfiguration, - // plugins / extensions - texture_renderer: TextureRenderer, - stencil_renderer: StencilRenderer, + // general buffers + pub(crate) globals: GpuGlobals, + pub globals_buffer: wgpu::Buffer, + pub transform_buffer: wgpu::Buffer, } impl<'w> RenderBackend<'w> { @@ -133,8 +101,8 @@ impl<'w> RenderBackend<'w> { let surface_config = SurfaceConfiguration { usage: TextureUsages::RENDER_ATTACHMENT, format: TEXTURE_FORMAT, - width: config.initial_width, - height: config.initial_height, + width: config.width, + height: config.height, present_mode: PresentMode::Fifo, alpha_mode: CompositeAlphaMode::PreMultiplied, view_formats: vec![TEXTURE_FORMAT], @@ -142,278 +110,56 @@ impl<'w> RenderBackend<'w> { }; surface.configure(&device, &surface_config); - fn create_buffer( - device: &Device, - name: &str, - size: u64, - usage_flags: BufferUsages, - ) -> (Vec, wgpu::Buffer) { - let data: Vec = vec![T::default(); size as usize]; - let buffer_ref = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(name), - contents: bytemuck::cast_slice(&data), - usage: usage_flags, - }); - (data, buffer_ref) - } - let globals = GpuGlobals { - resolution: [config.initial_width as f32, config.initial_height as f32], + resolution: [config.width as f32, config.height as f32], dpr: config.initial_dpr, _pad2: 0, }; - let (_, globals_buffer) = create_buffer::( - &device, - "Primitive Buffer", - 1, - BufferUsages::UNIFORM | BufferUsages::COPY_DST, - ); - - let (_, vertex_buffer) = create_buffer::( - &device, - "Vertex Buffer", - config.vertex_buffer_size, - BufferUsages::VERTEX | BufferUsages::COPY_DST, - ); - let (_, index_buffer) = create_buffer::( - &device, - "Index Buffer", - config.index_buffer_size, - BufferUsages::INDEX | BufferUsages::COPY_DST, - ); - let (_, primitive_buffer) = create_buffer::( - &device, - "Primitive Buffer", - config.primitive_buffer_size, - BufferUsages::UNIFORM | BufferUsages::COPY_DST, - ); - let (_, transforms_buffer) = create_buffer::( - &device, - "Transform Buffer", - config.transforms_buffer_size, - BufferUsages::UNIFORM | BufferUsages::COPY_DST, - ); - let (_, colors_buffer) = create_buffer::( - &device, - "Colors Buffer", - config.colors_buffer_size, - BufferUsages::UNIFORM | BufferUsages::COPY_DST, - ); - let (_, gradients_buffer) = create_buffer::( - &device, - "Gradients Buffer", - config.gradients_buffer_size, - BufferUsages::UNIFORM | BufferUsages::COPY_DST, - ); - - let primitive_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 4, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - label: Some("bind_group_layout"), - }); - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &primitive_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: primitive_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: transforms_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: colors_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 4, - resource: gradients_buffer.as_entire_binding(), - }, - ], - label: Some("bind_group"), + let globals_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Gpu Globals"), + contents: bytemuck::cast_slice(&[globals]), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, }); - let pipeline = - Self::create_pipeline(&device, surface_config.format, primitive_bind_group_layout); - - let texture_renderer = TextureRenderer::new(&device); - let stencil_renderer = StencilRenderer::new( - &device, - config.initial_width, - config.initial_height, - &globals_buffer, - ); + // global transform - can be set between render calls + let transform = GpuTransform::default(); + let transform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Gpu transform"), + contents: bytemuck::cast_slice(&[transform]), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); - let initial_width = config.initial_width; - let initial_height = config.initial_height; + let initial_width = config.width; + let initial_height = config.height; let initial_dpr = config.initial_dpr; + let mut backend = Self { - texture_renderer, - stencil_renderer, + // globally used GPU state _adapter: adapter, surface, device, queue, config, surface_config, - bind_group, - pipeline, - vertex_buffer, - index_buffer, - primitive_buffer, - transforms_buffer, globals_buffer, - colors_buffer, - gradients_buffer, globals, - index_count: 0, + transform_buffer, }; backend.globals.dpr = initial_dpr; backend.resize(initial_width, initial_height); Ok(backend) } - fn create_pipeline( - device: &Device, - format: TextureFormat, - primitive_bind_group_layout: BindGroupLayout, - ) -> RenderPipeline { - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("geometry.wgsl").into()), - }); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[&primitive_bind_group_layout], - push_constant_ranges: &[], - }); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[GpuVertex::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Stencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Equal, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::Keep, - }, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: Default::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }) - } - - pub fn push_stencil(&mut self, geometry: VertexBuffers) { - // self.stencil_renderer.clear(&self.device, &self.queue); - self.stencil_renderer - .push_stencil(&self.device, &self.queue, geometry); - } - - pub fn reset_stencil_depth_to(&mut self, depth: u32) { - self.stencil_renderer - .reset_stencil_depth_to(&self.device, &self.queue, depth); - } - - pub fn get_clip_depth(&mut self) -> u32 { - let (_, depth) = self.stencil_renderer.get_stencil(); - depth + pub fn set_transform(&mut self, transform: Transform2D) { + self.queue.write_buffer( + &self.transform_buffer, + 0, + bytemuck::cast_slice(&[GpuTransform { + transform: transform.to_arrays(), + _pad: 0, + _pad2: 0, + }]), + ); } pub fn resize(&mut self, width: u32, height: u32) { @@ -425,115 +171,10 @@ impl<'w> RenderBackend<'w> { 0, bytemuck::cast_slice(&[self.globals]), ); - self.stencil_renderer.resize(&self.device, width, height); self.surface.configure(&self.device, &self.surface_config); } - fn write_buffers(&mut self, buffers: &mut CpuBuffers) { - let CpuBuffers { - geometry: ref mut geom, - ref mut primitives, - ref mut colors, - ref mut gradients, - ref mut transforms, - } = buffers; - //Add ghost triangles to follow COPY_BUFFER_ALIGNMENT requirement - const ALIGNMENT: usize = 16; - while geom.indices.len() * std::mem::size_of::() % ALIGNMENT != 0 { - geom.indices.push(0); - geom.indices.push(0); - geom.indices.push(0); - } - while geom.vertices.len() * std::mem::size_of::() % ALIGNMENT != 0 { - geom.vertices.push(GpuVertex::default()); - } - while primitives.len() * std::mem::size_of::() % ALIGNMENT != 0 { - primitives.push(GpuPrimitive::default()); - } - while transforms.len() * std::mem::size_of::() % ALIGNMENT != 0 { - transforms.push(GpuTransform::default()); - } - while colors.len() * std::mem::size_of::() % ALIGNMENT != 0 { - colors.push(GpuColor::default()); - } - while gradients.len() * std::mem::size_of::() % ALIGNMENT != 0 { - gradients.push(GpuGradient::default()); - } - - if geom.indices.len() >= self.config.index_buffer_size as usize - || geom.vertices.len() >= self.config.vertex_buffer_size as usize - || primitives.len() >= self.config.primitive_buffer_size as usize - || transforms.len() >= self.config.transforms_buffer_size as usize - || colors.len() >= self.config.colors_buffer_size as usize - || gradients.len() >= self.config.gradients_buffer_size as usize - { - //TODO do resize here instead - log::warn!("render backend: buffer to large, skipping render"); - } - - self.queue - .write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&geom.indices)); - self.queue - .write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&geom.vertices)); - self.queue - .write_buffer(&self.primitive_buffer, 0, bytemuck::cast_slice(primitives)); - self.queue - .write_buffer(&self.colors_buffer, 0, bytemuck::cast_slice(colors)); - self.queue - .write_buffer(&self.gradients_buffer, 0, bytemuck::cast_slice(gradients)); - self.queue - .write_buffer(&self.transforms_buffer, 0, bytemuck::cast_slice(transforms)); - - self.index_count = geom.indices.len() as u64; - } - - pub(crate) fn render_primitives(&mut self, buffers: &mut CpuBuffers) { - let (screen_surface, screen_texture) = self.get_screen_texture(); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let (stencil_texture, stencil_index) = self.stencil_renderer.get_stencil(); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &screen_texture, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: stencil_texture, - depth_ops: None, - stencil_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - }), - timestamp_writes: None, - occlusion_query_set: None, - }); - self.write_buffers(buffers); - - render_pass.set_pipeline(&self.pipeline); - render_pass.set_bind_group(0, &self.bind_group, &[]); - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - render_pass.set_stencil_reference(stencil_index); //this needs to be dynamic? - render_pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint16); - render_pass.draw_indexed(0..self.index_count as u32, 0, 0..1); - } - - //render primitives - self.queue.submit(std::iter::once(encoder.finish())); - screen_surface.present(); - } - - fn get_screen_texture(&self) -> (SurfaceTexture, TextureView) { + pub fn get_screen_texture(&self) -> (SurfaceTexture, TextureView) { let screen_surface = self.surface.get_current_texture().unwrap(); let screen_texture = screen_surface .texture @@ -541,24 +182,24 @@ impl<'w> RenderBackend<'w> { (screen_surface, screen_texture) } - pub(crate) fn render_image(&mut self, image: &Image, transform: Transform2D, rect: Box2D) { - let (screen_surface, screen_texture) = self.get_screen_texture(); - self.texture_renderer.render_image( - &self.device, - &self.queue, - &screen_texture, - &self.globals_buffer, - &self.stencil_renderer, - &image.rgba, - image.pixel_width, - transform, - rect, - ); - screen_surface.present(); - } + // pub(crate) fn render_image(&mut self, image: &Image, transform: Transform2D, rect: Box2D) { + // let (screen_surface, screen_texture) = self.get_screen_texture(); + // self.texture_renderer.render_image( + // &self.device, + // &self.queue, + // &screen_texture, + // &self.globals_buffer, + // &self.stencil_renderer, + // &image.rgba, + // image.pixel_width, + // transform, + // rect, + // ); + // screen_surface.present(); + // } pub(crate) fn clear(&mut self) { - self.stencil_renderer.clear(&self.device, &self.queue); + self.set_transform(Transform2D::identity()); let (screen_surface, screen_texture) = self.get_screen_texture(); let mut encoder = self .device @@ -592,34 +233,6 @@ impl<'w> RenderBackend<'w> { } } -#[derive(Debug)] -pub(crate) struct CpuBuffers { - pub geometry: VertexBuffers, - pub primitives: Vec, - pub transforms: Vec, - pub colors: Vec, - pub gradients: Vec, -} - -impl CpuBuffers { - pub(crate) fn reset(&mut self) { - let CpuBuffers { - geometry, - primitives, - transforms, - colors, - gradients, - } = self; - geometry.vertices.clear(); - geometry.indices.clear(); - primitives.clear(); - colors.clear(); - gradients.clear(); - // leave the identity transform at the start - transforms.truncate(1); - } -} - #[derive(Clone)] pub struct Image { pub rgba: Vec, diff --git a/pax-pixels/src/render_backend/web_backend.rs b/pax-pixels/src/render_backend/web_backend.rs deleted file mode 100644 index 8b1378917..000000000 --- a/pax-pixels/src/render_backend/web_backend.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pax-pixels/src/render_context.rs b/pax-pixels/src/render_context.rs index 51f0d78fb..c247fcdaf 100644 --- a/pax-pixels/src/render_context.rs +++ b/pax-pixels/src/render_context.rs @@ -1,5 +1,3 @@ -use crate::render_backend::stencil; -use crate::render_backend::CpuBuffers; use crate::Box2D; use crate::Image; use crate::Point2D; @@ -11,236 +9,222 @@ use lyon::lyon_tessellation::FillTessellator; use lyon::lyon_tessellation::FillVertex; use lyon::lyon_tessellation::VertexBuffers; use lyon::path::Path; -use lyon::tessellation::StrokeOptions; -use lyon::tessellation::StrokeTessellator; -use lyon::tessellation::StrokeVertex; -use crate::render_backend::data::GpuColor; -use crate::render_backend::data::GpuGradient; -use crate::render_backend::data::GpuPrimitive; -use crate::render_backend::data::GpuTransform; +mod mesh_renderer; +mod stencil_renderer; +mod texture_renderer; use crate::render_backend::data::GpuVertex; use crate::render_backend::RenderBackend; +use mesh_renderer::MeshRenderer; +use stencil_renderer::StencilRenderer; +use texture_renderer::TextureRenderer; + pub struct WgpuRenderer<'w> { - buffers: CpuBuffers, - render_backend: RenderBackend<'w>, - // these reference indicies in the CpuBuffer "transforms" - transform_index_stack: Vec, - // tuble of transform stack index to go to, and clip depth to go to - saves: Vec<(usize, usize)>, + backend: RenderBackend<'w>, + pub texture_renderer: TextureRenderer, + pub stencil_renderer: StencilRenderer, + pub mesh_renderer: MeshRenderer, + encoder: wgpu::CommandEncoder, tolerance: f32, } impl<'w> WgpuRenderer<'w> { - pub fn new(render_backend: RenderBackend<'w>) -> Self { - let geometry: VertexBuffers = VertexBuffers::new(); + pub fn new(backend: RenderBackend<'w>) -> Self { + let texture_renderer = TextureRenderer::new(&backend); + let stencil_renderer = StencilRenderer::new(&backend); + let mesh_renderer = MeshRenderer::new(&backend); + let encoder = backend + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); Self { - render_backend, - buffers: CpuBuffers { - geometry, - primitives: Vec::new(), - colors: Vec::new(), - gradients: Vec::new(), - transforms: vec![GpuTransform::default()], - }, - tolerance: 0.5, //TODO expose as option - transform_index_stack: vec![], - saves: vec![], + backend, + texture_renderer, + stencil_renderer, + mesh_renderer, + encoder, + //TODO expose as option + tolerance: 0.5, } } - fn current_transform(&self) -> Transform2D { - Transform2D::from_arrays( - self.buffers - .transforms - .get(self.transform_index_stack.last().cloned().unwrap_or(0)) - .expect("at least one identity transform should exist on the transform stack") - .transform, - ) - } + // fn current_transform(&self) -> Transform2D { + // Transform2D::from_arrays( + // self.buffers + // .transforms + // .get(self.transform_index_stack.last().cloned().unwrap_or(0)) + // .expect("at least one identity transform should exist on the transform stack") + // .transform, + // ) + // } pub fn stroke_path(&mut self, path: Path, stroke_fill: Fill, stroke_width: f32) { - let prim_id = self.push_primitive_def(stroke_fill); - let options = StrokeOptions::tolerance(self.tolerance).with_line_width(stroke_width); - let mut geometry_builder = - BuffersBuilder::new(&mut self.buffers.geometry, |vertex: StrokeVertex| { - GpuVertex { - position: vertex.position().to_array(), - normal: [0.0; 2], - prim_id, - } - }); - match StrokeTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { - Ok(_) => {} - Err(e) => log::warn!("{:?}", e), - }; + // let prim_id = self.push_primitive_def(stroke_fill); + // let options = StrokeOptions::tolerance(self.tolerance).with_line_width(stroke_width); + // let mut geometry_builder = + // BuffersBuilder::new(&mut self.buffers.geometry, |vertex: StrokeVertex| { + // GpuVertex { + // position: vertex.position().to_array(), + // normal: [0.0; 2], + // prim_id, + // } + // }); + // match StrokeTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { + // Ok(_) => {} + // Err(e) => log::warn!("{:?}", e), + // }; } pub fn fill_path(&mut self, path: Path, fill: Fill) { - let prim_id = self.push_primitive_def(fill); let options = FillOptions::tolerance(self.tolerance); + let mut geometry = VertexBuffers::new(); let mut geometry_builder = - BuffersBuilder::new(&mut self.buffers.geometry, |vertex: FillVertex| GpuVertex { + BuffersBuilder::new(&mut geometry, |vertex: FillVertex| GpuVertex { position: vertex.position().to_array(), normal: [0.0; 2], - prim_id, + prim_id: 0, }); match FillTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { Ok(_) => {} Err(e) => log::warn!("{:?}", e), }; - } - fn push_primitive_def(&mut self, fill: Fill) -> u32 { - let fill_id; - let fill_type_flag; - match fill { - Fill::Solid(color) => { - fill_id = self.buffers.colors.len() as u16; - fill_type_flag = 0; - self.buffers.colors.push(GpuColor { color: color.rgba }); - } - Fill::Gradient { - gradient_type, - pos, - main_axis, - off_axis, - stops, - } => { - fill_id = self.buffers.gradients.len() as u16; - fill_type_flag = 1; - if stops.len() > 8 { - log::warn!("can't draw graidents with more than 8 stops. truncating."); - } - let len = stops.len().min(8); - let mut colors_buff = [[0.0; 4]; 8]; - let mut stops_buff = [0.0; 8]; - for i in 0..len { - colors_buff[i] = stops[i].color.rgba; - stops_buff[i] = stops[i].stop; - } - //this should be filled in with custom gradient stuff later: - self.buffers.gradients.push(GpuGradient { - type_id: match gradient_type { - GradientType::Linear => 0, - GradientType::Radial => 1, - }, - position: pos.to_array(), - main_axis: main_axis.to_array(), - off_axis: off_axis.to_array(), - stop_count: len as u32, - colors: colors_buff, - stops: stops_buff, - _padding: [0; 16], - }); - } - } - let primitive = GpuPrimitive { - fill_id, - fill_type_flag, - clipping_id: 0, - transform_id: self.transform_index_stack.last().cloned().unwrap_or(0) as u32, - z_index: 0, - }; - let prim_id = self.buffers.primitives.len() as u32; - self.buffers.primitives.push(primitive); - prim_id + // TODO don't recreate encoder/render pass each frame + let mesh = self + .mesh_renderer + .make_mesh(&self.backend.device, &geometry, fill); + let mut render_pass = + Self::main_draw_render_pass(&self.backend, &self.stencil_renderer, &mut self.encoder); + self.mesh_renderer.render_meshes(&mut render_pass, &[mesh]); } pub fn clear(&mut self) { - self.render_backend.clear(); + self.backend.clear(); + // need to clear the stencil texture + self.stencil_renderer + .clear(&self.backend.device, &self.backend.queue); } pub fn draw_image(&mut self, image: &Image, rect: Box2D) { - let transform = self.current_transform(); - if self.buffers.primitives.len() > 0 { - self.render_backend.render_primitives(&mut self.buffers); - let CpuBuffers { - geometry, - primitives, - transforms: _, - colors, - gradients, - } = &mut self.buffers; - geometry.vertices.clear(); - geometry.indices.clear(); - primitives.clear(); - colors.clear(); - gradients.clear(); - } - self.render_backend.render_image(image, transform, rect); + // let transform = self.current_transform(); + // self.render_backend.render_image(image, transform, rect); + } + + // used for image and mesh drawing + pub fn main_draw_render_pass<'encoder>( + backend: &RenderBackend, + stencil_renderer: &StencilRenderer, + encoder: &'encoder mut wgpu::CommandEncoder, + ) -> wgpu::RenderPass<'encoder> { + let (_screen_surface, screen_texture) = backend.get_screen_texture(); + let (stencil_texture, stencil_index) = stencil_renderer.get_stencil(); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Main draw render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &screen_texture, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: stencil_texture, + depth_ops: None, + stencil_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }), + }), + timestamp_writes: None, + occlusion_query_set: None, + }); + render_pass.set_stencil_reference(stencil_index); + render_pass } pub fn flush(&mut self) { - if self.buffers.primitives.len() > 0 { - self.render_backend.render_primitives(&mut self.buffers); - } - self.buffers.reset(); - self.transform_index_stack.clear(); + let encoder = std::mem::replace( + &mut self.encoder, + self.backend + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }), + ); + self.backend.queue.submit(std::iter::once(encoder.finish())); + let (screen_surface, _screen_texture) = self.backend.get_screen_texture(); + //render primitives + screen_surface.present(); + // TODO finish and recreate encoder here + // if self.buffers.primitives.len() > 0 { + // self.render_backend.render_primitives(&mut self.buffers); + // } + // self.buffers.reset(); + // self.transform_index_stack.clear(); } pub fn save(&mut self) { - let transform_len = self.transform_index_stack.len(); - self.saves - .push((transform_len, self.render_backend.get_clip_depth() as usize)); + // let transform_len = self.transform_index_stack.len(); + // self.saves + // .push((transform_len, self.render_backend.get_clip_depth() as usize)); } pub fn restore(&mut self) { - if let Some((t_pen, clip_depth)) = self.saves.pop() { - self.transform_index_stack.truncate(t_pen); - self.render_backend - .reset_stencil_depth_to(clip_depth as u32); - } + // if let Some((t_pen, clip_depth)) = self.saves.pop() { + // self.transform_index_stack.truncate(t_pen); + // self.render_backend + // .reset_stencil_depth_to(clip_depth as u32); + // } } pub fn transform(&mut self, transform: Transform2D) { - let new_ind = self.buffers.transforms.len(); - self.buffers.transforms.push(GpuTransform { - transform: transform.then(&self.current_transform()).to_arrays(), - _pad: 0, - _pad2: 0, - }); - self.transform_index_stack.push(new_ind); + self.backend.set_transform(transform); } pub fn clip(&mut self, path: Path) { // fine to transform on CPU - shouldn't be large meshes - let path = path.transformed(&self.current_transform()); - if self.buffers.primitives.len() > 0 { - self.render_backend.render_primitives(&mut self.buffers); - let CpuBuffers { - geometry, - primitives, - transforms: _, - colors, - gradients, - } = &mut self.buffers; - geometry.vertices.clear(); - geometry.indices.clear(); - primitives.clear(); - colors.clear(); - gradients.clear(); - } - let options = FillOptions::tolerance(self.tolerance); - let mut geometry = VertexBuffers::new(); - let mut geometry_builder = - BuffersBuilder::new(&mut geometry, |vertex: FillVertex| stencil::Vertex { - position: vertex.position().to_array(), - }); - match FillTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { - Ok(_) => {} - Err(e) => log::warn!("{:?}", e), - }; - self.render_backend.push_stencil(geometry); + // let path = path.transformed(&self.current_transform()); + // if self.buffers.primitives.len() > 0 { + // self.render_backend.render_primitives(&mut self.buffers); + // let CpuBuffers { + // geometry, + // primitives, + // transforms: _, + // colors, + // gradients, + // } = &mut self.buffers; + // geometry.vertices.clear(); + // geometry.indices.clear(); + // primitives.clear(); + // colors.clear(); + // gradients.clear(); + // } + // let options = FillOptions::tolerance(self.tolerance); + // let mut geometry = VertexBuffers::new(); + // let mut geometry_builder = BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { + // stencil_renderer::Vertex { + // position: vertex.position().to_array(), + // } + // }); + // match FillTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { + // Ok(_) => {} + // Err(e) => log::warn!("{:?}", e), + // }; + // self.render_backend.push_stencil(geometry); } pub fn resize(&mut self, width: f32, height: f32) { - self.render_backend.resize(width as u32, height as u32); + self.backend.resize(width as u32, height as u32); + // needs to resize stencil texture + self.stencil_renderer + .resize(&self.backend.device, width as u32, height as u32); } pub fn size(&self) -> (f32, f32) { - let res = &self.render_backend.globals.resolution; + let res = &self.backend.globals.resolution; (res[0], res[1]) } } @@ -259,7 +243,7 @@ pub enum GradientType { #[derive(Debug)] pub struct Color { - rgba: [f32; 4], + pub rgba: [f32; 4], } impl Color { diff --git a/pax-pixels/src/render_context/mesh_renderer.rs b/pax-pixels/src/render_context/mesh_renderer.rs new file mode 100644 index 000000000..d76c91bda --- /dev/null +++ b/pax-pixels/src/render_context/mesh_renderer.rs @@ -0,0 +1,292 @@ +use crate::{ + render_backend::{ + data::{GpuMeshMetadata, GpuVertex}, + RenderBackend, + }, + Fill, GradientType, +}; +use lyon::tessellation::VertexBuffers; +use wgpu::util::DeviceExt; + +pub struct MeshResource { + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + index_count: u32, + + bind_group: wgpu::BindGroup, + _metadata_buffer: wgpu::Buffer, +} + +impl MeshResource { + pub(crate) fn new( + device: &wgpu::Device, + mesh_bind_group_layout: &wgpu::BindGroupLayout, + geometry: &VertexBuffers, + fill: Fill, + ) -> Self { + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("mesh vertices"), + contents: bytemuck::cast_slice(&geometry.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("mesh indicies"), + contents: bytemuck::cast_slice(&geometry.indices), + usage: wgpu::BufferUsages::INDEX, + }); + + let mesh_metadata = match fill { + Fill::Solid(color) => GpuMeshMetadata { + type_id: 0, + colors: { + let mut colors = [[0.0; 4]; 8]; + colors[0] = color.rgba; + colors + }, + ..Default::default() + }, + Fill::Gradient { + gradient_type, + pos, + main_axis, + off_axis, + stops, + } => { + if stops.len() > 8 { + log::warn!("can't draw graidents with more than 8 stops. truncating."); + } + let len = stops.len().min(8); + let mut colors_buff = [[0.0; 4]; 8]; + let mut stops_buff = [0.0; 8]; + for i in 0..len { + colors_buff[i] = stops[i].color.rgba; + stops_buff[i] = stops[i].stop; + } + //this should be filled in with custom gradient stuff later: + GpuMeshMetadata { + type_id: match gradient_type { + GradientType::Linear => 1, + GradientType::Radial => 2, + }, + position: pos.to_array(), + main_axis: main_axis.to_array(), + off_axis: off_axis.to_array(), + stop_count: len as u32, + colors: colors_buff, + stops: stops_buff, + _padding: [0; 16], + } + } + }; + + let metadata_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("mesh metadata"), + contents: bytemuck::cast_slice(&[mesh_metadata]), + usage: wgpu::BufferUsages::UNIFORM, + }); + + let mesh_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &mesh_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: metadata_buffer.as_entire_binding(), + }], + label: Some("mesh_bind_group"), + }); + + Self { + vertex_buffer, + index_buffer, + index_count: geometry.indices.len() as u32, + _metadata_buffer: metadata_buffer, + bind_group: mesh_bind_group, + } + } +} + +pub struct MeshRenderer { + pipeline: wgpu::RenderPipeline, + mesh_bind_group_layout: wgpu::BindGroupLayout, + globals_bind_group: wgpu::BindGroup, +} + +impl MeshRenderer { + pub fn new(backend: &RenderBackend) -> Self { + let mesh_bind_group_layout = + backend + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("mesh_bind_group_layout"), + }); + + let globals_bind_group_layout = + backend + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + label: Some("globals_bind_group_layout"), + }); + + let pipeline = Self::create_pipeline( + &backend.device, + backend.surface_config.format, + &globals_bind_group_layout, + &mesh_bind_group_layout, + ); + + // TODO move this to backend itself? (and use in other render plugins as well) + let globals_bind_group = backend + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: &globals_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: backend.globals_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: backend.transform_buffer.as_entire_binding(), + }, + ], + label: Some("globals_bind_group"), + }); + + Self { + pipeline, + mesh_bind_group_layout, + globals_bind_group, + } + } + + fn create_pipeline( + device: &wgpu::Device, + format: wgpu::TextureFormat, + globals_bind_group_layout: &wgpu::BindGroupLayout, + mesh_bind_group_layout: &wgpu::BindGroupLayout, + ) -> wgpu::RenderPipeline { + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[&globals_bind_group_layout, &mesh_bind_group_layout], + push_constant_ranges: &[], + }); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("../../shaders/geometry.wgsl").into()), + }); + + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[GpuVertex::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Stencil8, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Always, + stencil: wgpu::StencilState { + front: wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Equal, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::Keep, + }, + back: wgpu::StencilFaceState::IGNORE, + read_mask: !0, + write_mask: !0, + }, + bias: Default::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }) + } + + pub(crate) fn make_mesh( + &self, + device: &wgpu::Device, + geometry: &VertexBuffers, + fill: Fill, + ) -> MeshResource { + MeshResource::new(device, &self.mesh_bind_group_layout, geometry, fill) + } + + pub(crate) fn render_meshes( + &self, + render_pass: &mut wgpu::RenderPass, + meshes: &[MeshResource], + ) { + render_pass.set_pipeline(&self.pipeline); + render_pass.set_bind_group(0, &self.globals_bind_group, &[]); + { + for mesh in meshes { + render_pass.set_bind_group(1, &mesh.bind_group, &[]); + render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + render_pass + .set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16); + render_pass.draw_indexed(0..mesh.index_count as u32, 0, 0..1); + } + } + } +} diff --git a/pax-pixels/src/render_backend/stencil.rs b/pax-pixels/src/render_context/stencil_renderer.rs similarity index 50% rename from pax-pixels/src/render_backend/stencil.rs rename to pax-pixels/src/render_context/stencil_renderer.rs index 8b8b5515a..6e542f945 100644 --- a/pax-pixels/src/render_backend/stencil.rs +++ b/pax-pixels/src/render_context/stencil_renderer.rs @@ -3,6 +3,8 @@ use lyon::tessellation::VertexBuffers; use wgpu::util::DeviceExt; use wgpu::{BufferUsages, Device, Queue, RenderPipeline}; +use crate::render_backend::RenderBackend; + pub struct StencilRenderer { stencil_pipeline: RenderPipeline, decrement_pipeline: RenderPipeline, @@ -25,161 +27,185 @@ pub struct Vertex { } impl StencilRenderer { - pub fn new(device: &Device, width: u32, height: u32, globals: &wgpu::Buffer) -> Self { - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Stencil Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("stencil.wgsl").into()), - }); + pub fn new(backend: &RenderBackend) -> Self { + let shader = backend + .device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Stencil Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("../../shaders/stencil.wgsl").into()), + }); let stencil_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { + backend + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("stencil_bind_group_layout"), + }); + + let stencil_bind_group = backend + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: &stencil_bind_group_layout, + entries: &[wgpu::BindGroupEntry { binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, + resource: backend.globals_buffer.as_entire_binding(), }], - label: Some("stencil_bind_group_layout"), + label: Some("stencil_bind_group"), }); - let stencil_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &stencil_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: globals.as_entire_binding(), - }], - label: Some("stencil_bind_group"), - }); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Stencil Pipeline Layout"), - bind_group_layouts: &[&stencil_bind_group_layout], - push_constant_ranges: &[], - }); + let pipeline_layout = + backend + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Stencil Pipeline Layout"), + bind_group_layouts: &[&stencil_bind_group_layout], + push_constant_ranges: &[], + }); // Main stencil pipeline for incrementing - let stencil_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Stencil Pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![0 => Float32x2], - }], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[None], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Stencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Always, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::IncrementClamp, + let stencil_pipeline = + backend + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Stencil Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x2], + }], + compilation_options: Default::default(), }, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: Default::default(), - }), - multisample: wgpu::MultisampleState::default(), - multiview: None, - cache: None, - }); + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[None], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Stencil8, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Always, + stencil: wgpu::StencilState { + front: wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Always, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::IncrementClamp, + }, + back: wgpu::StencilFaceState::IGNORE, + read_mask: !0, + write_mask: !0, + }, + bias: Default::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); // Decrement pipeline for popping - let decrement_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Decrement Pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![0 => Float32x2], - }], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[None], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Stencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Always, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::DecrementClamp, + let decrement_pipeline = + backend + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Decrement Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x2], + }], + compilation_options: Default::default(), }, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: Default::default(), - }), - multisample: wgpu::MultisampleState::default(), - multiview: None, - cache: None, - }); + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[None], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Stencil8, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Always, + stencil: wgpu::StencilState { + front: wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Always, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::DecrementClamp, + }, + back: wgpu::StencilFaceState::IGNORE, + read_mask: !0, + write_mask: !0, + }, + bias: Default::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); let vertices = [Vertex { position: [0.0, 0.0], }; 1024]; - let vertices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Stencil Vertices"), - contents: bytemuck::cast_slice(&vertices), - usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, - }); + let vertices_buffer = + backend + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Stencil Vertices"), + contents: bytemuck::cast_slice(&vertices), + usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, + }); let indices: [u16; 1024] = [0; 1024]; - let indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Stencil Indices"), - contents: bytemuck::cast_slice(&indices), - usage: BufferUsages::INDEX | BufferUsages::COPY_DST, - }); + let indices_buffer = backend + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Stencil Indices"), + contents: bytemuck::cast_slice(&indices), + usage: BufferUsages::INDEX | BufferUsages::COPY_DST, + }); - let (stencil_texture, stencil_view) = Self::create_stencil_texture(device, width, height); + let (stencil_texture, stencil_view) = Self::create_stencil_texture( + &backend.device, + backend.config.width, + backend.config.height, + ); Self { stencil_pipeline, @@ -188,8 +214,8 @@ impl StencilRenderer { indices_buffer, stencil_texture, stencil_view, - width, - height, + width: backend.config.width, + height: backend.config.height, stencil_layer: 0, stencil_geometry_stack: vec![], stencil_bind_group, diff --git a/pax-pixels/src/render_backend/texture.rs b/pax-pixels/src/render_context/texture_renderer.rs similarity index 55% rename from pax-pixels/src/render_backend/texture.rs rename to pax-pixels/src/render_context/texture_renderer.rs index 6f97fd71d..915bef62b 100644 --- a/pax-pixels/src/render_backend/texture.rs +++ b/pax-pixels/src/render_context/texture_renderer.rs @@ -4,12 +4,13 @@ use lyon::geom::Point; use wgpu::BufferUsages; use wgpu::IndexFormat; +use crate::render_backend::RenderBackend; use crate::Box2D; use crate::Transform2D; use wgpu::util::DeviceExt; use wgpu::TextureFormat; -use super::stencil::StencilRenderer; +use super::stencil_renderer::StencilRenderer; pub struct TextureRenderer { vertices_buffer: wgpu::Buffer, @@ -21,119 +22,135 @@ pub struct TextureRenderer { } impl TextureRenderer { - pub fn new(device: &wgpu::Device) -> Self { + pub fn new(backend: &RenderBackend) -> Self { const TEXTURE_FORMAT: TextureFormat = TextureFormat::Rgba16Float; - let texture_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Texture Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("textures.wgsl").into()), - }); + let texture_shader = backend + .device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Texture Shader"), + source: wgpu::ShaderSource::Wgsl( + include_str!("../../shaders/textures.wgsl").into(), + ), + }); let texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + backend + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }); let texture_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Texture Pipeline Layout"), - bind_group_layouts: &[&texture_bind_group_layout], - push_constant_ranges: &[], - }); + backend + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Texture Pipeline Layout"), + bind_group_layouts: &[&texture_bind_group_layout], + push_constant_ranges: &[], + }); - let texture_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Texture Pipeline"), - layout: Some(&texture_pipeline_layout), - vertex: wgpu::VertexState { - module: &texture_shader, - entry_point: "vs_main", - buffers: &[TextureVertex::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &texture_shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: TEXTURE_FORMAT, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Stencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState { - compare: wgpu::CompareFunction::Equal, - fail_op: wgpu::StencilOperation::Keep, - depth_fail_op: wgpu::StencilOperation::Keep, - pass_op: wgpu::StencilOperation::Keep, + let texture_pipeline = + backend + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Texture Pipeline"), + layout: Some(&texture_pipeline_layout), + vertex: wgpu::VertexState { + module: &texture_shader, + entry_point: "vs_main", + buffers: &[TextureVertex::desc()], + compilation_options: Default::default(), }, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: Default::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + fragment: Some(wgpu::FragmentState { + module: &texture_shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: TEXTURE_FORMAT, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Stencil8, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Always, + stencil: wgpu::StencilState { + front: wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Equal, + fail_op: wgpu::StencilOperation::Keep, + depth_fail_op: wgpu::StencilOperation::Keep, + pass_op: wgpu::StencilOperation::Keep, + }, + back: wgpu::StencilFaceState::IGNORE, + read_mask: !0, + write_mask: !0, + }, + bias: Default::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); let vertices = [TextureVertex::default(); 6]; - let vertices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, - }); + let vertices_buffer = + backend + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&vertices), + usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, + }); let indices: &[u16] = &[1, 0, 2, 1, 2, 3]; - let indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(&indices), - usage: BufferUsages::INDEX, - }); - let texture_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + let indices_buffer = backend + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&indices), + usage: BufferUsages::INDEX, + }); + let texture_sampler = backend.device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, diff --git a/pax-runtime/src/engine/mod.rs b/pax-runtime/src/engine/mod.rs index 2a6f7115d..1b90942f0 100644 --- a/pax-runtime/src/engine/mod.rs +++ b/pax-runtime/src/engine/mod.rs @@ -7,6 +7,7 @@ use std::ops::Range; use std::rc::Rc; use std::sync::atomic::{AtomicUsize, Ordering}; +use kurbo::{Affine, Circle, Shape}; use pax_message::NativeMessage; use pax_runtime_api::{ pax_value::PaxAny, use_RefCell, Event, Focus, SelectStart, Variable, Window, OS, @@ -15,7 +16,7 @@ use pax_runtime_api::{ use crate::api::{KeyDown, KeyPress, KeyUp, NodeContext, RenderContext}; use crate::{ComponentInstance, RuntimeContext}; -use pax_runtime_api::Platform; +use pax_runtime_api::{Color, Fill, GradientStop, LinearGradient, Platform, Size}; pub mod node_interface; pub mod occlusion; @@ -300,13 +301,39 @@ impl PaxEngine { .unwrap_or(false) { rcs.clear(i); + let circle = Circle::new((300.0, 300.0), 400.0); + rcs.fill(i, circle.into_path(0.1), &Fill::Solid(Color::BLUE)); + let circle = Circle::new((600.0, 500.0), 300.0); + rcs.transform(i, Affine::translate((200.0, 200.0))); + rcs.fill( + i, + circle.into_path(0.1), + &Fill::LinearGradient(LinearGradient { + start: (Size::Percent(0.into()), Size::Percent(0.into())), + end: (Size::Percent(100.0.into()), Size::Percent(100.0.into())), + stops: vec![ + GradientStop { + position: Size::Percent(0.0.into()), + color: Color::RED, + }, + GradientStop { + position: Size::Percent(50.0.into()), + color: Color::BLUE, + }, + GradientStop { + position: Size::Percent(100.0.into()), + color: Color::GREEN, + }, + ], + }), + ); } } // This is pretty useful during debugging - left it here since I use it often. /Sam // crate::api::log(&format!("tree: {:#?}", self.root_node)); - self.root_expanded_node - .recurse_render_queue(&mut self.runtime_context, rcs); - self.runtime_context.recurse_flush_queued_renders(rcs); + // self.root_expanded_node + // .recurse_render_queue(&mut self.runtime_context, rcs); + // self.runtime_context.recurse_flush_queued_renders(rcs); self.runtime_context.clear_all_dirty_canvases(); From 0dfecde949cc5965a2e5421a0573234a3478d038 Mon Sep 17 00:00:00 2001 From: Samuel Selleck Date: Thu, 31 Oct 2024 18:24:14 -0700 Subject: [PATCH 2/2] everything working after resource refactor --- pax-chassis-web/Cargo.toml | 2 +- pax-pixels/src/render_context.rs | 250 +++++++--------- .../src/render_context/stencil_renderer.rs | 2 +- .../src/render_context/texture_renderer.rs | 277 +++++++++--------- pax-runtime/src/engine/mod.rs | 32 +- .../src/engine/pax_pixels_render_context.rs | 6 +- 6 files changed, 247 insertions(+), 322 deletions(-) diff --git a/pax-chassis-web/Cargo.toml b/pax-chassis-web/Cargo.toml index 804dc54b9..0ae77e3ad 100644 --- a/pax-chassis-web/Cargo.toml +++ b/pax-chassis-web/Cargo.toml @@ -13,7 +13,7 @@ include = ["/src", "/interface/public"] crate-type = ["cdylib", "rlib"] [features] -default = ["console_error_panic_hook", "gpu"] +default = ["console_error_panic_hook"] designtime = ["dep:pax-designtime", "pax-runtime/designtime"] gpu = [] diff --git a/pax-pixels/src/render_context.rs b/pax-pixels/src/render_context.rs index c247fcdaf..753f0594e 100644 --- a/pax-pixels/src/render_context.rs +++ b/pax-pixels/src/render_context.rs @@ -9,6 +9,9 @@ use lyon::lyon_tessellation::FillTessellator; use lyon::lyon_tessellation::FillVertex; use lyon::lyon_tessellation::VertexBuffers; use lyon::path::Path; +use lyon::tessellation::StrokeOptions; +use lyon::tessellation::StrokeTessellator; +use lyon::tessellation::StrokeVertex; mod mesh_renderer; mod stencil_renderer; @@ -21,12 +24,20 @@ use stencil_renderer::StencilRenderer; use texture_renderer::TextureRenderer; pub struct WgpuRenderer<'w> { + // rendering backend: RenderBackend<'w>, pub texture_renderer: TextureRenderer, pub stencil_renderer: StencilRenderer, pub mesh_renderer: MeshRenderer, encoder: wgpu::CommandEncoder, + + // config tolerance: f32, + + //state + transform_stack: Vec, + // points to restore to, transform_stack/clipping_stack respectively + save_points: Vec<(usize, usize)>, } impl<'w> WgpuRenderer<'w> { @@ -47,34 +58,38 @@ impl<'w> WgpuRenderer<'w> { encoder, //TODO expose as option tolerance: 0.5, + transform_stack: vec![], + save_points: vec![], } } - // fn current_transform(&self) -> Transform2D { - // Transform2D::from_arrays( - // self.buffers - // .transforms - // .get(self.transform_index_stack.last().cloned().unwrap_or(0)) - // .expect("at least one identity transform should exist on the transform stack") - // .transform, - // ) - // } - pub fn stroke_path(&mut self, path: Path, stroke_fill: Fill, stroke_width: f32) { - // let prim_id = self.push_primitive_def(stroke_fill); - // let options = StrokeOptions::tolerance(self.tolerance).with_line_width(stroke_width); - // let mut geometry_builder = - // BuffersBuilder::new(&mut self.buffers.geometry, |vertex: StrokeVertex| { - // GpuVertex { - // position: vertex.position().to_array(), - // normal: [0.0; 2], - // prim_id, - // } - // }); - // match StrokeTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { - // Ok(_) => {} - // Err(e) => log::warn!("{:?}", e), - // }; + let options = StrokeOptions::tolerance(self.tolerance).with_line_width(stroke_width); + let mut geometry = VertexBuffers::new(); + let mut geometry_builder = + BuffersBuilder::new(&mut geometry, |vertex: StrokeVertex| GpuVertex { + position: vertex.position().to_array(), + normal: [0.0; 2], + prim_id: 0, + }); + match StrokeTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { + Ok(_) => {} + Err(e) => log::warn!("{:?}", e), + }; + + // TODO ------- everything bellow looks the same for fill -------- + + // TODO don't recreate this mesh each frame, instead return this mesh from a create_fill_path_resource method + let mesh = self + .mesh_renderer + .make_mesh(&self.backend.device, &geometry, stroke_fill); + + // TODO this code becomes the new "fill_path" method, everything above the "create" + // TODO another big optimization: if transform/clip doesn't change, we can use the same render pass + // for drawing all meshes and images. + let mut render_pass = + Self::main_draw_render_pass(&self.backend, &self.stencil_renderer, &mut self.encoder); + self.mesh_renderer.render_meshes(&mut render_pass, &[mesh]); } pub fn fill_path(&mut self, path: Path, fill: Fill) { @@ -91,10 +106,14 @@ impl<'w> WgpuRenderer<'w> { Err(e) => log::warn!("{:?}", e), }; - // TODO don't recreate encoder/render pass each frame + // TODO ------- everything bellow looks the same for stroke -------- + + // TODO don't recreate this mesh each frame, instead return this mesh from a create_fill_path_resource method let mesh = self .mesh_renderer .make_mesh(&self.backend.device, &geometry, fill); + + // TODO this code becomes the new "fill_path" method, everything above the "create" let mut render_pass = Self::main_draw_render_pass(&self.backend, &self.stencil_renderer, &mut self.encoder); self.mesh_renderer.render_meshes(&mut render_pass, &[mesh]); @@ -108,8 +127,21 @@ impl<'w> WgpuRenderer<'w> { } pub fn draw_image(&mut self, image: &Image, rect: Box2D) { - // let transform = self.current_transform(); - // self.render_backend.render_image(image, transform, rect); + let transform = self.transform_stack.last().cloned().unwrap_or_default(); + + // TODO don't create this every time an image is drawn, instead split into two parts: "load_image" that gives back + // a resource, and "draw_image" that takes that resource and draws it using the below logic + let image_resource = self.texture_renderer.make_image( + &self.backend, + &transform, + &rect, + &image.rgba, + image.pixel_width, + ); + let mut render_pass = + Self::main_draw_render_pass(&self.backend, &self.stencil_renderer, &mut self.encoder); + self.texture_renderer + .render_image(&mut render_pass, &image_resource) } // used for image and mesh drawing @@ -146,6 +178,7 @@ impl<'w> WgpuRenderer<'w> { } pub fn flush(&mut self) { + // draw everything scheduled with the previous encoder, and create a new one for the next frame let encoder = std::mem::replace( &mut self.encoder, self.backend @@ -158,62 +191,59 @@ impl<'w> WgpuRenderer<'w> { let (screen_surface, _screen_texture) = self.backend.get_screen_texture(); //render primitives screen_surface.present(); - // TODO finish and recreate encoder here - // if self.buffers.primitives.len() > 0 { - // self.render_backend.render_primitives(&mut self.buffers); - // } - // self.buffers.reset(); - // self.transform_index_stack.clear(); } pub fn save(&mut self) { - // let transform_len = self.transform_index_stack.len(); - // self.saves - // .push((transform_len, self.render_backend.get_clip_depth() as usize)); + let transform_len = self.transform_stack.len(); + self.save_points + .push((transform_len, self.stencil_renderer.stencil_layer as usize)); } pub fn restore(&mut self) { - // if let Some((t_pen, clip_depth)) = self.saves.pop() { - // self.transform_index_stack.truncate(t_pen); - // self.render_backend - // .reset_stencil_depth_to(clip_depth as u32); - // } + if let Some((t_pen, clip_depth)) = self.save_points.pop() { + self.transform_stack.truncate(t_pen); + // make sure everything queued get's drawn with the current transform/stencil + // before we modify it + self.flush(); + self.backend + .set_transform(self.transform_stack.last().cloned().unwrap_or_default()); + self.stencil_renderer.reset_stencil_depth_to( + &self.backend.device, + &self.backend.queue, + clip_depth as u32, + ); + } } - pub fn transform(&mut self, transform: Transform2D) { - self.backend.set_transform(transform); + pub fn transform(&mut self, transform: &Transform2D) { + let prev_transform = self.transform_stack.last().cloned().unwrap_or_default(); + let new_transform = prev_transform.then(&transform); + self.transform_stack.push(new_transform); + // make sure everything queued get's drawn with the current transform + // before we modify it + self.flush(); + self.backend.set_transform(new_transform); } pub fn clip(&mut self, path: Path) { + // make sure everything queued get's drawn with the current stencil + // before we modify it + self.flush(); // fine to transform on CPU - shouldn't be large meshes - // let path = path.transformed(&self.current_transform()); - // if self.buffers.primitives.len() > 0 { - // self.render_backend.render_primitives(&mut self.buffers); - // let CpuBuffers { - // geometry, - // primitives, - // transforms: _, - // colors, - // gradients, - // } = &mut self.buffers; - // geometry.vertices.clear(); - // geometry.indices.clear(); - // primitives.clear(); - // colors.clear(); - // gradients.clear(); - // } - // let options = FillOptions::tolerance(self.tolerance); - // let mut geometry = VertexBuffers::new(); - // let mut geometry_builder = BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { - // stencil_renderer::Vertex { - // position: vertex.position().to_array(), - // } - // }); - // match FillTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { - // Ok(_) => {} - // Err(e) => log::warn!("{:?}", e), - // }; - // self.render_backend.push_stencil(geometry); + let path = path.transformed(&self.transform_stack.last().cloned().unwrap_or_default()); + let options = FillOptions::tolerance(self.tolerance); + let mut geometry = VertexBuffers::new(); + let mut geometry_builder = BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { + stencil_renderer::Vertex { + position: vertex.position().to_array(), + } + }); + match FillTessellator::new().tessellate_path(&path, &options, &mut geometry_builder) { + Ok(_) => {} + Err(e) => log::warn!("{:?}", e), + }; + self.stencil_renderer + .push_stencil(&self.backend.device, &self.backend.queue, geometry); } pub fn resize(&mut self, width: f32, height: f32) { @@ -246,84 +276,6 @@ pub struct Color { pub rgba: [f32; 4], } -impl Color { - pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { - Self { rgba: [r, g, b, a] } - } - - //credit: ChatGPT - pub fn hsva(h: f32, s: f32, v: f32, a: f32) -> Self { - let i = (h * 6.0).floor() as i32; - let f = h * 6.0 - i as f32; - let p = v * (1.0 - s); - let q = v * (1.0 - f * s); - let t = v * (1.0 - (1.0 - f) * s); - - let (r, g, b) = match i % 6 { - 0 => (v, t, p), - 1 => (q, v, p), - 2 => (p, v, t), - 3 => (p, q, v), - 4 => (t, p, v), - _ => (v, p, q), - }; - Self { rgba: [r, g, b, a] } - } - - //Credit piet library: https://docs.rs/piet/latest/src/piet/color.rs.html#130-173 - pub fn hlca(h: f32, l: f32, c: f32, a: f32) -> Self { - // The reverse transformation from Lab to XYZ, see - // https://en.wikipedia.org/wiki/CIELAB_color_space - fn f_inv(t: f32) -> f32 { - let d = 6. / 29.; - if t > d { - t.powi(3) - } else { - 3. * d * d * (t - 4. / 29.) - } - } - let th = h * (std::f32::consts::PI / 180.); - let a_2 = c * th.cos(); - let b = c * th.sin(); - let ll = (l + 16.) * (1. / 116.); - // Produce raw XYZ values - let x = f_inv(ll + a_2 * (1. / 500.)); - let y = f_inv(ll); - let z = f_inv(ll - b * (1. / 200.)); - // This matrix is the concatenation of three sources. - // First, the white point is taken to be ICC standard D50, so - // the diagonal matrix of [0.9642, 1, 0.8249]. Note that there - // is some controversy around this value. However, it matches - // the other matrices, thus minimizing chroma error. - // - // Second, an adaption matrix from D50 to D65. This is the - // inverse of the recommended D50 to D65 adaptation matrix - // from the W3C sRGB spec: - // https://www.w3.org/Graphics/Color/srgb - // - // Finally, the conversion from XYZ to linear sRGB values, - // also taken from the W3C sRGB spec. - let r_lin = 3.02172918 * x - 1.61692294 * y - 0.40480625 * z; - let g_lin = -0.94339358 * x + 1.91584267 * y + 0.02755094 * z; - let b_lin = 0.06945666 * x - 0.22903204 * y + 1.15957526 * z; - fn gamma(u: f32) -> f32 { - if u <= 0.0031308 { - 12.92 * u - } else { - 1.055 * u.powf(1. / 2.4) - 0.055 - } - } - Self { - rgba: [ - gamma(r_lin).clamp(0.0, 1.0), - gamma(g_lin).clamp(0.0, 1.0), - gamma(b_lin).clamp(0.0, 1.0), - a, - ], - } - } -} - #[derive(Debug)] pub enum Fill { Solid(Color), diff --git a/pax-pixels/src/render_context/stencil_renderer.rs b/pax-pixels/src/render_context/stencil_renderer.rs index 6e542f945..97b1e081d 100644 --- a/pax-pixels/src/render_context/stencil_renderer.rs +++ b/pax-pixels/src/render_context/stencil_renderer.rs @@ -12,7 +12,7 @@ pub struct StencilRenderer { indices_buffer: wgpu::Buffer, stencil_texture: wgpu::Texture, stencil_view: wgpu::TextureView, - stencil_layer: u32, + pub stencil_layer: u32, stencil_geometry_stack: Vec>, width: u32, height: u32, diff --git a/pax-pixels/src/render_context/texture_renderer.rs b/pax-pixels/src/render_context/texture_renderer.rs index 915bef62b..2475f8d48 100644 --- a/pax-pixels/src/render_context/texture_renderer.rs +++ b/pax-pixels/src/render_context/texture_renderer.rs @@ -10,12 +10,125 @@ use crate::Transform2D; use wgpu::util::DeviceExt; use wgpu::TextureFormat; -use super::stencil_renderer::StencilRenderer; - -pub struct TextureRenderer { +pub struct ImageResource { vertices_buffer: wgpu::Buffer, indices_buffer: wgpu::Buffer, + texture_bind_group: wgpu::BindGroup, + texture: wgpu::Texture, +} + +impl ImageResource { + fn new( + backend: &RenderBackend, + texture_bind_group_layout: &wgpu::BindGroupLayout, + texture_sampler: &wgpu::Sampler, + transform: &Transform2D, + location: &Box2D, + rgba: &[u8], + rgba_width: u32, + ) -> Self { + let points = get_transformed_corners(&location, &transform); + let height = rgba.len() as u32 / (rgba_width * 4); + let vertices = [ + TextureVertex { + position: points[0].to_array(), + texture_coord: [0.0, 0.0], + }, + TextureVertex { + position: points[1].to_array(), + texture_coord: [1.0, 0.0], + }, + TextureVertex { + position: points[2].to_array(), + texture_coord: [0.0, 1.0], + }, + TextureVertex { + position: points[3].to_array(), + texture_coord: [1.0, 1.0], + }, + ]; + let vertices_buffer = + backend + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&vertices), + usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, + }); + let indices: &[u16] = &[1, 0, 2, 1, 2, 3]; + let indices_buffer = backend + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&indices), + usage: BufferUsages::INDEX, + }); + + let size = wgpu::Extent3d { + width: rgba_width, + height, + depth_or_array_layers: 1, + }; + let texture = backend.device.create_texture(&wgpu::TextureDescriptor { + label: Some("Texture Creation"), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + backend.queue.write_texture( + wgpu::ImageCopyTexture { + aspect: wgpu::TextureAspect::All, + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + }, + &rgba, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(4 * rgba_width), + rows_per_image: Some(height), + }, + size, + ); + + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let texture_bind_group = backend + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: backend.globals_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&texture_view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&texture_sampler), + }, + ], + label: Some("texture_bind_group"), + }); + + Self { + vertices_buffer, + indices_buffer, + texture_bind_group, + texture, + } + } +} +pub struct TextureRenderer { texture_sampler: wgpu::Sampler, texture_pipeline: wgpu::RenderPipeline, texture_bind_group_layout: wgpu::BindGroupLayout, @@ -133,23 +246,6 @@ impl TextureRenderer { cache: None, }); - let vertices = [TextureVertex::default(); 6]; - let vertices_buffer = - backend - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, - }); - let indices: &[u16] = &[1, 0, 2, 1, 2, 3]; - let indices_buffer = backend - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(&indices), - usage: BufferUsages::INDEX, - }); let texture_sampler = backend.device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, @@ -160,136 +256,37 @@ impl TextureRenderer { ..Default::default() }); Self { - vertices_buffer, - indices_buffer, texture_sampler, texture_pipeline, texture_bind_group_layout, } } - pub fn render_image( + pub fn make_image( &self, - device: &wgpu::Device, - queue: &wgpu::Queue, - target: &wgpu::TextureView, - globals: &wgpu::Buffer, - stencil_renderer: &StencilRenderer, + backend: &RenderBackend, + transform: &Transform2D, + location: &Box2D, rgba: &[u8], rgba_width: u32, - transform: Transform2D, - location: Box2D, - ) { - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Texture Encoder"), - }); - let points = get_transformed_corners(&location, &transform); - let height = rgba.len() as u32 / (rgba_width * 4); - let verts = [ - TextureVertex { - position: points[0].to_array(), - texture_coord: [0.0, 0.0], - }, - TextureVertex { - position: points[1].to_array(), - texture_coord: [1.0, 0.0], - }, - TextureVertex { - position: points[2].to_array(), - texture_coord: [0.0, 1.0], - }, - TextureVertex { - position: points[3].to_array(), - texture_coord: [1.0, 1.0], - }, - ]; - queue.write_buffer(&self.vertices_buffer, 0, bytemuck::cast_slice(&verts)); - - let size = wgpu::Extent3d { - width: rgba_width, - height, - depth_or_array_layers: 1, - }; - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("Texture Creation"), - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - view_formats: &[], - }); - - queue.write_texture( - wgpu::ImageCopyTexture { - aspect: wgpu::TextureAspect::All, - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - &rgba, - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(4 * rgba_width), - rows_per_image: Some(height), - }, - size, - ); - - let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &self.texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&texture_view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&self.texture_sampler), - }, - ], - label: Some("texture_bind_group"), - }); + ) -> ImageResource { + ImageResource::new( + backend, + &self.texture_bind_group_layout, + &self.texture_sampler, + transform, + location, + rgba, + rgba_width, + ) + } - { - let (stencil_texture, stencil_index) = stencil_renderer.get_stencil(); - //write image in render pass - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Texture Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &target, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: stencil_texture, - depth_ops: None, - stencil_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - }), - timestamp_writes: None, - occlusion_query_set: None, - }); - render_pass.set_pipeline(&self.texture_pipeline); - render_pass.set_bind_group(0, &bind_group, &[]); - render_pass.set_stencil_reference(stencil_index); - render_pass.set_vertex_buffer(0, self.vertices_buffer.slice(..)); - render_pass.set_index_buffer(self.indices_buffer.slice(..), IndexFormat::Uint16); - render_pass.draw_indexed(0..6, 0, 0..1); - } - queue.submit(std::iter::once(encoder.finish())); + pub fn render_image(&self, render_pass: &mut wgpu::RenderPass, image: &ImageResource) { + render_pass.set_pipeline(&self.texture_pipeline); + render_pass.set_bind_group(0, &image.texture_bind_group, &[]); + render_pass.set_vertex_buffer(0, image.vertices_buffer.slice(..)); + render_pass.set_index_buffer(image.indices_buffer.slice(..), IndexFormat::Uint16); + render_pass.draw_indexed(0..6, 0, 0..1); } } diff --git a/pax-runtime/src/engine/mod.rs b/pax-runtime/src/engine/mod.rs index 1b90942f0..cc93b11c8 100644 --- a/pax-runtime/src/engine/mod.rs +++ b/pax-runtime/src/engine/mod.rs @@ -301,39 +301,13 @@ impl PaxEngine { .unwrap_or(false) { rcs.clear(i); - let circle = Circle::new((300.0, 300.0), 400.0); - rcs.fill(i, circle.into_path(0.1), &Fill::Solid(Color::BLUE)); - let circle = Circle::new((600.0, 500.0), 300.0); - rcs.transform(i, Affine::translate((200.0, 200.0))); - rcs.fill( - i, - circle.into_path(0.1), - &Fill::LinearGradient(LinearGradient { - start: (Size::Percent(0.into()), Size::Percent(0.into())), - end: (Size::Percent(100.0.into()), Size::Percent(100.0.into())), - stops: vec![ - GradientStop { - position: Size::Percent(0.0.into()), - color: Color::RED, - }, - GradientStop { - position: Size::Percent(50.0.into()), - color: Color::BLUE, - }, - GradientStop { - position: Size::Percent(100.0.into()), - color: Color::GREEN, - }, - ], - }), - ); } } // This is pretty useful during debugging - left it here since I use it often. /Sam // crate::api::log(&format!("tree: {:#?}", self.root_node)); - // self.root_expanded_node - // .recurse_render_queue(&mut self.runtime_context, rcs); - // self.runtime_context.recurse_flush_queued_renders(rcs); + self.root_expanded_node + .recurse_render_queue(&mut self.runtime_context, rcs); + self.runtime_context.recurse_flush_queued_renders(rcs); self.runtime_context.clear_all_dirty_canvases(); diff --git a/pax-runtime/src/engine/pax_pixels_render_context.rs b/pax-runtime/src/engine/pax_pixels_render_context.rs index 4a20d7834..ae650f147 100644 --- a/pax-runtime/src/engine/pax_pixels_render_context.rs +++ b/pax-runtime/src/engine/pax_pixels_render_context.rs @@ -103,7 +103,7 @@ impl RenderContext for PaxPixelsRenderer { } fn transform(&mut self, layer: usize, affine: kurbo::Affine) { self.with_layer_context(layer, |context| { - context.transform(Transform2D::from_array( + context.transform(&Transform2D::from_array( affine.as_coeffs().map(|v| v as f32), )) }); @@ -295,7 +295,9 @@ fn to_pax_pixels_fill(fill: &pax_runtime_api::Fill, rect: kurbo::Rect) -> pax_pi pub fn to_pax_pixels_color(color: &pax_runtime_api::Color) -> pax_pixels::Color { let [r, g, b, a] = color.to_rgba_0_1(); - pax_pixels::Color::rgba(r as f32, g as f32, b as f32, a as f32) + pax_pixels::Color { + rgba: [r as f32, g as f32, b as f32, a as f32], + } } pub fn convert_kurbo_to_lyon_path(kurbo_path: &BezPath) -> Path {