以前にこんなのを書いたけど、
けっきょく、nannou の挙動がわからないので勉強がてら素の wgpu と lyon を使ってみるか〜となってしまったのでそのメモ(vulkan のドライバまわり?の謎のエラーの切り分けのために、詳しめのエラーを出してくれる開発版 wgpu を使いたかった、という実利的な理由もあります)。 wgpu-rs も lyon も執筆時点の GitHub 最新版を使っているので、ここに書くことはすぐに風化していく知識です。
CullMode
は None
wgpu::RasterizationStateDescriptor
の cull_mode
は、 描画しない vertex の向きを指定するオプション。
learn-wgpu だと wgpu::CullMode::Back
を使ってるけど、 lyon を使う時にこれを指定してしまうと何も表示されない。
2D だと表も裏もないので culling されないようにするのが正しいっぽい(Front
でも動くけど)。
index buffer は padding する
lyon で tessellation をやるとき、
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new(); let tolerance = 0.0001; let mut stroke_tess = StrokeTessellator::new(); stroke_tess .tessellate_path( &path, &StrokeOptions::tolerance(tolerance).with_line_width(0.13), &mut BuffersBuilder::new(&mut geometry, |vertex: tessellation::StrokeVertex| { Vertex { position: vertex.position().to_array(), } }), ) .unwrap();
みたいなことをするけど、このとき、 geometry
には vertex とその index が書き込まれる。が、そのまま使うとたぶんこういうエラーになる。
thread 'main' panicked at 'assertion failed: `(left == right)` left: `2`, right: `0`: Buffers that are mapped at creation have to be aligned to COPY_BUFFER_ALIGNMENT', <::std::macros::panic macros>:5:6
これは、以下の制限が少し前に入ったから。
device.create_buffer_with_data()
を使う場合、データ長が wgpu::COPY_BUFFER_ALIGNMENT
(具体的には 4)の倍数になっている必要がある。
が、 vertex は多くの場合三角形なので、 index data は 3 の倍数になる。
ということで、こんな感じで自分で padding する必要がある。 render_pass.draw_indexed()
には元の範囲を指定する必要があるので、 extend()
する前に range を記録しておく。
let stroke_range = 0..(geometry.indices.len() as u32); geometry.indices.extend(std::iter::repeat(0).take( wgpu::COPY_BUFFER_ALIGNMENT as usize - geometry.indices.len() % wgpu::COPY_BUFFER_ALIGNMENT as usize, ));
MSAA
TextureView をつくってそれを wgpu::RenderPassColorAttachmentDescriptor
の attachment
に指定し、 frame.output.view
は resolve_target
に移す。基本は公式レポジトリの exampleを見れば書かれている。
--- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,8 @@ use winit::{ use futures::executor::block_on; +const SAMPLE_COUNT: u32 = 4; + // The vertex type that we will use to represent a point on our triangle. #[repr(C)] #[derive(Clone, Copy)] @@ -35,6 +37,7 @@ struct State { // index_count: usize, // bind_group: wgpu::BindGroup, render_pipeline: wgpu::RenderPipeline, + multisampled_framebuffer: wgpu::TextureView, // blur_render_pipeline: wgpu::RenderPipeline, geometry: VertexBuffers<Vertex, u16>, stroke_range: std::ops::Range<u32>, @@ -120,6 +123,16 @@ impl State { // Load shader modules. let vs_mod = device.create_shader_module(wgpu::include_spirv!("shaders/shader.vert.spv")); let fs_mod = device.create_shader_module(wgpu::include_spirv!("shaders/shader.frag.spv")); + + // MSAA + let multisampled_texture_extent = wgpu::Extent3d { + width: sc_desc.width, + height: sc_desc.height, + depth: 1, + }; + + let multisampled_framebuffer = create_multisampled_framebuffer(&device, &sc_desc); + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { @@ -153,7 +166,7 @@ impl State { attributes: &wgpu::vertex_attr_array![0 => Float2], }], }, - sample_count: 1, + sample_count: SAMPLE_COUNT, sample_mask: !0, alpha_to_coverage_enabled: false, }); @@ -177,6 +190,7 @@ impl State { // index_count, // bind_group, render_pipeline, + multisampled_framebuffer, geometry, stroke_range, size, @@ -189,6 +203,8 @@ impl State { self.sc_desc.width = new_size.width; self.sc_desc.height = new_size.height; self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + + self.update_multisampled_framebuffer(); } fn input(&mut self, event: &WindowEvent) -> bool { @@ -227,8 +243,8 @@ impl State { { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { - attachment: &frame.output.view, - resolve_target: None, + attachment: &self.multisampled_framebuffer, + resolve_target: Some(&frame.output.view), ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.1, @@ -262,6 +278,35 @@ impl State { &self.queue.submit(Some(encoder.finish())); } + + fn update_multisampled_framebuffer(&mut self) { + self.multisampled_framebuffer = + create_multisampled_framebuffer(&self.device, &self.sc_desc); + } +} + +fn create_multisampled_framebuffer( + device: &wgpu::Device, + sc_desc: &wgpu::SwapChainDescriptor, +) -> wgpu::TextureView { + let multisampled_texture_extent = wgpu::Extent3d { + width: sc_desc.width, + height: sc_desc.height, + depth: 1, + }; + let multisampled_frame_descriptor = &wgpu::TextureDescriptor { + size: multisampled_texture_extent, + mip_level_count: 1, + sample_count: SAMPLE_COUNT, + dimension: wgpu::TextureDimension::D2, + format: sc_desc.format, + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + label: None, + }; + + device + .create_texture(multisampled_frame_descriptor) + .create_default_view() } // main() is derived from sotrh/learn-wgpu
結果