From 2b047f996b267e098bb0cc1b881bbf2727735f52 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 14 Jun 2019 00:31:32 -0400 Subject: [PATCH] [rs] Mipmapping example --- .gitignore | 3 + wgpu/Cargo.toml | 2 +- wgpu/examples/mipmap/blit.frag | 10 + wgpu/examples/mipmap/blit.vert | 15 ++ wgpu/examples/mipmap/draw.frag | 12 + wgpu/examples/mipmap/draw.vert | 15 ++ wgpu/examples/mipmap/main.rs | 419 +++++++++++++++++++++++++++++++++ 7 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 wgpu/examples/mipmap/blit.frag create mode 100644 wgpu/examples/mipmap/blit.vert create mode 100644 wgpu/examples/mipmap/draw.frag create mode 100644 wgpu/examples/mipmap/draw.vert create mode 100644 wgpu/examples/mipmap/main.rs diff --git a/.gitignore b/.gitignore index 088ba6ba7..2de194b17 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# Other +.DS_Store diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index c34ebfbf2..ef77f551f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -24,7 +24,7 @@ gl = ["wgn/gfx-backend-gl"] [dependencies] #TODO: only depend on the published version -wgn = { package = "wgpu-native", features = ["local", "window-winit"], git = "https://github.com/gfx-rs/wgpu", rev = "8cc50253c428fb0a9aab3c74639a866465d65272" } +wgn = { package = "wgpu-native", version = "0.2.6", features = ["local", "window-winit"], git = "https://github.com/gfx-rs/wgpu", rev = "a667d50d01c3df03b8540c523278e111da7fc82a" } arrayvec = "0.4" [dev-dependencies] diff --git a/wgpu/examples/mipmap/blit.frag b/wgpu/examples/mipmap/blit.frag new file mode 100644 index 000000000..cfe6499aa --- /dev/null +++ b/wgpu/examples/mipmap/blit.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec2 v_TexCoord; +layout(location = 0) out vec4 o_Target; +layout(set = 0, binding = 0) uniform texture2D t_Color; +layout(set = 0, binding = 1) uniform sampler s_Color; + +void main() { + o_Target = textureLod(sampler2D(t_Color, s_Color), v_TexCoord, 0.0); +} diff --git a/wgpu/examples/mipmap/blit.vert b/wgpu/examples/mipmap/blit.vert new file mode 100644 index 000000000..2be374a11 --- /dev/null +++ b/wgpu/examples/mipmap/blit.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) out vec2 v_TexCoord; + +void main() { + vec2 tc = vec2(0.0); + switch(gl_VertexIndex) { + case 0: tc = vec2(1.0, 0.0); break; + case 1: tc = vec2(1.0, 1.0); break; + case 2: tc = vec2(0.0, 0.0); break; + case 3: tc = vec2(0.0, 1.0); break; + } + v_TexCoord = tc; + gl_Position = vec4(tc * 2.0 - 1.0, 0.5, 1.0); +} diff --git a/wgpu/examples/mipmap/draw.frag b/wgpu/examples/mipmap/draw.frag new file mode 100644 index 000000000..b88e3abf0 --- /dev/null +++ b/wgpu/examples/mipmap/draw.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) in vec2 v_TexCoord; +layout(location = 0) out vec4 o_Target; +layout(set = 0, binding = 1) uniform texture2D t_Color; +layout(set = 0, binding = 2) uniform sampler s_Color; + +void main() { + vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord); + float mag = length(v_TexCoord-vec2(0.5)); + o_Target = mix(tex, vec4(0.0), mag*mag); +} diff --git a/wgpu/examples/mipmap/draw.vert b/wgpu/examples/mipmap/draw.vert new file mode 100644 index 000000000..a3caa12e5 --- /dev/null +++ b/wgpu/examples/mipmap/draw.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec4 a_Pos; +layout(location = 0) out vec2 v_TexCoord; + +layout(set = 0, binding = 0) uniform Locals { + mat4 u_Transform; +}; + +void main() { + v_TexCoord = a_Pos.xy / 10.0; + gl_Position = u_Transform * a_Pos; + // convert from -1,1 Z to 0,1 + gl_Position.z = 0.5 * (gl_Position.z + gl_Position.w); +} diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs new file mode 100644 index 000000000..08ae411b6 --- /dev/null +++ b/wgpu/examples/mipmap/main.rs @@ -0,0 +1,419 @@ +#[path = "../framework.rs"] +mod framework; + +const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R8Unorm; + +#[derive(Clone, Copy)] +struct Vertex { + #[allow(dead_code)] + pos: [f32; 4], +} + +fn create_vertices() -> Vec { + vec![ + Vertex { + pos: [100.0, 0.0, 0.0, 1.0], + }, + Vertex { + pos: [100.0, 1000.0, 0.0, 1.0], + }, + Vertex { + pos: [-100.0, 0.0, 0.0, 1.0], + }, + Vertex { + pos: [-100.0, 1000.0, 0.0, 1.0], + }, + ] +} + +fn create_texels(size: usize) -> Vec { + (0 .. size * size) + .map(|id| { + if (id + id / size) % 2 == 1 { + 0xFF + } else { + 0 + } + }) + .collect() +} + +struct Example { + vertex_buf: wgpu::Buffer, + bind_group: wgpu::BindGroup, + uniform_buf: wgpu::Buffer, + draw_pipeline: wgpu::RenderPipeline, +} + +impl Example { + fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { + let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 1000.0); + let mx_view = cgmath::Matrix4::look_at( + cgmath::Point3::new(0f32, 0.0, 10.0), + cgmath::Point3::new(0f32, 100.0, 0.0), + -cgmath::Vector3::unit_z(), + ); + mx_projection * mx_view + } + + fn generate_mipmaps( + device: &wgpu::Device, texture: &wgpu::Texture, mip_count: u32 + ) -> wgpu::CommandBuffer { + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&bind_group_layout], + }); + + let vs_bytes = framework::load_glsl( + include_str!("blit.vert"), + framework::ShaderStage::Vertex, + ); + let fs_bytes = framework::load_glsl( + include_str!("blit.frag"), + framework::ShaderStage::Fragment, + ); + let vs_module = device.create_shader_module(&vs_bytes); + let fs_module = device.create_shader_module(&fs_bytes); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::PipelineStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::PipelineStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Cw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }, + primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, + color_states: &[wgpu::ColorStateDescriptor { + format: TEXTURE_FORMAT, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + sample_count: 1, + }); + + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let views = (0 .. mip_count) + .map(|mip| texture.create_view(&wgpu::TextureViewDescriptor { + format: TEXTURE_FORMAT, + dimension: wgpu::TextureViewDimension::D2, + aspect: wgpu::TextureAspectFlags::COLOR, + base_mip_level: mip, + level_count: 1, + base_array_layer: 0, + array_count: 1, + })) + .collect::>(); + + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { todo: 0 } + ); + + for target_mip in 1 .. mip_count as usize { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&views[target_mip - 1]), + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + ], + }); + + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &views[target_mip], + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color::WHITE, + }], + depth_stencil_attachment: None, + }); + rpass.set_pipeline(&pipeline); + rpass.set_bind_group(0, &bind_group, &[]); + rpass.draw(0 .. 4, 0 .. 1); + } + + encoder.finish() + } +} + +impl framework::Example for Example { + fn init(sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device) -> Self { + use std::mem; + + let mut init_encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + + // Create the vertex and index buffers + let vertex_size = mem::size_of::(); + let vertex_data = create_vertices(); + let vertex_buf = device + .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(&vertex_data); + + // Create pipeline layout + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::UniformBuffer, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture, + }, + wgpu::BindGroupLayoutBinding { + binding: 2, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&bind_group_layout], + }); + + // Create the texture + let mip_level_count = 10; + let size = 1 << mip_level_count; + let texels = create_texels(size as usize); + let texture_extent = wgpu::Extent3d { + width: size, + height: size, + depth: 1, + }; + let texture = device.create_texture(&wgpu::TextureDescriptor { + size: texture_extent, + array_layer_count: 1, + mip_level_count, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: TEXTURE_FORMAT, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::TRANSFER_DST, + }); + let texture_view = texture.create_default_view(); + let temp_buf = device + .create_buffer_mapped(texels.len(), wgpu::BufferUsage::TRANSFER_SRC) + .fill_from_slice(&texels); + init_encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &temp_buf, + offset: 0, + row_pitch: 1 * size, + image_height: size, + }, + wgpu::TextureCopyView { + texture: &texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d { + x: 0.0, + y: 0.0, + z: 0.0, + }, + }, + texture_extent, + ); + + // Create other resources + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::Repeat, + address_mode_v: wgpu::AddressMode::Repeat, + address_mode_w: wgpu::AddressMode::Repeat, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let mx_ref: &[f32; 16] = mx_total.as_ref(); + let uniform_buf = device + .create_buffer_mapped( + 16, + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::TRANSFER_DST, + ) + .fill_from_slice(mx_ref); + + // Create bind group + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buf, + range: 0 .. 64, + }, + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::TextureView(&texture_view), + }, + wgpu::Binding { + binding: 2, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + ], + }); + + // Create the render pipeline + let vs_bytes = framework::load_glsl( + include_str!("draw.vert"), + framework::ShaderStage::Vertex, + ); + let fs_bytes = framework::load_glsl( + include_str!("draw.frag"), + framework::ShaderStage::Fragment, + ); + let vs_module = device.create_shader_module(&vs_bytes); + let fs_module = device.create_shader_module(&fs_bytes); + + let draw_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::PipelineStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::PipelineStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Cw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }, + primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, + color_states: &[wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[wgpu::VertexBufferDescriptor { + stride: vertex_size as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + format: wgpu::VertexFormat::Float4, + offset: 0, + shader_location: 0, + }, + ], + }], + sample_count: 1, + }); + + // Done + let init_command_buf = init_encoder.finish(); + let mipmap_command_buf = Self::generate_mipmaps(&device, &texture, mip_level_count); + device.get_queue().submit(&[init_command_buf, mipmap_command_buf]); + Example { + vertex_buf, + bind_group, + uniform_buf, + draw_pipeline, + } + } + + fn update(&mut self, _event: wgpu::winit::WindowEvent) { + //empty + } + + fn resize(&mut self, sc_desc: &wgpu::SwapChainDescriptor, device: &mut wgpu::Device) { + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let mx_ref: &[f32; 16] = mx_total.as_ref(); + + let temp_buf = device + .create_buffer_mapped(16, wgpu::BufferUsage::TRANSFER_SRC) + .fill_from_slice(mx_ref); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + encoder.copy_buffer_to_buffer(&temp_buf, 0, &self.uniform_buf, 0, 64); + device.get_queue().submit(&[encoder.finish()]); + } + + fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + }], + depth_stencil_attachment: None, + }); + rpass.set_pipeline(&self.draw_pipeline); + rpass.set_bind_group(0, &self.bind_group, &[]); + rpass.set_vertex_buffers(&[(&self.vertex_buf, 0)]); + rpass.draw(0 .. 4, 0 .. 1); + } + + device.get_queue().submit(&[encoder.finish()]); + } +} + +fn main() { + framework::run::("cube"); +}