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..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; @@ -79,13 +80,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 +102,13 @@ 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_scale: [f32; 2], + viewport_offset: [f32; 2], + viewport_texture: Option, + ui_texture: Option, + bind_group: Option, } impl GraphicsState { @@ -156,6 +162,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 +182,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..size_of::() as u32, + }], }); let render_pipeline = context.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -211,10 +230,13 @@ impl GraphicsState { surface, context, config, - texture: None, - bind_group: None, render_pipeline, sampler, + viewport_scale: [1.0, 1.0], + viewport_offset: [0.0, 0.0], + viewport_texture: None, + ui_texture: None, + bind_group: None, } } @@ -226,25 +248,47 @@ 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); } - fn create_bindgroup(&self, texture: &wgpu::Texture) -> wgpu::BindGroup { - let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + 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); + } + + 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; + } + + 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 +319,14 @@ impl GraphicsState { }); render_pass.set_pipeline(&self.render_pipeline); + 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 @@ -288,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 216d820063..cdbe00bf53 100644 --- a/desktop/src/render/fullscreen_texture.wgsl +++ b/desktop/src/render/fullscreen_texture.wgsl @@ -25,12 +25,27 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { return out; } +struct Constants { + viewport_scale: vec2, + 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_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); }