[rs] Added example for conservative rasterization (#791)

* Example for conservative rasterization

* Update to wgpu-core/types 717c2d73e725c064a3c4696d6d7bdb428c9500b6

* conservative-raster example: fix wglsl const & readme

* conservative raster example: optional lines, no more interaction
This commit is contained in:
Wumpf 2021-03-16 03:33:44 +01:00 committed by GitHub
parent 23c5e83167
commit 2bda1508f1
7 changed files with 420 additions and 37 deletions

View File

@ -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"

View File

@ -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

View File

@ -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)

View File

@ -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<wgpu::RenderPipeline>,
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::<Example>("conservative-raster");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,22 @@
[[stage(vertex)]]
fn vs_main([[builtin(vertex_index)]] vertex_index: u32) -> [[builtin(position)]] vec4<f32> {
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<f32>(x, y, 0.0, 1.0);
}
[[stage(fragment)]]
fn fs_main_red() -> [[location(0)]] vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
[[stage(fragment)]]
fn fs_main_blue() -> [[location(0)]] vec4<f32> {
return vec4<f32>(0.13, 0.31, 0.85, 1.0); // cornflower blue in linear space
}
[[stage(fragment)]]
fn fs_main_white() -> [[location(0)]] vec4<f32> {
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
}

View File

@ -0,0 +1,24 @@
struct VertexOutput {
[[builtin(position)]] position: vec4<f32>;
[[location(0)]] tex_coords: vec2<f32>;
};
[[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<f32>(x, -y, 0.0, 1.0);
output.tex_coords = vec2<f32>(x + 1.0, y + 1.0) * 0.5;
return output;
}
[[group(0), binding(0)]]
var r_color: texture_2d<f32>;
[[group(0), binding(1)]]
var r_sampler: sampler;
[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
return textureSample(r_color, r_sampler, in.tex_coords);
}