From 211c0b921e5407d053ecce93128dd7909663aa86 Mon Sep 17 00:00:00 2001 From: numzero Date: Sun, 29 Dec 2024 18:57:13 +0300 Subject: [PATCH] Two-pass render for a much smoother look --- src/bin/minitracer/main.rs | 68 ++++++++++++++++----- src/bin/minitracer/present.rs | 103 ++++++++++++++++++++++++++++++++ src/bin/minitracer/present.wgsl | 21 +++++++ 3 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 src/bin/minitracer/present.rs create mode 100644 src/bin/minitracer/present.wgsl diff --git a/src/bin/minitracer/main.rs b/src/bin/minitracer/main.rs index b0caad7..229a4d0 100644 --- a/src/bin/minitracer/main.rs +++ b/src/bin/minitracer/main.rs @@ -2,6 +2,7 @@ use std::error::Error; use glam::{vec2, vec3}; use image::ImageReader; +use present::Presenter; use trace::{Tracer, TracerData, TracerEnv, Vertex}; use winit::{ event::{Event, WindowEvent}, @@ -10,6 +11,7 @@ use winit::{ }; mod anim; +mod present; mod trace; pub use trace::Sphere; @@ -65,6 +67,8 @@ fn main() { }; let tracer_env = TracerEnv::new(&device, &tracer, &envmap); + let presenter = Presenter::new(&device, format); + let mut time = 0.0; let mut surface_configured = false; @@ -101,23 +105,57 @@ fn main() { let output = surface.get_current_texture().unwrap(); let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let size = output.texture.size(); + let buf = device.create_texture(&wgpu::TextureDescriptor { label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, + size: wgpu::Extent3d { + width: 2 * size.width, + height: 2 * size.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], }); - tracer.render(&mut render_pass, &data, &tracer_env); - drop(render_pass); + let buf = buf.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &buf, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + tracer.render(&mut render_pass, &data, &tracer_env); + } + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + presenter.render(&device, &mut render_pass, &buf); + } queue.submit(std::iter::once(encoder.finish())); output.present(); } diff --git a/src/bin/minitracer/present.rs b/src/bin/minitracer/present.rs new file mode 100644 index 0000000..ae2b069 --- /dev/null +++ b/src/bin/minitracer/present.rs @@ -0,0 +1,103 @@ +use std::mem::offset_of; + +use bytemuck::{bytes_of, Pod, Zeroable}; +use glam::{vec2, Vec2}; +use wgpu::util::DeviceExt; + +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[repr(C)] +struct Vertex { + pub screen: Vec2, +} + +pub struct Presenter { + view_buf: wgpu::Buffer, + sampler: wgpu::Sampler, + pipeline: wgpu::RenderPipeline, +} + +static SHADER: &str = include_str!("present.wgsl"); + +impl Presenter { + pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { + let view_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytes_of(&[(0., 0.), (1., 0.), (0., 1.), (1., 1.)].map(|(x, y)| Vertex { screen: vec2(x, y) })), + usage: wgpu::BufferUsages::VERTEX, + }); + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(SHADER.into()), + }); + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + buffers: &[wgpu::VertexBufferLayout { + array_stride: size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[wgpu::VertexAttribute { + shader_location: 0, + offset: offset_of!(Vertex, screen) as u64, + format: wgpu::VertexFormat::Float32x2, + }], + }], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + min_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + Self { + view_buf, + sampler, + pipeline, + } + } + + pub fn render(&self, device: &wgpu::Device, pass: &mut wgpu::RenderPass, texture: &wgpu::TextureView) { + let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &self.pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&self.sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(texture), + }, + ], + }); + pass.set_pipeline(&self.pipeline); + pass.set_vertex_buffer(0, self.view_buf.slice(..)); + pass.set_bind_group(0, &bindings, &[]); + pass.draw(0..4, 0..1); + } +} diff --git a/src/bin/minitracer/present.wgsl b/src/bin/minitracer/present.wgsl new file mode 100644 index 0000000..e206941 --- /dev/null +++ b/src/bin/minitracer/present.wgsl @@ -0,0 +1,21 @@ +struct Vertex { + @location(0) screen: vec2f, +} + +struct Varying { + @location(0) tex: vec2f, + @builtin(position) v: vec4f, +} + +@group(0) @binding(0) var smp: sampler; +@group(0) @binding(1) var tex: texture_2d; + +@vertex +fn on_vertex(in: Vertex) -> Varying { + return Varying(in.screen, vec4(2.0 * in.screen - 1.0, 0.0, 1.0)); +} + +@fragment +fn on_fragment(in: Varying) -> @location(0) vec4f { + return textureSample(tex, smp, in.tex); +}