diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 3de4f3289..5ecb007aa 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -26,20 +26,20 @@ webgl = ["wgc"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "5e15980bb53d2a5c24546dab640dcc72d211bd36" +rev = "717c2d73e725c064a3c4696d6d7bdb428c9500b6" features = ["raw-window-handle", "cross"] [target.'cfg(target_arch = "wasm32")'.dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "5e15980bb53d2a5c24546dab640dcc72d211bd36" +rev = "717c2d73e725c064a3c4696d6d7bdb428c9500b6" features = ["raw-window-handle", "cross"] optional = true [dependencies.wgt] package = "wgpu-types" git = "https://github.com/gfx-rs/wgpu" -rev = "5e15980bb53d2a5c24546dab640dcc72d211bd36" +rev = "717c2d73e725c064a3c4696d6d7bdb428c9500b6" [dependencies] arrayvec = "0.5" diff --git a/wgpu/examples/README.md b/wgpu/examples/README.md index e1740072e..f2e178edb 100644 --- a/wgpu/examples/README.md +++ b/wgpu/examples/README.md @@ -10,40 +10,41 @@ Notably, `capture` example shows rendering without a surface/window. It reads ba All framework-based examples render to the window. ## Feature matrix -| Feature | boids | cube | mipmap | msaa-line | shadow | skybox | texture-arrays | water | -| ---------------------- | ------ | ------ | ------ | --------- | ------ | ------ | -------------- | ------ | -| WGSL shaders | :star: | :star: | :star: | :star: | :star: | :star: | | | -| vertex attributes | :star: | :star: | | :star: | :star: | :star: | :star: | :star: | -| instancing | :star: | | | | | | | | -| lines and points | | | | :star: | | | | | -| dynamic buffer offsets | | | | | :star: | | | | -| implicit layout | | | :star: | | | | | | -| sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: | -| storage textures | :star: | | | | | | | | -| binding array | | | | | | | :star: | | -| comparison samplers | | | | | :star: | | | | -| subresource views | | | :star: | | :star: | | | | -| cubemaps | | | | | | :star: | | | -| multisampling | | | | :star: | | | | | -| off-screen rendering | | | | | :star: | | | :star: | -| stencil testing | | | | | | | | | -| depth testing | | | | | :star: | :star: | | :star: | -| depth biasing | | | | | :star: | | | | -| read-only depth | | | | | | | | :star: | -| blending | | :star: | | | | | | :star: | -| render bundles | | | | :star: | | | | :star: | -| compute passes | :star: | | | | | | | | -| *optional extensions* | | | | | | | :star: | | -| - binding indexing | | | | | | | :star: | | -| - push constants | | | | | | | :star: | | -| - depth clamping | | | | | :star: | | | | -| - compressed textures | | | | | | :star: | | | -| - polygon mode | | :star: | | | | | | | -| - queries | | | :star: | | | | | | -| *integrations* | | | | | | | | | -| - staging belt | | | | | | | | | -| - typed arena | | | | | | | | | -| - obj loading | | | | | | :star: | | | +| Feature | boids | cube | mipmap | msaa-line | shadow | skybox | texture-arrays | water | conservative-raster | +| ---------------------------- | ------ | ------ | ------ | --------- | ------ | ------ | -------------- | ------ | ------------------- | +| WGSL shaders | :star: | :star: | :star: | :star: | :star: | :star: | | | :star: | +| vertex attributes | :star: | :star: | | :star: | :star: | :star: | :star: | :star: | | +| instancing | :star: | | | | | | | | | +| lines and points | | | | :star: | | | | | :star: | +| dynamic buffer offsets | | | | | :star: | | | | | +| implicit layout | | | :star: | | | | | | | +| sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: | :star: | +| storage textures | :star: | | | | | | | | | +| binding array | | | | | | | :star: | | | +| comparison samplers | | | | | :star: | | | | | +| subresource views | | | :star: | | :star: | | | | | +| cubemaps | | | | | | :star: | | | | +| multisampling | | | | :star: | | | | | | +| off-screen rendering | | | | | :star: | | | :star: | :star: | +| stencil testing | | | | | | | | | | +| depth testing | | | | | :star: | :star: | | :star: | | +| depth biasing | | | | | :star: | | | | | +| read-only depth | | | | | | | | :star: | | +| blending | | :star: | | | | | | :star: | | +| render bundles | | | | :star: | | | | :star: | | +| compute passes | :star: | | | | | | | | | +| *optional extensions* | | | | | | | :star: | | | +| - binding indexing | | | | | | | :star: | | | +| - push constants | | | | | | | :star: | | | +| - depth clamping | | | | | :star: | | | | | +| - compressed textures | | | | | | :star: | | | | +| - polygon mode | | :star: | | | | | | | | +| - queries | | | :star: | | | | | | | +| - conservative rasterization | | | | | | | | | :star: | +| *integrations* | | | | | | | | | | +| - staging belt | | | | | | | | | | +| - typed arena | | | | | | | | | | +| - obj loading | | | | | | :star: | | | | ## Hacking diff --git a/wgpu/examples/conservative-raster/README.md b/wgpu/examples/conservative-raster/README.md new file mode 100644 index 000000000..bd157319b --- /dev/null +++ b/wgpu/examples/conservative-raster/README.md @@ -0,0 +1,20 @@ +# conservative-raster + +This example shows how to render with conservative rasterization (native extension with limited support). + +When enabled, any pixel touched by a triangle primitive is rasterized. +This is useful for various advanced techniques, most prominently for implementing realtime voxelization. + +The demonstration here is implemented by rendering a triangle to a low-resolution target and then upscaling it with nearest-neighbor filtering. +The outlines of the triangle are then rendered in the original solution, using the same vertex shader as the triangle. +Pixels only drawn with conservative rasterization enabled are depicted red. + +## To Run + +``` +cargo run --example conservative-raster +``` + +## Screenshots + +![Conservative-raster window](./screenshot.png) diff --git a/wgpu/examples/conservative-raster/main.rs b/wgpu/examples/conservative-raster/main.rs new file mode 100644 index 000000000..e379520fa --- /dev/null +++ b/wgpu/examples/conservative-raster/main.rs @@ -0,0 +1,316 @@ +#[path = "../framework.rs"] +mod framework; + +use std::borrow::Cow; + +const RENDER_TARGET_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; + +struct Example { + low_res_target: wgpu::TextureView, + bind_group_upscale: wgpu::BindGroup, + + pipeline_triangle_conservative: wgpu::RenderPipeline, + pipeline_triangle_regular: wgpu::RenderPipeline, + pipeline_upscale: wgpu::RenderPipeline, + pipeline_lines: Option, + bind_group_layout_upscale: wgpu::BindGroupLayout, +} + +impl Example { + fn create_low_res_target( + sc_desc: &wgpu::SwapChainDescriptor, + device: &wgpu::Device, + bind_group_layout_upscale: &wgpu::BindGroupLayout, + ) -> (wgpu::TextureView, wgpu::BindGroup) { + let texture_view = device + .create_texture(&wgpu::TextureDescriptor { + label: Some("Low Resolution Target"), + size: wgpu::Extent3d { + width: sc_desc.width / 16, + height: sc_desc.width / 16, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: RENDER_TARGET_FORMAT, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, + }) + .create_view(&Default::default()); + + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("Nearest Neighbor Sampler"), + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("upscale bind group"), + layout: &bind_group_layout_upscale, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + ], + }); + + (texture_view, bind_group) + } +} + +impl framework::Example for Example { + fn required_features() -> wgpu::Features { + wgpu::Features::CONSERVATIVE_RASTERIZATION + } + fn optional_features() -> wgpu::Features { + wgpu::Features::NON_FILL_POLYGON_MODE + } + fn init( + sc_desc: &wgpu::SwapChainDescriptor, + adapter: &wgpu::Adapter, + device: &wgpu::Device, + _queue: &wgpu::Queue, + ) -> Self { + let pipeline_layout_empty = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let shader_triangle_and_lines = + device.create_shader_module(&wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!( + "triangle_and_lines.wgsl" + ))), + flags: wgpu::ShaderFlags::all(), + }); + + let pipeline_triangle_conservative = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Conservative Rasterization"), + layout: Some(&pipeline_layout_empty), + vertex: wgpu::VertexState { + module: &shader_triangle_and_lines, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader_triangle_and_lines, + entry_point: "fs_main_red", + targets: &[RENDER_TARGET_FORMAT.into()], + }), + primitive: wgpu::PrimitiveState { + conservative: true, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + }); + + let pipeline_triangle_regular = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Regular Rasterization"), + layout: Some(&pipeline_layout_empty), + vertex: wgpu::VertexState { + module: &shader_triangle_and_lines, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader_triangle_and_lines, + entry_point: "fs_main_blue", + targets: &[RENDER_TARGET_FORMAT.into()], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + }); + + let pipeline_lines = if adapter + .features() + .contains(wgpu::Features::NON_FILL_POLYGON_MODE) + { + Some( + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Lines"), + layout: Some(&pipeline_layout_empty), + vertex: wgpu::VertexState { + module: &shader_triangle_and_lines, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader_triangle_and_lines, + entry_point: "fs_main_white", + targets: &[sc_desc.format.into()], + }), + primitive: wgpu::PrimitiveState { + polygon_mode: wgpu::PolygonMode::Line, + topology: wgpu::PrimitiveTopology::LineStrip, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + }), + ) + } else { + None + }; + + let (pipeline_upscale, bind_group_layout_upscale) = { + let bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("upscale bindgroup"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { + filtering: false, + comparison: false, + }, + count: None, + }, + ], + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("upscale.wgsl"))), + flags: wgpu::ShaderFlags::all(), + }); + ( + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Upscale"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[sc_desc.format.into()], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + }), + bind_group_layout, + ) + }; + + let (low_res_target, bind_group_upscale) = + Self::create_low_res_target(sc_desc, device, &bind_group_layout_upscale); + + Self { + low_res_target, + bind_group_upscale, + + pipeline_triangle_conservative, + pipeline_triangle_regular, + pipeline_upscale, + pipeline_lines, + bind_group_layout_upscale, + } + } + + fn resize( + &mut self, + sc_desc: &wgpu::SwapChainDescriptor, + device: &wgpu::Device, + _queue: &wgpu::Queue, + ) { + let (low_res_target, bind_group_upscale) = + Self::create_low_res_target(sc_desc, device, &self.bind_group_layout_upscale); + self.low_res_target = low_res_target; + self.bind_group_upscale = bind_group_upscale; + } + + fn update(&mut self, _event: winit::event::WindowEvent) {} + + fn render( + &mut self, + frame: &wgpu::SwapChainTexture, + device: &wgpu::Device, + queue: &wgpu::Queue, + _spawner: &framework::Spawner, + ) { + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("primary"), + }); + + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("low resolution"), + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &self.low_res_target, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: true, + }, + }], + depth_stencil_attachment: None, + }); + + rpass.set_pipeline(&self.pipeline_triangle_conservative); + rpass.draw(0..3, 0..1); + rpass.set_pipeline(&self.pipeline_triangle_regular); + rpass.draw(0..3, 0..1); + } + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("full resolution"), + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: true, + }, + }], + depth_stencil_attachment: None, + }); + + rpass.set_pipeline(&self.pipeline_upscale); + rpass.set_bind_group(0, &self.bind_group_upscale, &[]); + rpass.draw(0..3, 0..1); + + if let Some(pipeline_lines) = &self.pipeline_lines { + rpass.set_pipeline(pipeline_lines); + rpass.draw(0..4, 0..1); + } + } + + queue.submit(Some(encoder.finish())); + } +} + +fn main() { + framework::run::("conservative-raster"); +} diff --git a/wgpu/examples/conservative-raster/screenshot.png b/wgpu/examples/conservative-raster/screenshot.png new file mode 100644 index 000000000..e227e10e6 Binary files /dev/null and b/wgpu/examples/conservative-raster/screenshot.png differ diff --git a/wgpu/examples/conservative-raster/triangle_and_lines.wgsl b/wgpu/examples/conservative-raster/triangle_and_lines.wgsl new file mode 100644 index 000000000..2f0331f04 --- /dev/null +++ b/wgpu/examples/conservative-raster/triangle_and_lines.wgsl @@ -0,0 +1,22 @@ +[[stage(vertex)]] +fn vs_main([[builtin(vertex_index)]] vertex_index: u32) -> [[builtin(position)]] vec4 { + const i: i32 = i32(vertex_index % 3u); + const x: f32 = f32(i - 1) * 0.75; + const y: f32 = f32((i & 1) * 2 - 1) * 0.75 + x * 0.2 + 0.1; + return vec4(x, y, 0.0, 1.0); +} + +[[stage(fragment)]] +fn fs_main_red() -> [[location(0)]] vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); +} + +[[stage(fragment)]] +fn fs_main_blue() -> [[location(0)]] vec4 { + return vec4(0.13, 0.31, 0.85, 1.0); // cornflower blue in linear space +} + +[[stage(fragment)]] +fn fs_main_white() -> [[location(0)]] vec4 { + return vec4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/wgpu/examples/conservative-raster/upscale.wgsl b/wgpu/examples/conservative-raster/upscale.wgsl new file mode 100644 index 000000000..ff1c00eba --- /dev/null +++ b/wgpu/examples/conservative-raster/upscale.wgsl @@ -0,0 +1,24 @@ +struct VertexOutput { + [[builtin(position)]] position: vec4; + [[location(0)]] tex_coords: vec2; +}; + +[[stage(vertex)]] +fn vs_main([[builtin(vertex_index)]] vertex_index: u32) -> VertexOutput { + const x: f32 = f32(i32(vertex_index & 1) << 2) - 1.0; + const y: f32 = f32(i32(vertex_index & 2) << 1) - 1.0; + var output: VertexOutput; + output.position = vec4(x, -y, 0.0, 1.0); + output.tex_coords = vec2(x + 1.0, y + 1.0) * 0.5; + return output; +} + +[[group(0), binding(0)]] +var r_color: texture_2d; +[[group(0), binding(1)]] +var r_sampler: sampler; + +[[stage(fragment)]] +fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { + return textureSample(r_color, r_sampler, in.tex_coords); +} \ No newline at end of file