use bytemuck::{Pod, Zeroable};
use std::borrow::Cow;
use std::mem;
use wgpu::util::DeviceExt;
#[path = "../framework.rs"]
mod framework;
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Vertex {
_pos: [f32; 4],
}
fn vertex(x: f32, y: f32) -> Vertex {
Vertex {
_pos: [x, y, 0.0, 1.0],
}
}
struct Triangles {
outer_vertex_buffer: wgpu::Buffer,
mask_vertex_buffer: wgpu::Buffer,
outer_pipeline: wgpu::RenderPipeline,
mask_pipeline: wgpu::RenderPipeline,
stencil_buffer: wgpu::Texture,
}
impl framework::Example for Triangles {
fn init(
config: &wgpu::SurfaceConfiguration,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> Self {
let vertex_size = mem::size_of::<Vertex>();
let outer_vertices = [vertex(-1.0, -1.0), vertex(1.0, -1.0), vertex(0.0, 1.0)];
let mask_vertices = [vertex(-0.5, 0.0), vertex(0.0, -1.0), vertex(0.5, 0.0)];
let outer_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Outer Vertex Buffer"),
contents: bytemuck::cast_slice(&outer_vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let mask_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Mask Vertex Buffer"),
contents: bytemuck::cast_slice(&mask_vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
});
let vertex_buffers = [wgpu::VertexBufferLayout {
array_stride: vertex_size as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x4,
offset: 0,
shader_location: 0,
}],
}];
let mask_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &vertex_buffers,
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: None,
write_mask: wgpu::ColorWrites::empty(),
})],
}),
primitive: Default::default(),
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Stencil8,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Always,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Always,
pass_op: wgpu::StencilOperation::Replace,
..Default::default()
},
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: Default::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
let outer_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &vertex_buffers,
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(config.format.into())],
}),
primitive: Default::default(),
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Stencil8,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Always,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Greater,
..Default::default()
},
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: Default::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
let stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Stencil buffer"),
size: wgpu::Extent3d {
width: config.width,
height: config.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Stencil8,
view_formats: &[],
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
});
Triangles {
outer_vertex_buffer,
mask_vertex_buffer,
outer_pipeline,
mask_pipeline,
stencil_buffer,
}
}
fn update(&mut self, _event: winit::event::WindowEvent) {
}
fn resize(
&mut self,
_config: &wgpu::SurfaceConfiguration,
_device: &wgpu::Device,
_queue: &wgpu::Queue,
) {
}
fn render(
&mut self,
view: &wgpu::TextureView,
device: &wgpu::Device,
queue: &wgpu::Queue,
_spawner: &framework::Spawner,
) {
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let depth_view = self.stencil_buffer.create_view(&Default::default());
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_view,
depth_ops: None,
stencil_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0),
store: true,
}),
}),
});
rpass.set_stencil_reference(1);
rpass.set_pipeline(&self.mask_pipeline);
rpass.set_vertex_buffer(0, self.mask_vertex_buffer.slice(..));
rpass.draw(0..3, 0..1);
rpass.set_pipeline(&self.outer_pipeline);
rpass.set_vertex_buffer(0, self.outer_vertex_buffer.slice(..));
rpass.draw(0..3, 0..1);
}
queue.submit(Some(encoder.finish()));
}
}
fn main() {
framework::run::<Triangles>("stencil-triangles");
}
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
#[test]
#[wasm_bindgen_test::wasm_bindgen_test]
fn stencil_triangles() {
framework::test::<Triangles>(framework::FrameworkRefTest {
image_path: "/examples/stencil-triangles/screenshot.png",
width: 1024,
height: 768,
optional_features: wgpu::Features::default(),
base_test_parameters: framework::test_common::TestParameters::default(),
tolerance: 1,
max_outliers: 0,
});
}