[rs] Mipmapping example

This commit is contained in:
Dzmitry Malyshau 2019-06-14 00:31:32 -04:00
parent 154ed3a9a5
commit 2b047f996b
7 changed files with 475 additions and 1 deletions

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# Other
.DS_Store

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Vertex> {
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<u8> {
(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<f32> {
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::<Vec<_>>();
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::<Vertex>();
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::<Example>("cube");
}