From 4c47ba452c9b90fe392c36ba6bbf665da9c45946 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 15:53:41 +0000 Subject: [PATCH 1/3] Allow rendering viewport texture beneath ui texture --- Cargo.lock | 1 + desktop/Cargo.toml | 3 +- desktop/src/app.rs | 2 +- desktop/src/render.rs | 66 +++++++++++++++++----- desktop/src/render/fullscreen_texture.wgsl | 17 +++++- 5 files changed, 71 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f0015bbcc..03ea5524f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1834,6 +1834,7 @@ dependencies = [ name = "graphite-desktop" version = "0.1.0" dependencies = [ + "bytemuck", "cef", "dirs", "futures", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 581f0ed604..30723b7848 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -28,4 +28,5 @@ cef = { workspace = true } include_dir = { workspace = true } tracing-subscriber = { workspace = true } tracing = { workspace = true } -dirs = {workspace = true} +dirs = { workspace = true } +bytemuck = { workspace = true } diff --git a/desktop/src/app.rs b/desktop/src/app.rs index eced6556c1..8a904662db 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -83,7 +83,7 @@ impl ApplicationHandler for WinitApp { match event { CustomEvent::UiUpdate(texture) => { if let Some(graphics_state) = self.graphics_state.as_mut() { - graphics_state.bind_texture(&texture); + graphics_state.bind_ui_texture(&texture); graphics_state.resize(texture.width(), texture.height()); } if let Some(window) = &self.window { diff --git a/desktop/src/render.rs b/desktop/src/render.rs index 5141544666..462cb7a019 100644 --- a/desktop/src/render.rs +++ b/desktop/src/render.rs @@ -79,13 +79,15 @@ impl WgpuContext { .await .unwrap(); + let required_limits = adapter.limits(); + let (device, queue) = adapter .request_device(&wgpu::DeviceDescriptor { - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::default(), label: None, + required_features: wgpu::Features::PUSH_CONSTANTS, + required_limits, memory_hints: Default::default(), - ..Default::default() + trace: wgpu::Trace::Off, }) .await .unwrap(); @@ -99,10 +101,12 @@ pub(crate) struct GraphicsState { surface: wgpu::Surface<'static>, context: WgpuContext, config: wgpu::SurfaceConfiguration, - texture: Option, - bind_group: Option, render_pipeline: wgpu::RenderPipeline, sampler: wgpu::Sampler, + viewport_offset: [f32; 2], + viewport_texture: Option, + ui_texture: Option, + bind_group: Option, } impl GraphicsState { @@ -156,6 +160,16 @@ impl GraphicsState { 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, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, @@ -166,7 +180,10 @@ impl GraphicsState { let render_pipeline_layout = context.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), bind_group_layouts: &[&texture_bind_group_layout], - push_constant_ranges: &[], + push_constant_ranges: &[wgpu::PushConstantRange { + stages: wgpu::ShaderStages::FRAGMENT, + range: 0..std::mem::size_of::<[f32; 2]>() as u32, // 2 floats for viewport offset + }], }); let render_pipeline = context.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -211,10 +228,12 @@ impl GraphicsState { surface, context, config, - texture: None, - bind_group: None, render_pipeline, sampler, + viewport_offset: [0.0, 0.0], + viewport_texture: None, + ui_texture: None, + bind_group: None, } } @@ -226,25 +245,43 @@ impl GraphicsState { } } - pub(crate) fn bind_texture(&mut self, texture: &wgpu::Texture) { - let bind_group = self.create_bindgroup(texture); - self.texture = Some(texture.clone()); + pub(crate) fn bind_ui_texture(&mut self, texture: &wgpu::Texture) { + let bind_group = self.create_bindgroup(texture, &self.viewport_texture.clone().unwrap_or(texture.clone())); + + self.ui_texture = Some(texture.clone()); + + self.bind_group = Some(bind_group); + } + + pub(crate) fn bind_viewport_texture(&mut self, texture: &wgpu::Texture) { + let bind_group = self.create_bindgroup(&self.ui_texture.clone().unwrap_or(texture.clone()), texture); + + self.viewport_texture = Some(texture.clone()); self.bind_group = Some(bind_group); } - fn create_bindgroup(&self, texture: &wgpu::Texture) -> wgpu::BindGroup { - let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + pub(crate) fn set_viewport_offset(&mut self, offset: [f32; 2]) { + self.viewport_offset = offset; + } + + fn create_bindgroup(&self, ui_texture: &wgpu::Texture, viewport_texture: &wgpu::Texture) -> wgpu::BindGroup { + let ui_texture_view = ui_texture.create_view(&wgpu::TextureViewDescriptor::default()); + let viewport_texture_view = viewport_texture.create_view(&wgpu::TextureViewDescriptor::default()); self.context.device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &self.render_pipeline.get_bind_group_layout(0), entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::TextureView(&texture_view), + resource: wgpu::BindingResource::TextureView(&ui_texture_view), }, wgpu::BindGroupEntry { binding: 1, + resource: wgpu::BindingResource::TextureView(&viewport_texture_view), + }, + wgpu::BindGroupEntry { + binding: 2, resource: wgpu::BindingResource::Sampler(&self.sampler), }, ], @@ -275,6 +312,7 @@ impl GraphicsState { }); render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytemuck::cast_slice(&self.viewport_offset)); if let Some(bind_group) = &self.bind_group { render_pass.set_bind_group(0, bind_group, &[]); render_pass.draw(0..6, 0..1); // Draw 3 vertices for fullscreen triangle diff --git a/desktop/src/render/fullscreen_texture.wgsl b/desktop/src/render/fullscreen_texture.wgsl index 216d820063..8cacee633a 100644 --- a/desktop/src/render/fullscreen_texture.wgsl +++ b/desktop/src/render/fullscreen_texture.wgsl @@ -25,12 +25,25 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { return out; } +struct Constants { + viewport_offset: vec2, +}; + +var constants: Constants; + @group(0) @binding(0) -var t_diffuse: texture_2d; +var t_ui: texture_2d; @group(0) @binding(1) +var t_viewport: texture_2d; +@group(0) @binding(2) var s_diffuse: sampler; @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - return textureSample(t_diffuse, s_diffuse, in.tex_coords); + let ui_color: vec4 = textureSample(t_ui, s_diffuse, in.tex_coords); + if (ui_color.a == 1.0) { + return ui_color; + } + let viewport_color: vec4 = textureSample(t_viewport, s_diffuse, in.tex_coords - constants.viewport_offset); + return ui_color * ui_color.a + viewport_color * (1.0 - ui_color.a); } From 223ce1720bfbfeac7ad9ac66fd03cb1f22e1e455 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 16:39:57 +0000 Subject: [PATCH 2/3] Add viewport scale --- desktop/src/render.rs | 25 ++++++++++++++++++++-- desktop/src/render/fullscreen_texture.wgsl | 4 +++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/desktop/src/render.rs b/desktop/src/render.rs index 462cb7a019..5aaf422ee1 100644 --- a/desktop/src/render.rs +++ b/desktop/src/render.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use bytemuck::{Pod, Zeroable}; use thiserror::Error; use winit::window::Window; @@ -103,6 +104,7 @@ pub(crate) struct GraphicsState { config: wgpu::SurfaceConfiguration, render_pipeline: wgpu::RenderPipeline, sampler: wgpu::Sampler, + viewport_scale: [f32; 2], viewport_offset: [f32; 2], viewport_texture: Option, ui_texture: Option, @@ -182,7 +184,7 @@ impl GraphicsState { bind_group_layouts: &[&texture_bind_group_layout], push_constant_ranges: &[wgpu::PushConstantRange { stages: wgpu::ShaderStages::FRAGMENT, - range: 0..std::mem::size_of::<[f32; 2]>() as u32, // 2 floats for viewport offset + range: 0..size_of::() as u32, }], }); @@ -230,6 +232,7 @@ impl GraphicsState { config, render_pipeline, sampler, + viewport_scale: [1.0, 1.0], viewport_offset: [0.0, 0.0], viewport_texture: None, ui_texture: None, @@ -261,6 +264,10 @@ impl GraphicsState { self.bind_group = Some(bind_group); } + pub(crate) fn set_viewport_scale(&mut self, scale: [f32; 2]) { + self.viewport_scale = scale; + } + pub(crate) fn set_viewport_offset(&mut self, offset: [f32; 2]) { self.viewport_offset = offset; } @@ -312,7 +319,14 @@ impl GraphicsState { }); render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytemuck::cast_slice(&self.viewport_offset)); + render_pass.set_push_constants( + wgpu::ShaderStages::FRAGMENT, + 0, + bytemuck::bytes_of(&Constants { + viewport_scale: self.viewport_scale, + viewport_offset: self.viewport_offset, + }), + ); if let Some(bind_group) = &self.bind_group { render_pass.set_bind_group(0, bind_group, &[]); render_pass.draw(0..6, 0..1); // Draw 3 vertices for fullscreen triangle @@ -326,3 +340,10 @@ impl GraphicsState { Ok(()) } } + +#[repr(C)] +#[derive(Copy, Clone, Pod, Zeroable)] +struct Constants { + viewport_scale: [f32; 2], + viewport_offset: [f32; 2], +} diff --git a/desktop/src/render/fullscreen_texture.wgsl b/desktop/src/render/fullscreen_texture.wgsl index 8cacee633a..93dcd43f73 100644 --- a/desktop/src/render/fullscreen_texture.wgsl +++ b/desktop/src/render/fullscreen_texture.wgsl @@ -26,6 +26,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { } struct Constants { + viewport_scale: vec2, viewport_offset: vec2, }; @@ -44,6 +45,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { if (ui_color.a == 1.0) { return ui_color; } - let viewport_color: vec4 = textureSample(t_viewport, s_diffuse, in.tex_coords - constants.viewport_offset); + let viewport_tex_coords = (in.tex_coords - constants.viewport_offset) * constants.viewport_scale; + let viewport_color: vec4 = textureSample(t_viewport, s_diffuse, viewport_tex_coords); return ui_color * ui_color.a + viewport_color * (1.0 - ui_color.a); } From 8595cb1863d2e257e9850c43ac8e0eaa412b68b5 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Mon, 28 Jul 2025 19:00:31 +0200 Subject: [PATCH 3/3] Update desktop/src/render/fullscreen_texture.wgsl --- desktop/src/render/fullscreen_texture.wgsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/render/fullscreen_texture.wgsl b/desktop/src/render/fullscreen_texture.wgsl index 93dcd43f73..cdbe00bf53 100644 --- a/desktop/src/render/fullscreen_texture.wgsl +++ b/desktop/src/render/fullscreen_texture.wgsl @@ -27,7 +27,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { struct Constants { viewport_scale: vec2, - viewport_offset: vec2, + viewport_offset: vec2, }; var constants: Constants;