From 5c1e25ec29638d2ac47898cd7d637175c82eadac Mon Sep 17 00:00:00 2001 From: chyyran Date: Tue, 6 Feb 2024 00:29:45 -0500 Subject: [PATCH] rt(wgpu): fix enough stuff to get it to draw a frame --- librashader-runtime-wgpu/src/buffer.rs | 26 ++-- librashader-runtime-wgpu/src/draw_quad.rs | 6 +- librashader-runtime-wgpu/src/filter_chain.rs | 143 ++++++++++------- librashader-runtime-wgpu/src/filter_pass.rs | 108 +++++++------ librashader-runtime-wgpu/src/framebuffer.rs | 12 +- .../src/graphics_pipeline.rs | 130 ++++++++-------- librashader-runtime-wgpu/src/lib.rs | 9 +- librashader-runtime-wgpu/src/luts.rs | 19 ++- librashader-runtime-wgpu/src/options.rs | 2 +- librashader-runtime-wgpu/src/samplers.rs | 13 +- librashader-runtime-wgpu/src/texture.rs | 68 ++++---- .../tests/hello_triangle.rs | 147 +++++++++++++----- 12 files changed, 400 insertions(+), 283 deletions(-) diff --git a/librashader-runtime-wgpu/src/buffer.rs b/librashader-runtime-wgpu/src/buffer.rs index a41352f8..486aa791 100644 --- a/librashader-runtime-wgpu/src/buffer.rs +++ b/librashader-runtime-wgpu/src/buffer.rs @@ -1,26 +1,27 @@ use std::ops::{Deref, DerefMut}; use std::sync::Arc; +use parking_lot::RwLock; -pub struct WgpuMappedBuffer { +pub struct WgpuStagedBuffer { buffer: wgpu::Buffer, shadow: Box<[u8]>, } -impl WgpuMappedBuffer { +impl WgpuStagedBuffer { pub fn new( device: &Arc, usage: wgpu::BufferUsages, size: wgpu::BufferAddress, - label: wgpu::Label<'static> - ) -> WgpuMappedBuffer { + label: wgpu::Label<'static>, + ) -> WgpuStagedBuffer { let buffer = device.create_buffer(&wgpu::BufferDescriptor { label, size, - usage, - mapped_at_creation: true, + usage: usage | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, }); - WgpuMappedBuffer { + WgpuStagedBuffer { buffer, shadow: vec![0u8; size as usize].into_boxed_slice(), } @@ -31,13 +32,12 @@ impl WgpuMappedBuffer { } /// Write the contents of the backing buffer to the device buffer. - pub fn flush(&self) { - self.buffer.slice(..) - .get_mapped_range_mut().copy_from_slice(&self.shadow) + pub fn flush(&self, queue: &wgpu::Queue) { + queue.write_buffer(&self.buffer, 0, &self.shadow); } } -impl Deref for WgpuMappedBuffer { +impl Deref for WgpuStagedBuffer { type Target = [u8]; fn deref(&self) -> &Self::Target { @@ -45,8 +45,8 @@ impl Deref for WgpuMappedBuffer { } } -impl DerefMut for WgpuMappedBuffer { +impl DerefMut for WgpuStagedBuffer { fn deref_mut(&mut self) -> &mut Self::Target { self.shadow.deref_mut() } -} \ No newline at end of file +} diff --git a/librashader-runtime-wgpu/src/draw_quad.rs b/librashader-runtime-wgpu/src/draw_quad.rs index 32f3a096..d59dfb9e 100644 --- a/librashader-runtime-wgpu/src/draw_quad.rs +++ b/librashader-runtime-wgpu/src/draw_quad.rs @@ -28,7 +28,7 @@ pub struct DrawQuad { } impl DrawQuad { - pub fn new(device: &Device, queue: &mut Queue) -> DrawQuad { + pub fn new(device: &Device) -> DrawQuad { let buffer = device.create_buffer_init(&BufferInitDescriptor { label: Some("librashader vbo"), contents: bytemuck::cast_slice(&VBO_DATA), @@ -38,11 +38,9 @@ impl DrawQuad { DrawQuad { buffer } } - pub fn bind_vbo_for_frame<'a, 'b: 'a>(&'b self, cmd: &mut RenderPass<'a>) { + pub fn draw_quad<'a, 'b: 'a>(&'b self, cmd: &mut RenderPass<'a>, vbo: QuadType) { cmd.set_vertex_buffer(0, self.buffer.slice(0..)); - } - pub fn draw_quad<'a, 'b: 'a>(&'b self, cmd: &mut RenderPass<'a>, vbo: QuadType) { let offset = match vbo { QuadType::Offscreen => 0..3, QuadType::Final => 1..4, diff --git a/librashader-runtime-wgpu/src/filter_chain.rs b/librashader-runtime-wgpu/src/filter_chain.rs index 43cce563..5e6e36e0 100644 --- a/librashader-runtime-wgpu/src/filter_chain.rs +++ b/librashader-runtime-wgpu/src/filter_chain.rs @@ -16,20 +16,22 @@ use std::convert::Infallible; use std::path::Path; use std::sync::Arc; +use crate::buffer::WgpuStagedBuffer; +use crate::draw_quad::DrawQuad; use librashader_common::{ImageFormat, Size, Viewport}; use librashader_reflect::back::wgsl::WgslCompileOptions; use librashader_runtime::framebuffer::FramebufferInit; use librashader_runtime::render_target::RenderTarget; use librashader_runtime::scaling::ScaleFramebuffer; use rayon::prelude::*; -use wgpu::{BindGroupEntry, CommandBuffer, CommandEncoder, Device, Queue, TextureAspect, TextureFormat}; -use crate::buffer::WgpuMappedBuffer; -use crate::draw_quad::DrawQuad; +use wgpu::{ + BindGroupEntry, CommandBuffer, CommandEncoder, Device, Queue, TextureAspect, TextureFormat, +}; use crate::error; use crate::error::FilterChainError; use crate::filter_pass::FilterPass; -use crate::framebuffer::OutputImage; +use crate::framebuffer::OutputView; use crate::graphics_pipeline::WgpuGraphicsPipeline; use crate::luts::LutTexture; use crate::options::FrameOptionsWGPU; @@ -73,6 +75,7 @@ pub(crate) struct FilterCommon { pub internal_frame_count: i32, pub(crate) draw_quad: DrawQuad, device: Arc, + pub(crate) queue: Arc } impl FilterChainWGPU { @@ -85,7 +88,7 @@ impl FilterChainWGPU { /// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame). pub fn load_from_preset_deferred( device: Arc, - queue: &mut wgpu::Queue, + queue: Arc, cmd: &mut wgpu::CommandEncoder, preset: ShaderPreset, ) -> error::Result { @@ -94,11 +97,17 @@ impl FilterChainWGPU { // // initialize passes let filters = Self::init_passes(Arc::clone(&device), passes, &semantics)?; // - let luts = FilterChainWGPU::load_luts(&device, queue, cmd, &preset.textures)?; + let luts = FilterChainWGPU::load_luts(&device, &queue, cmd, &preset.textures)?; let samplers = SamplerSet::new(&device); // - let framebuffer_gen = - || Ok::<_, error::FilterChainError>(OwnedImage::new(Arc::clone(&device), Size::new(1, 1), 1, ImageFormat::R8G8B8A8Unorm)); + let framebuffer_gen = || { + Ok::<_, error::FilterChainError>(OwnedImage::new( + Arc::clone(&device), + Size::new(1, 1), + 1, + ImageFormat::R8G8B8A8Unorm, + )) + }; let input_gen = || None; let framebuffer_init = FramebufferInit::new( filters.iter().map(|f| &f.reflection.meta), @@ -122,39 +131,39 @@ impl FilterChainWGPU { // FrameResiduals::new(&device.device) // }); - let draw_quad = DrawQuad::new(&device, queue); + let draw_quad = DrawQuad::new(&device); Ok(FilterChainWGPU { common: FilterCommon { luts, samplers, - config: FilterMutable { - passes_enabled: preset.shader_count as usize, - parameters: preset - .parameters - .into_iter() - .map(|param| (param.name, param.value)) - .collect(), - }, - draw_quad, - device, - output_textures, - feedback_textures, - history_textures, - internal_frame_count: 0, - + config: FilterMutable { + passes_enabled: preset.shader_count as usize, + parameters: preset + .parameters + .into_iter() + .map(|param| (param.name, param.value)) + .collect(), + }, + draw_quad, + device, + queue, + output_textures, + feedback_textures, + history_textures, + internal_frame_count: 0, }, passes: filters, - output_framebuffers, - feedback_framebuffers, - history_framebuffers, - disable_mipmaps: false // todo: force no mipmaps, + output_framebuffers, + feedback_framebuffers, + history_framebuffers, + disable_mipmaps: false, // todo: force no mipmaps, }) } fn load_luts( device: &wgpu::Device, - queue: &mut wgpu::Queue, + queue: &wgpu::Queue, cmd: &mut wgpu::CommandEncoder, textures: &[TextureConfig], ) -> error::Result> { @@ -170,6 +179,29 @@ impl FilterChainWGPU { Ok(luts) } + fn push_history(&mut self, input: &wgpu::Texture, cmd: &mut wgpu::CommandEncoder) { + if let Some(mut back) = self.history_framebuffers.pop_back() { + if back.image.size() != input.size() || input.format() != back.image.format() { + // old back will get dropped.. do we need to defer? + let _old_back = std::mem::replace( + &mut back, + OwnedImage::new( + Arc::clone(&self.common.device), + input.size().into(), + 1, + input.format().into(), + ), + ); + } + + unsafe { + back.copy_from(cmd, input); + } + + self.history_framebuffers.push_front(back) + } + } + fn init_passes( device: Arc, passes: Vec, @@ -193,10 +225,19 @@ impl FilterChainWGPU { .as_ref() .map_or(0, |push| push.size as wgpu::BufferAddress); - let uniform_storage = UniformStorage::new_with_storage( - WgpuMappedBuffer::new(&device, wgpu::BufferUsages::UNIFORM, ubo_size as wgpu::BufferAddress, Some("ubo")), - WgpuMappedBuffer::new(&device, wgpu::BufferUsages::UNIFORM, push_size as wgpu::BufferAddress, Some("push")) + WgpuStagedBuffer::new( + &device, + wgpu::BufferUsages::UNIFORM, + ubo_size as wgpu::BufferAddress, + Some("ubo"), + ), + WgpuStagedBuffer::new( + &device, + wgpu::BufferUsages::UNIFORM, + push_size as wgpu::BufferAddress, + Some("push"), + ), ); let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); @@ -212,11 +253,9 @@ impl FilterChainWGPU { Arc::clone(&device), &wgsl, &reflection, - render_pass_format.unwrap_or(TextureFormat::R8Unorm), + render_pass_format.unwrap_or(TextureFormat::Rgba8Unorm), ); - - Ok(FilterPass { device: Arc::clone(&device), reflection, @@ -235,13 +274,13 @@ impl FilterChainWGPU { Ok(filters.into_boxed_slice()) } - pub fn frame<'a>(&mut self, + pub fn frame<'a>( + &mut self, input: Arc, - viewport: &Viewport>, + viewport: &Viewport>, cmd: &mut wgpu::CommandEncoder, frame_count: usize, - options: Option<&FrameOptionsWGPU>, - + options: Option<&FrameOptionsWGPU>, ) -> error::Result<()> { let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled); let passes = &mut self.passes[0..max]; @@ -249,7 +288,7 @@ impl FilterChainWGPU { if let Some(options) = &options { if options.clear_history { for history in &mut self.history_framebuffers { - // history.clear(cmd); + history.clear(cmd); } } } @@ -258,21 +297,11 @@ impl FilterChainWGPU { return Ok(()); } - let original_image_view = input.create_view(&wgpu::TextureViewDescriptor { - label: Some("original_image_view"), - format: Some(input.format()), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: TextureAspect::All, - base_mip_level: 1, - mip_level_count: None, - base_array_layer: 1, - array_layer_count: None, - }); + let original_image_view = input.create_view(&wgpu::TextureViewDescriptor::default()); let filter = passes[0].config.filter; let wrap_mode = passes[0].config.wrap_mode; - // update history for (texture, image) in self .common @@ -325,14 +354,13 @@ impl FilterChainWGPU { let frame_direction = options.map_or(1, |f| f.frame_direction); - for (index, pass) in pass.iter_mut().enumerate() { let target = &self.output_framebuffers[index]; source.filter_mode = pass.config.filter; source.wrap_mode = pass.config.wrap_mode; source.mip_filter = pass.config.filter; - let output_image = OutputImage::new(target); + let output_image = OutputView::new(target); let out = RenderTarget::identity(&output_image); pass.draw( @@ -345,11 +373,11 @@ impl FilterChainWGPU { &original, &source, &out, - QuadType::Offscreen + QuadType::Offscreen, )?; if target.max_miplevels > 1 && !self.disable_mipmaps { - // todo: mipmaps + target.generate_mipmaps(cmd); } source = self.common.output_textures[index].clone().unwrap(); @@ -378,13 +406,12 @@ impl FilterChainWGPU { &original, &source, &out, - QuadType::Final + QuadType::Final, )?; } - // self.push_history(input, cmd)?; + self.push_history(&input, cmd); self.common.internal_frame_count = self.common.internal_frame_count.wrapping_add(1); Ok(()) } - } diff --git a/librashader-runtime-wgpu/src/filter_pass.rs b/librashader-runtime-wgpu/src/filter_pass.rs index 151feff8..53a128e1 100644 --- a/librashader-runtime-wgpu/src/filter_pass.rs +++ b/librashader-runtime-wgpu/src/filter_pass.rs @@ -1,37 +1,42 @@ +use crate::buffer::WgpuStagedBuffer; +use crate::error; +use crate::filter_chain::FilterCommon; +use crate::framebuffer::OutputView; use crate::graphics_pipeline::WgpuGraphicsPipeline; +use crate::samplers::SamplerSet; +use crate::texture::{InputImage, OwnedImage}; +use librashader_common::{ImageFormat, Size, Viewport}; use librashader_preprocess::ShaderSource; use librashader_presets::ShaderPassConfig; use librashader_reflect::back::wgsl::NagaWgslContext; use librashader_reflect::back::ShaderCompilerOutput; -use librashader_reflect::reflect::semantics::{BindingStage, MemberOffset, TextureBinding, UniformBinding}; +use librashader_reflect::reflect::semantics::{ + BindingStage, MemberOffset, TextureBinding, UniformBinding, +}; use librashader_reflect::reflect::ShaderReflection; -use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess}; -use rustc_hash::FxHashMap; -use std::sync::Arc; -use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferUsages, RenderPass, ShaderStages, TextureView}; -use wgpu::util::{BufferInitDescriptor, DeviceExt}; -use librashader_common::{ImageFormat, Size, Viewport}; use librashader_runtime::binding::{BindSemantics, TextureInput}; use librashader_runtime::filter_pass::FilterPassMeta; use librashader_runtime::quad::QuadType; use librashader_runtime::render_target::RenderTarget; -use crate::buffer::WgpuMappedBuffer; -use crate::error; -use crate::filter_chain::FilterCommon; -use crate::framebuffer::OutputImage; -use crate::samplers::SamplerSet; -use crate::texture::{InputImage, OwnedImage}; +use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess}; +use rustc_hash::FxHashMap; +use std::sync::Arc; +use wgpu::util::{BufferInitDescriptor, DeviceExt}; +use wgpu::{ + BindGroupDescriptor, BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferUsages, + RenderPass, ShaderStages, TextureView, +}; pub struct FilterPass { pub device: Arc, pub reflection: ShaderReflection, pub(crate) compiled: ShaderCompilerOutput, - pub(crate) uniform_storage: UniformStorage, WgpuMappedBuffer, WgpuMappedBuffer>, + pub(crate) uniform_storage: + UniformStorage, WgpuStagedBuffer, WgpuStagedBuffer>, pub uniform_bindings: FxHashMap, pub source: ShaderSource, pub config: ShaderPassConfig, pub graphics_pipeline: WgpuGraphicsPipeline, - } impl TextureInput for InputImage { @@ -42,10 +47,10 @@ impl TextureInput for InputImage { pub struct WgpuArcBinding { binding: u32, - resource: Arc + resource: Arc, } -impl BindSemantics, WgpuMappedBuffer, WgpuMappedBuffer> for FilterPass { +impl BindSemantics, WgpuStagedBuffer, WgpuStagedBuffer> for FilterPass { type InputTexture = InputImage; type SamplerSet = SamplerSet; type DescriptorSet<'a> = ( @@ -66,15 +71,21 @@ impl BindSemantics, WgpuMappedBuffer, WgpuMappedBuff let sampler = samplers.get(texture.wrap_mode, texture.filter_mode, texture.mip_filter); let (texture_binding, sampler_binding) = descriptors; - texture_binding.insert(binding.binding, WgpuArcBinding { - binding: binding.binding, - resource: Arc::clone(&texture.view) - }); + texture_binding.insert( + binding.binding, + WgpuArcBinding { + binding: binding.binding, + resource: Arc::clone(&texture.view), + }, + ); - sampler_binding.insert(binding.binding, WgpuArcBinding { - binding: binding.binding, - resource: sampler, - }); + sampler_binding.insert( + binding.binding, + WgpuArcBinding { + binding: binding.binding, + resource: sampler, + }, + ); } } @@ -86,13 +97,12 @@ impl FilterPass { parent: &FilterCommon, frame_count: u32, frame_direction: i32, - viewport: &Viewport, + viewport: &Viewport, original: &InputImage, source: &InputImage, - output: &RenderTarget, + output: &RenderTarget, vbo_type: QuadType, ) -> error::Result<()> { - let mut main_heap = FxHashMap::default(); let mut sampler_heap = FxHashMap::default(); @@ -110,21 +120,20 @@ impl FilterPass { &mut sampler_heap, ); - let mut main_heap_array = Vec::with_capacity(main_heap.len() + 1); let mut sampler_heap_array = Vec::with_capacity(sampler_heap.len() + 1); for binding in main_heap.values() { main_heap_array.push(BindGroupEntry { binding: binding.binding, - resource: BindingResource::TextureView(&binding.resource) + resource: BindingResource::TextureView(&binding.resource), }) } for binding in sampler_heap.values() { sampler_heap_array.push(BindGroupEntry { binding: binding.binding, - resource: BindingResource::Sampler(&binding.resource) + resource: BindingResource::Sampler(&binding.resource), }) } @@ -157,31 +166,24 @@ impl FilterPass { let main_bind_group = self.device.create_bind_group(&BindGroupDescriptor { label: Some("main bind group"), layout: &self.graphics_pipeline.layout.main_bind_group_layout, - entries: &main_heap_array + entries: &main_heap_array, }); let sampler_bind_group = self.device.create_bind_group(&BindGroupDescriptor { label: Some("sampler bind group"), layout: &self.graphics_pipeline.layout.sampler_bind_group_layout, - entries: &sampler_heap_array + entries: &sampler_heap_array, }); - let mut render_pass = self.graphics_pipeline - .begin_rendering(output, cmd); + let mut render_pass = self.graphics_pipeline.begin_rendering(output, cmd); - render_pass.set_bind_group( - 0, - &main_bind_group, - &[] - ); + render_pass.set_bind_group(0, &main_bind_group, &[]); - render_pass.set_bind_group( - 1, - &sampler_bind_group, - &[] - ); + render_pass.set_bind_group(1, &sampler_bind_group, &[]); - if let Some(push) = &self.reflection.push_constant && !has_pcb_buffer { + if let Some(push) = &self.reflection.push_constant + && !has_pcb_buffer + { let mut stage_mask = ShaderStages::empty(); if push.stage_mask.contains(BindingStage::FRAGMENT) { stage_mask |= ShaderStages::FRAGMENT; @@ -189,11 +191,7 @@ impl FilterPass { if push.stage_mask.contains(BindingStage::VERTEX) { stage_mask |= ShaderStages::VERTEX; } - render_pass.set_push_constants( - stage_mask, - 0, - self.uniform_storage.push_slice() - ) + render_pass.set_push_constants(stage_mask, 0, self.uniform_storage.push_slice()) } parent.draw_quad.draw_quad(&mut render_pass, vbo_type); @@ -213,7 +211,7 @@ impl FilterPass { original: &InputImage, source: &InputImage, main_heap: &'a mut FxHashMap>, - sampler_heap: &'a mut FxHashMap> + sampler_heap: &'a mut FxHashMap>, ) { Self::bind_semantics( &self.device, @@ -240,8 +238,8 @@ impl FilterPass { ); // flush to buffers - self.uniform_storage.inner_ubo().flush(); - self.uniform_storage.inner_push().flush(); + self.uniform_storage.inner_ubo().flush(&parent.queue); + self.uniform_storage.inner_push().flush(&parent.queue); } } @@ -253,4 +251,4 @@ impl FilterPassMeta for FilterPass { fn config(&self) -> &ShaderPassConfig { &self.config } -} \ No newline at end of file +} diff --git a/librashader-runtime-wgpu/src/framebuffer.rs b/librashader-runtime-wgpu/src/framebuffer.rs index 2e40e381..fc96e13d 100644 --- a/librashader-runtime-wgpu/src/framebuffer.rs +++ b/librashader-runtime-wgpu/src/framebuffer.rs @@ -1,18 +1,20 @@ -use librashader_common::Size; use crate::texture::OwnedImage; +use librashader_common::Size; -pub struct OutputImage<'a> { +pub struct OutputView<'a> { pub size: Size, pub view: &'a wgpu::TextureView, pub format: wgpu::TextureFormat, } -impl<'a> OutputImage<'a> { +impl<'a> OutputView<'a> { pub fn new(image: &'a OwnedImage) -> Self { Self { size: image.size, view: &image.view, - format: image.image.format() + format: image.image.format(), } } -} \ No newline at end of file +} + + diff --git a/librashader-runtime-wgpu/src/graphics_pipeline.rs b/librashader-runtime-wgpu/src/graphics_pipeline.rs index ec2db5dd..67d0c725 100644 --- a/librashader-runtime-wgpu/src/graphics_pipeline.rs +++ b/librashader-runtime-wgpu/src/graphics_pipeline.rs @@ -1,17 +1,24 @@ +use crate::framebuffer::OutputView; use crate::{error, util}; use librashader_reflect::back::wgsl::NagaWgslContext; use librashader_reflect::back::ShaderCompilerOutput; use librashader_reflect::reflect::ShaderReflection; +use librashader_runtime::render_target::RenderTarget; use std::borrow::Cow; use std::sync::Arc; -use wgpu::{BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, CommandEncoder, Device, Operations, PipelineLayout, PushConstantRange, RenderPass, RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, SamplerBindingType, ShaderModule, ShaderSource, ShaderStages, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute, VertexBufferLayout}; -use librashader_runtime::render_target::RenderTarget; -use crate::framebuffer::OutputImage; +use wgpu::{ + BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, CommandEncoder, Device, + Operations, PipelineLayout, PushConstantRange, RenderPass, RenderPassColorAttachment, + RenderPassDescriptor, RenderPipelineDescriptor, SamplerBindingType, ShaderModule, ShaderSource, + ShaderStages, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute, + VertexBufferLayout, +}; pub struct WgpuGraphicsPipeline { pub layout: PipelineLayoutObjects, render_pipeline: wgpu::RenderPipeline, - pub format: wgpu::TextureFormat + pub format: wgpu::TextureFormat, } pub struct PipelineLayoutObjects { @@ -22,7 +29,7 @@ pub struct PipelineLayoutObjects { vertex_entry_name: String, vertex: ShaderModule, fragment: ShaderModule, - device: Arc + device: Arc, } impl PipelineLayoutObjects { @@ -138,55 +145,56 @@ impl PipelineLayoutObjects { } pub fn create_pipeline(&self, framebuffer_format: TextureFormat) -> wgpu::RenderPipeline { - self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&self.layout), - vertex: wgpu::VertexState { - module: &self.vertex, - entry_point: &self.vertex_entry_name, - buffers: &[VertexBufferLayout { - array_stride: 4 * std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }, - wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: (2 * std::mem::size_of::()) as wgpu::BufferAddress, - shader_location: 1, - }, - ], - }], - }, - fragment: Some(wgpu::FragmentState { - module: &self.fragment, - entry_point: &self.fragment_entry_name, - targets: &[Some(wgpu::ColorTargetState { - format: framebuffer_format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleStrip, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - unclipped_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }) + self.device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&self.layout), + vertex: wgpu::VertexState { + module: &self.vertex, + entry_point: &self.vertex_entry_name, + buffers: &[VertexBufferLayout { + array_stride: 4 * std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + format: wgpu::VertexFormat::Float32x2, + offset: 0, + shader_location: 0, + }, + wgpu::VertexAttribute { + format: wgpu::VertexFormat::Float32x2, + offset: (2 * std::mem::size_of::()) as wgpu::BufferAddress, + shader_location: 1, + }, + ], + }], + }, + fragment: Some(wgpu::FragmentState { + module: &self.fragment, + entry_point: &self.fragment_entry_name, + targets: &[Some(wgpu::ColorTargetState { + format: framebuffer_format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + unclipped_depth: false, + polygon_mode: wgpu::PolygonMode::Fill, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }) } } @@ -202,7 +210,7 @@ impl WgpuGraphicsPipeline { Self { layout, render_pipeline, - format: render_pass_format + format: render_pass_format, } } @@ -213,8 +221,8 @@ impl WgpuGraphicsPipeline { pub(crate) fn begin_rendering<'pass>( &'pass self, - output: &RenderTarget<'pass, OutputImage>, - cmd: &'pass mut CommandEncoder + output: &RenderTarget<'pass, OutputView>, + cmd: &'pass mut CommandEncoder, ) -> RenderPass<'pass> { let mut render_pass = cmd.begin_render_pass(&RenderPassDescriptor { label: Some("librashader"), @@ -240,19 +248,19 @@ impl WgpuGraphicsPipeline { output.x as u32, output.y as u32, output.output.size.width, - output.output.size.height + output.output.size.height, ); - + render_pass.set_viewport( output.x, output.y, output.output.size.width as f32, output.output.size.height as f32, 1.0, - 1.0 + 1.0, ); + render_pass.set_pipeline(&self.render_pipeline); render_pass } - } diff --git a/librashader-runtime-wgpu/src/lib.rs b/librashader-runtime-wgpu/src/lib.rs index fcb373af..88032f50 100644 --- a/librashader-runtime-wgpu/src/lib.rs +++ b/librashader-runtime-wgpu/src/lib.rs @@ -7,18 +7,19 @@ #![feature(let_chains)] #![feature(strict_provenance)] +mod buffer; mod draw_quad; mod error; mod filter_chain; mod filter_pass; +mod framebuffer; mod graphics_pipeline; +mod luts; +mod options; mod samplers; mod texture; mod util; -mod framebuffer; -mod luts; -mod options; -mod buffer; +pub use framebuffer::OutputView; pub use filter_chain::FilterChainWGPU; pub use filter_pass::FilterPass; diff --git a/librashader-runtime-wgpu/src/luts.rs b/librashader-runtime-wgpu/src/luts.rs index eeff3cc2..6e2f1d50 100644 --- a/librashader-runtime-wgpu/src/luts.rs +++ b/librashader-runtime-wgpu/src/luts.rs @@ -1,10 +1,10 @@ -use std::sync::Arc; -use wgpu::{ImageDataLayout, Label, TextureDescriptor}; -use wgpu::util::DeviceExt; +use crate::texture::{Handle, InputImage}; use librashader_presets::TextureConfig; -use librashader_runtime::image::{BGRA8, Image}; +use librashader_runtime::image::{Image, BGRA8}; use librashader_runtime::scaling::MipmapSize; -use crate::texture::{Handle, InputImage}; +use std::sync::Arc; +use wgpu::util::DeviceExt; +use wgpu::{ImageDataLayout, Label, TextureDescriptor}; pub(crate) struct LutTexture(InputImage); impl AsRef for LutTexture { @@ -16,10 +16,10 @@ impl AsRef for LutTexture { impl LutTexture { pub fn new( device: &wgpu::Device, - queue: &mut wgpu::Queue, + queue: &wgpu::Queue, _cmd: &mut wgpu::CommandEncoder, image: Image, - config: &TextureConfig + config: &TextureConfig, ) -> LutTexture { let texture = device.create_texture(&TextureDescriptor { label: Some(&config.name), @@ -39,7 +39,6 @@ impl LutTexture { view_formats: &[wgpu::TextureFormat::Rgba8Unorm], }); - queue.write_texture( wgpu::ImageCopyTexture { texture: &texture, @@ -53,7 +52,7 @@ impl LutTexture { bytes_per_row: Some(4 * image.size.width), rows_per_image: None, }, - image.size.into() + image.size.into(), ); // todo: mipmaps @@ -80,4 +79,4 @@ impl LutTexture { Self(image) } -} \ No newline at end of file +} diff --git a/librashader-runtime-wgpu/src/options.rs b/librashader-runtime-wgpu/src/options.rs index a685c853..2ea17c8f 100644 --- a/librashader-runtime-wgpu/src/options.rs +++ b/librashader-runtime-wgpu/src/options.rs @@ -7,4 +7,4 @@ pub struct FrameOptionsWGPU { /// The direction of rendering. /// -1 indicates that the frames are played in reverse order. pub frame_direction: i32, -} \ No newline at end of file +} diff --git a/librashader-runtime-wgpu/src/samplers.rs b/librashader-runtime-wgpu/src/samplers.rs index e93e29d7..cfba9129 100644 --- a/librashader-runtime-wgpu/src/samplers.rs +++ b/librashader-runtime-wgpu/src/samplers.rs @@ -16,9 +16,12 @@ impl SamplerSet { // SAFETY: the sampler set is complete for the matrix // wrap x filter x mipmap unsafe { - Arc::clone(&self.samplers - .get(&(wrap, filter, mipmap)) - .unwrap_unchecked()) + Arc::clone( + &self + .samplers + .get(&(wrap, filter, mipmap)) + .unwrap_unchecked(), + ) } } @@ -48,8 +51,8 @@ impl SamplerSet { compare: None, anisotropy_clamp: 1, border_color: Some(SamplerBorderColor::TransparentBlack), - }), - )); + })), + ); } } } diff --git a/librashader-runtime-wgpu/src/texture.rs b/librashader-runtime-wgpu/src/texture.rs index e950f978..e9ff5114 100644 --- a/librashader-runtime-wgpu/src/texture.rs +++ b/librashader-runtime-wgpu/src/texture.rs @@ -1,10 +1,10 @@ -use std::ops::Deref; -use std::sync::Arc; -use wgpu::{TextureFormat, TextureView}; +use crate::error::FilterChainError; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_presets::Scale2D; use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize}; -use crate::error::FilterChainError; +use std::ops::Deref; +use std::sync::Arc; +use wgpu::{ImageCopyTexture, TextureFormat, TextureView}; pub struct OwnedImage { device: Arc, @@ -17,14 +17,14 @@ pub struct OwnedImage { pub enum Handle<'a, T> { Borrowed(&'a T), - Owned(Arc) + Owned(Arc), } impl Clone for Handle<'_, T> { fn clone(&self) -> Self { match self { Handle::Borrowed(r) => Handle::Borrowed(r), - Handle::Owned(r) => Handle::Owned(Arc::clone(r)) + Handle::Owned(r) => Handle::Owned(Arc::clone(r)), } } } @@ -34,7 +34,7 @@ impl Deref for Handle<'_, T> { fn deref(&self) -> &Self::Target { match self { Handle::Borrowed(r) => &r, - Handle::Owned(r) => &r + Handle::Owned(r) => &r, } } } @@ -56,12 +56,12 @@ impl AsRef for InputImage { } impl OwnedImage { - pub fn new(device: Arc, - size: Size, - max_miplevels: u32, + pub fn new( + device: Arc, + size: Size, + max_miplevels: u32, format: ImageFormat, ) -> Self { - let format: Option = format.into(); let format = format.unwrap_or(wgpu::TextureFormat::Rgba8Unorm); @@ -117,7 +117,12 @@ impl OwnedImage { || (!mipmap && self.max_miplevels != 1) || format != self.image.format() { - let mut new = OwnedImage::new(Arc::clone(&self.device), size, self.max_miplevels, format.into()); + let mut new = OwnedImage::new( + Arc::clone(&self.device), + size, + self.max_miplevels, + format.into(), + ); std::mem::swap(self, &mut new); } size @@ -132,25 +137,34 @@ impl OwnedImage { mip_filter: filter, } } + + pub fn copy_from(&self, cmd: &mut wgpu::CommandEncoder, source: &wgpu::Texture) { + cmd.copy_texture_to_texture( + source.as_image_copy(), + self.image.as_image_copy(), + source.size(), + ) + } + + pub fn clear(&self, cmd: &mut wgpu::CommandEncoder) { + cmd.clear_texture(&self.image, &wgpu::ImageSubresourceRange::default()); + } + pub fn generate_mipmaps(&self, cmd: &mut wgpu::CommandEncoder) {} } impl ScaleFramebuffer for OwnedImage { type Error = FilterChainError; type Context = (); - fn scale(&mut self, - scaling: Scale2D, - format: ImageFormat, - viewport_size: &Size, - source_size: &Size, - should_mipmap: bool, - _context: &Self::Context) -> Result, Self::Error> { - Ok(self.scale( - scaling, - format, - viewport_size, - source_size, - should_mipmap, - )) + fn scale( + &mut self, + scaling: Scale2D, + format: ImageFormat, + viewport_size: &Size, + source_size: &Size, + should_mipmap: bool, + _context: &Self::Context, + ) -> Result, Self::Error> { + Ok(self.scale(scaling, format, viewport_size, source_size, should_mipmap)) } -} \ No newline at end of file +} diff --git a/librashader-runtime-wgpu/tests/hello_triangle.rs b/librashader-runtime-wgpu/tests/hello_triangle.rs index d9b05d60..9fa93e50 100644 --- a/librashader-runtime-wgpu/tests/hello_triangle.rs +++ b/librashader-runtime-wgpu/tests/hello_triangle.rs @@ -1,5 +1,5 @@ use std::sync::Arc; -use wgpu::{ Maintain, }; +use wgpu::Maintain; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, @@ -12,6 +12,7 @@ use wgpu::util::DeviceExt; use winit::event_loop::EventLoopBuilder; use winit::keyboard::{Key, KeyCode, PhysicalKey}; use winit::platform::windows::EventLoopBuilderExtWindows; +use librashader_common::Viewport; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -64,7 +65,7 @@ const VERTICES: &[Vertex] = &[ struct State<'a> { surface: wgpu::Surface<'a>, device: Arc, - queue: wgpu::Queue, + queue: Arc, config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize, clear_color: wgpu::Color, @@ -74,6 +75,7 @@ struct State<'a> { vertex_buffer: wgpu::Buffer, num_vertices: u32, chain: FilterChainWGPU, + frame_count: usize } impl<'a> State<'a> { async fn new(window: &'a Window) -> Self { @@ -91,7 +93,7 @@ impl<'a> State<'a> { .await .unwrap(); - let (device, mut queue) = adapter + let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER, @@ -106,7 +108,7 @@ impl<'a> State<'a> { let swapchain_format = swapchain_capabilities.formats[0]; let mut config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, format: swapchain_format, width: size.width, height: size.height, @@ -117,16 +119,22 @@ impl<'a> State<'a> { }; let device = Arc::new(device); + let queue = Arc::new(queue); let mut cmd = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("start encoder"), }); let preset = - ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap(); + ShaderPreset::try_parse("../test/basic.slangp").unwrap(); - - let chain = FilterChainWGPU::load_from_preset_deferred(Arc::clone(&device), &mut queue, &mut cmd, preset).unwrap(); + let chain = FilterChainWGPU::load_from_preset_deferred( + Arc::clone(&device), + Arc::clone(&queue), + &mut cmd, + preset, + ) + .unwrap(); let cmd = cmd.finish(); @@ -202,6 +210,7 @@ impl<'a> State<'a> { vertex_buffer, num_vertices, chain, + frame_count: 0, } } fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { @@ -218,9 +227,46 @@ impl<'a> State<'a> { fn update(&mut self) {} fn render(&mut self) -> Result<(), wgpu::SurfaceError> { let output = self.surface.get_current_texture()?; - let view = output - .texture + + let render_output = Arc::new(self.device.create_texture( + &wgpu::TextureDescriptor { + label: Some("rendertexture"), + size: output.texture.size(), + mip_level_count: output.texture.mip_level_count(), + sample_count: output.texture.sample_count(), + dimension: output.texture.dimension(), + format: output.texture.format(), + usage: wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::COPY_SRC, + view_formats: &[output.texture.format()], + } + )); + + let filter_output = Arc::new(self.device.create_texture( + &wgpu::TextureDescriptor { + label: Some("filteroutput"), + size: output.texture.size(), + mip_level_count: output.texture.mip_level_count(), + sample_count: output.texture.sample_count(), + dimension: output.texture.dimension(), + format: output.texture.format(), + usage: wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::COPY_SRC, + view_formats: &[output.texture.format()], + } + )); + + + let view = render_output .create_view(&wgpu::TextureViewDescriptor::default()); + + let filter_view = filter_output + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = self .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { @@ -228,7 +274,8 @@ impl<'a> State<'a> { }); { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, @@ -247,9 +294,32 @@ impl<'a> State<'a> { render_pass.draw(0..self.num_vertices, 0..1); } + self.chain + .frame(Arc::clone(&render_output), + &Viewport { + x: 0.0, + y: 0.0, + mvp: None, + output: librashader_runtime_wgpu::OutputView { + size: filter_output.size().into(), + view: &filter_view, + format: filter_output.format(), + }, + }, + &mut encoder, + self.frame_count, None + ).expect("failed to draw frame"); + + encoder.copy_texture_to_texture( + filter_output.as_image_copy(), + output.texture.as_image_copy(), + output.texture.size() + ); + self.queue.submit(std::iter::once(encoder.finish())); output.present(); + self.frame_count += 1; Ok(()) } } @@ -266,39 +336,36 @@ pub fn run() { pollster::block_on(async { let mut state = State::new(&window).await; - event_loop.run(|event, target| { - - match event { - Event::WindowEvent { - window_id: _, - event - } => match event { - WindowEvent::Resized(new_size) => { - state.resize(new_size); - // On macos the window needs to be redrawn manually after resizing - window.request_redraw(); - } - WindowEvent::RedrawRequested => { - state.update(); - match state.render() { - Ok(_) => {} - Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => { - state.resize(state.size) + event_loop + .run(|event, target| { + match event { + Event::WindowEvent { + window_id: _, + event, + } => match event { + WindowEvent::Resized(new_size) => { + state.resize(new_size); + // On macos the window needs to be redrawn manually after resizing + window.request_redraw(); + } + WindowEvent::RedrawRequested => { + state.update(); + match state.render() { + Ok(_) => {} + Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => { + state.resize(state.size) + } + Err(wgpu::SurfaceError::OutOfMemory) => target.exit(), + Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"), } - Err(wgpu::SurfaceError::OutOfMemory) => target.exit(), - Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"), } - } - WindowEvent::CloseRequested => target.exit(), + WindowEvent::CloseRequested => target.exit(), + _ => {} + }, + Event::AboutToWait => window.request_redraw(), _ => {} } - Event::AboutToWait => { - window.request_redraw() - } - _ => {} - } - - - }).unwrap(); + }) + .unwrap(); }); }