Update to wgpu 0.9 (#179)
* Update to wgpu 0.9 * Fix validation error in WGSL shader - This moves the hardcoded vertex positions and texture coordinates to the vertex buffer. - Replaces the two-triangle quad to 1 full-screen triangle (fixes #180) - Rewrites the custom shader example to fix a bug with large surface textures; - The input texture size was used for the output texture, causing the purple rectangle to appear very jumpy on large displays in full screen. - The `ScalingRenderer` now exposes its clipping rectangle. The custom shader example uses this for its own clipping rectangle, but it can also be used for interacting with the border in general. * Switch to `wgpu::include_wgsl!()` - This is a nice little simplification. - Thanks to @JMS55 for the suggestion!
This commit is contained in:
parent
3ce4b75ad9
commit
c4df23f65d
|
@ -19,11 +19,12 @@ include = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
bytemuck = "1.7"
|
||||
pollster = "0.2"
|
||||
raw-window-handle = "0.3"
|
||||
thiserror = "1.0"
|
||||
ultraviolet = "0.8"
|
||||
wgpu = "0.8.1"
|
||||
wgpu = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
pixels-mocks = { path = "internals/pixels-mocks" }
|
||||
|
|
|
@ -142,7 +142,7 @@ fn create_window(
|
|||
let window = winit::window::WindowBuilder::new()
|
||||
.with_visible(false)
|
||||
.with_title(title)
|
||||
.build(&event_loop)
|
||||
.build(event_loop)
|
||||
.unwrap();
|
||||
let hidpi_factor = window.scale_factor();
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ optimize = ["log/release_max_level_warn"]
|
|||
default = ["optimize"]
|
||||
|
||||
[dependencies]
|
||||
bytemuck = "1.7"
|
||||
env_logger = "0.8"
|
||||
log = "0.4"
|
||||
pixels = { path = "../.." }
|
||||
|
|
|
@ -5,35 +5,14 @@ struct VertexOutput {
|
|||
[[builtin(position)]] position: vec4<f32>;
|
||||
};
|
||||
|
||||
let positions: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
|
||||
// Upper left triangle
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>(1.0, -1.0),
|
||||
vec2<f32>(-1.0, 1.0),
|
||||
|
||||
// Lower right triangle
|
||||
vec2<f32>(-1.0, 1.0),
|
||||
vec2<f32>(1.0, -1.0),
|
||||
vec2<f32>(1.0, 1.0),
|
||||
);
|
||||
|
||||
let uv: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
|
||||
// Upper left triangle
|
||||
vec2<f32>(0.0, 0.0),
|
||||
vec2<f32>(1.0, 0.0),
|
||||
vec2<f32>(0.0, 1.0),
|
||||
|
||||
// Lower right triangle
|
||||
vec2<f32>(0.0, 1.0),
|
||||
vec2<f32>(1.0, 0.0),
|
||||
vec2<f32>(1.0, 1.0),
|
||||
);
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn vs_main([[builtin(vertex_index)]] vertex_index: u32) -> VertexOutput {
|
||||
fn vs_main(
|
||||
[[location(0)]] position: vec2<f32>,
|
||||
[[location(1)]] tex_coord: vec2<f32>,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.tex_coord = uv[vertex_index];
|
||||
out.position = vec4<f32>(positions[vertex_index], 0.0, 1.0);
|
||||
out.tex_coord = tex_coord;
|
||||
out.position = vec4<f32>(position, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -64,10 +43,8 @@ fn random_vec2(st: vec2<f32>) -> f32 {
|
|||
|
||||
[[stage(fragment)]]
|
||||
fn fs_main([[location(0)]] tex_coord: vec2<f32>) -> [[location(0)]] vec4<f32> {
|
||||
let sampled_color: vec4<f32> = textureSample(r_tex_color, r_tex_sampler, tex_coord);
|
||||
let noise_color: vec3<f32> = vec3<f32>(random_vec2(
|
||||
tex_coord.xy * vec2<f32>(r_locals.time % tau + bias)
|
||||
));
|
||||
let sampled_color = textureSample(r_tex_color, r_tex_sampler, tex_coord);
|
||||
let noise_color = vec3<f32>(random_vec2(tex_coord.xy * vec2<f32>(r_locals.time % tau + bias)));
|
||||
|
||||
return vec4<f32>(sampled_color.rgb * noise_color, sampled_color.a);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use crate::renderers::NoiseRenderer;
|
||||
use log::error;
|
||||
use pixels::{wgpu, Error, Pixels, SurfaceTexture};
|
||||
use pixels::{Error, Pixels, SurfaceTexture};
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::{Event, VirtualKeyCode};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
|
@ -44,9 +44,8 @@ fn main() -> Result<(), Error> {
|
|||
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
||||
};
|
||||
let mut world = World::new();
|
||||
|
||||
let mut time = 0.0;
|
||||
let (scaled_texture, noise_renderer) = create_noise_renderer(&pixels);
|
||||
let mut noise_renderer = NoiseRenderer::new(pixels.device(), WIDTH, HEIGHT);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
// Draw the current frame
|
||||
|
@ -54,12 +53,13 @@ fn main() -> Result<(), Error> {
|
|||
world.draw(pixels.get_frame());
|
||||
|
||||
let render_result = pixels.render_with(|encoder, render_target, context| {
|
||||
context.scaling_renderer.render(encoder, &scaled_texture);
|
||||
let noise_texture = noise_renderer.get_texture_view();
|
||||
context.scaling_renderer.render(encoder, noise_texture);
|
||||
|
||||
noise_renderer.update(&context.queue, time);
|
||||
time += 0.01;
|
||||
|
||||
noise_renderer.render(encoder, render_target);
|
||||
noise_renderer.render(encoder, render_target, context.scaling_renderer.clip_rect());
|
||||
});
|
||||
|
||||
if render_result
|
||||
|
@ -82,6 +82,7 @@ fn main() -> Result<(), Error> {
|
|||
// Resize the window
|
||||
if let Some(size) = input.window_resized() {
|
||||
pixels.resize_surface(size.width, size.height);
|
||||
noise_renderer.resize(pixels.device(), size.width, size.height);
|
||||
}
|
||||
|
||||
// Update internal state and request a redraw
|
||||
|
@ -117,7 +118,7 @@ impl World {
|
|||
|
||||
/// Draw the `World` state to the frame buffer.
|
||||
///
|
||||
/// Assumes the default texture format: [`wgpu::TextureFormat::Rgba8UnormSrgb`]
|
||||
/// Assumes the default texture format: [`pixels::wgpu::TextureFormat::Rgba8UnormSrgb`]
|
||||
fn draw(&self, frame: &mut [u8]) {
|
||||
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
||||
let x = (i % WIDTH as usize) as i16;
|
||||
|
@ -138,27 +139,3 @@ impl World {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_noise_renderer(pixels: &Pixels) -> (wgpu::TextureView, NoiseRenderer) {
|
||||
let device = &pixels.device();
|
||||
|
||||
let texture_descriptor = wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size: pixels::wgpu::Extent3d {
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
};
|
||||
let scaled_texture = device
|
||||
.create_texture(&texture_descriptor)
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let noise_renderer = NoiseRenderer::new(device, &scaled_texture);
|
||||
|
||||
(scaled_texture, noise_renderer)
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
use pixels::wgpu::{self, util::DeviceExt};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub(crate) struct NoiseRenderer {
|
||||
texture_view: wgpu::TextureView,
|
||||
sampler: wgpu::Sampler,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
bind_group: wgpu::BindGroup,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
time_buffer: wgpu::Buffer,
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl NoiseRenderer {
|
||||
pub(crate) fn new(device: &wgpu::Device, texture_view: &wgpu::TextureView) -> Self {
|
||||
let shader = wgpu::ShaderModuleDescriptor {
|
||||
label: Some("custom_noise_shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/noise.wgsl"))),
|
||||
flags: wgpu::ShaderFlags::VALIDATION,
|
||||
};
|
||||
pub(crate) fn new(device: &wgpu::Device, width: u32, height: u32) -> Self {
|
||||
let shader = wgpu::include_wgsl!("../shaders/noise.wgsl");
|
||||
let module = device.create_shader_module(&shader);
|
||||
|
||||
// Create a texture view that will be used as input
|
||||
// This will be used as the render target for the default scaling renderer
|
||||
let texture_view = create_texture_view(device, width, height);
|
||||
|
||||
// Create a texture sampler with nearest neighbor
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("NoiseRenderer sampler"),
|
||||
|
@ -32,6 +35,37 @@ impl NoiseRenderer {
|
|||
border_color: None,
|
||||
});
|
||||
|
||||
// Create vertex buffer; array-of-array of position and texture coordinates
|
||||
let vertex_data: [[[f32; 2]; 2]; 3] = [
|
||||
// One full-screen triangle
|
||||
// See: https://github.com/parasyte/pixels/issues/180
|
||||
[[-1.0, -1.0], [0.0, 0.0]],
|
||||
[[3.0, -1.0], [2.0, 0.0]],
|
||||
[[-1.0, 3.0], [0.0, 2.0]],
|
||||
];
|
||||
let vertex_data_slice = bytemuck::cast_slice(&vertex_data);
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("NoiseRenderer vertex buffer"),
|
||||
contents: vertex_data_slice,
|
||||
usage: wgpu::BufferUsage::VERTEX,
|
||||
});
|
||||
let vertex_buffer_layout = wgpu::VertexBufferLayout {
|
||||
array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 4 * 2,
|
||||
shader_location: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Create uniform buffer
|
||||
let time_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("NoiseRenderer u_Time"),
|
||||
|
@ -74,28 +108,13 @@ impl NoiseRenderer {
|
|||
},
|
||||
],
|
||||
});
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(texture_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &time_buffer,
|
||||
offset: 0,
|
||||
size: None,
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
let bind_group = create_bind_group(
|
||||
device,
|
||||
&bind_group_layout,
|
||||
&texture_view,
|
||||
&sampler,
|
||||
&time_buffer,
|
||||
);
|
||||
|
||||
// Create pipeline
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
|
@ -109,7 +128,7 @@ impl NoiseRenderer {
|
|||
vertex: wgpu::VertexState {
|
||||
module: &module,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
buffers: &[vertex_buffer_layout],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
|
@ -129,12 +148,31 @@ impl NoiseRenderer {
|
|||
});
|
||||
|
||||
Self {
|
||||
texture_view,
|
||||
sampler,
|
||||
bind_group_layout,
|
||||
bind_group,
|
||||
render_pipeline,
|
||||
time_buffer,
|
||||
vertex_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_texture_view(&self) -> &wgpu::TextureView {
|
||||
&self.texture_view
|
||||
}
|
||||
|
||||
pub(crate) fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
|
||||
self.texture_view = create_texture_view(device, width, height);
|
||||
self.bind_group = create_bind_group(
|
||||
device,
|
||||
&self.bind_group_layout,
|
||||
&self.texture_view,
|
||||
&self.sampler,
|
||||
&self.time_buffer,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn update(&self, queue: &wgpu::Queue, time: f32) {
|
||||
queue.write_buffer(&self.time_buffer, 0, &time.to_ne_bytes());
|
||||
}
|
||||
|
@ -143,6 +181,7 @@ impl NoiseRenderer {
|
|||
&self,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
render_target: &wgpu::TextureView,
|
||||
clip_rect: (u32, u32, u32, u32),
|
||||
) {
|
||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("NoiseRenderer render pass"),
|
||||
|
@ -158,6 +197,59 @@ impl NoiseRenderer {
|
|||
});
|
||||
rpass.set_pipeline(&self.render_pipeline);
|
||||
rpass.set_bind_group(0, &self.bind_group, &[]);
|
||||
rpass.draw(0..6, 0..1);
|
||||
rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
rpass.set_scissor_rect(clip_rect.0, clip_rect.1, clip_rect.2, clip_rect.3);
|
||||
rpass.draw(0..3, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_texture_view(device: &wgpu::Device, width: u32, height: u32) -> wgpu::TextureView {
|
||||
let texture_descriptor = wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size: pixels::wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
};
|
||||
|
||||
device
|
||||
.create_texture(&texture_descriptor)
|
||||
.create_view(&wgpu::TextureViewDescriptor::default())
|
||||
}
|
||||
|
||||
fn create_bind_group(
|
||||
device: &wgpu::Device,
|
||||
bind_group_layout: &wgpu::BindGroupLayout,
|
||||
texture_view: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
time_buffer: &wgpu::Buffer,
|
||||
) -> pixels::wgpu::BindGroup {
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(texture_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: time_buffer,
|
||||
offset: 0,
|
||||
size: None,
|
||||
}),
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -11,17 +11,14 @@ default = ["optimize"]
|
|||
|
||||
[dependencies]
|
||||
egui = "0.12"
|
||||
egui_wgpu_backend = "0.9"
|
||||
env_logger = "0.8"
|
||||
log = "0.4"
|
||||
pixels = { path = "../.." }
|
||||
winit = "0.25"
|
||||
winit_input_helper = "0.10"
|
||||
|
||||
[dependencies.egui_wgpu_backend]
|
||||
git = "https://github.com/hasenbanck/egui_wgpu_backend.git"
|
||||
rev = "63a002c6a9b6c016e45806dd065864431caab621"
|
||||
|
||||
[dependencies.egui_winit_platform]
|
||||
git = "https://github.com/hasenbanck/egui_winit_platform.git"
|
||||
rev = "f9a0814a395a5cc1cc997081f33ee09503a4a307"
|
||||
rev = "ab576591949a2c0ac1494af52713a1771ff8326a"
|
||||
features = ["webbrowser"]
|
||||
|
|
|
@ -12,9 +12,12 @@ default = ["optimize"]
|
|||
[dependencies]
|
||||
env_logger = "0.8"
|
||||
imgui = "0.7"
|
||||
imgui-wgpu = "0.15.1"
|
||||
imgui-winit-support = { version = "0.7.1", default-features = false, features = ["winit-25"] }
|
||||
log = "0.4"
|
||||
pixels = { path = "../.." }
|
||||
winit = "0.25"
|
||||
winit_input_helper = "0.10"
|
||||
|
||||
[dependencies.imgui-wgpu]
|
||||
git = "https://github.com/Yatekii/imgui-wgpu-rs.git"
|
||||
rev = "5980d8f3ebeda52310bb9f38d98ae1d513907e7f"
|
||||
|
|
|
@ -49,7 +49,7 @@ impl Gui {
|
|||
texture_format,
|
||||
..Default::default()
|
||||
};
|
||||
let renderer = imgui_wgpu::Renderer::new(&mut imgui, &device, &queue, config);
|
||||
let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config);
|
||||
|
||||
// Return GUI context
|
||||
Self {
|
||||
|
|
|
@ -102,7 +102,7 @@ impl Collision {
|
|||
|
||||
for (i, shield_rect) in shield_rects.iter().enumerate() {
|
||||
// broad phase collision detection
|
||||
if bullet_rect.intersects(&shield_rect) {
|
||||
if bullet_rect.intersects(shield_rect) {
|
||||
// TODO: Narrow phase (per-pixel) collision detection
|
||||
// TODO: Break shield
|
||||
|
||||
|
@ -163,7 +163,7 @@ impl Collision {
|
|||
|
||||
for (i, shield_rect) in shield_rects.iter().enumerate() {
|
||||
// broad phase collision detection
|
||||
if laser_rect.intersects(&shield_rect) {
|
||||
if laser_rect.intersects(shield_rect) {
|
||||
// TODO: Narrow phase (per-pixel) collision detection
|
||||
// TODO: Break shield
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ fn create_window(
|
|||
let window = winit::window::WindowBuilder::new()
|
||||
.with_visible(false)
|
||||
.with_title(title)
|
||||
.build(&event_loop)
|
||||
.build(event_loop)
|
||||
.unwrap();
|
||||
let hidpi_factor = window.scale_factor();
|
||||
|
||||
|
|
|
@ -10,35 +10,14 @@ struct VertexOutput {
|
|||
};
|
||||
[[group(0), binding(2)]] var r_locals: Locals;
|
||||
|
||||
let positions: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
|
||||
// Upper left triangle
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>(1.0, -1.0),
|
||||
vec2<f32>(-1.0, 1.0),
|
||||
|
||||
// Lower right triangle
|
||||
vec2<f32>(-1.0, 1.0),
|
||||
vec2<f32>(1.0, -1.0),
|
||||
vec2<f32>(1.0, 1.0),
|
||||
);
|
||||
|
||||
let uv: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
|
||||
// Upper left triangle
|
||||
vec2<f32>(0.0, 0.0),
|
||||
vec2<f32>(1.0, 0.0),
|
||||
vec2<f32>(0.0, 1.0),
|
||||
|
||||
// Lower right triangle
|
||||
vec2<f32>(0.0, 1.0),
|
||||
vec2<f32>(1.0, 0.0),
|
||||
vec2<f32>(1.0, 1.0),
|
||||
);
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn vs_main([[builtin(vertex_index)]] vertex_index: u32) -> VertexOutput {
|
||||
fn vs_main(
|
||||
[[location(0)]] position: vec2<f32>,
|
||||
[[location(1)]] tex_coord: vec2<f32>,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.tex_coord = uv[vertex_index];
|
||||
out.position = r_locals.transform * vec4<f32>(positions[vertex_index], 0.0, 1.0);
|
||||
out.tex_coord = tex_coord;
|
||||
out.position = r_locals.transform * vec4<f32>(position, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -256,7 +256,7 @@ pub(crate) fn create_swap_chain(
|
|||
present_mode: wgpu::PresentMode,
|
||||
) -> wgpu::SwapChain {
|
||||
device.create_swap_chain(
|
||||
&surface,
|
||||
surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
format,
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
use crate::SurfaceSize;
|
||||
use std::borrow::Cow;
|
||||
use ultraviolet::Mat4;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
/// The default renderer that scales your frame to the screen size.
|
||||
#[derive(Debug)]
|
||||
pub struct ScalingRenderer {
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
bind_group: wgpu::BindGroup,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
width: f32,
|
||||
height: f32,
|
||||
clip_rect: (u32, u32, u32, u32),
|
||||
render_texture_format: wgpu::TextureFormat,
|
||||
}
|
||||
|
||||
|
@ -22,11 +23,7 @@ impl ScalingRenderer {
|
|||
surface_size: &SurfaceSize,
|
||||
render_texture_format: wgpu::TextureFormat,
|
||||
) -> Self {
|
||||
let shader = wgpu::ShaderModuleDescriptor {
|
||||
label: Some("pixels_scaling_renderer_shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/scale.wgsl"))),
|
||||
flags: wgpu::ShaderFlags::VALIDATION,
|
||||
};
|
||||
let shader = wgpu::include_wgsl!("../shaders/scale.wgsl");
|
||||
let module = device.create_shader_module(&shader);
|
||||
|
||||
// Create a texture sampler with nearest neighbor
|
||||
|
@ -45,6 +42,37 @@ impl ScalingRenderer {
|
|||
border_color: None,
|
||||
});
|
||||
|
||||
// Create vertex buffer; array-of-array of position and texture coordinates
|
||||
let vertex_data: [[[f32; 2]; 2]; 3] = [
|
||||
// One full-screen triangle
|
||||
// See: https://github.com/parasyte/pixels/issues/180
|
||||
[[-1.0, -1.0], [0.0, 0.0]],
|
||||
[[3.0, -1.0], [2.0, 0.0]],
|
||||
[[-1.0, 3.0], [0.0, 2.0]],
|
||||
];
|
||||
let vertex_data_slice = bytemuck::cast_slice(&vertex_data);
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("pixels_scaling_renderer_vertex_buffer"),
|
||||
contents: vertex_data_slice,
|
||||
usage: wgpu::BufferUsage::VERTEX,
|
||||
});
|
||||
let vertex_buffer_layout = wgpu::VertexBufferLayout {
|
||||
array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 4 * 2,
|
||||
shader_location: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Create uniform buffer
|
||||
let matrix = ScalingMatrix::new(
|
||||
(texture_size.width as f32, texture_size.height as f32),
|
||||
|
@ -53,7 +81,7 @@ impl ScalingRenderer {
|
|||
let transform_bytes = matrix.as_bytes();
|
||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("pixels_scaling_renderer_matrix_uniform_buffer"),
|
||||
contents: &transform_bytes,
|
||||
contents: transform_bytes,
|
||||
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||
});
|
||||
|
||||
|
@ -127,7 +155,7 @@ impl ScalingRenderer {
|
|||
vertex: wgpu::VertexState {
|
||||
module: &module,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
buffers: &[vertex_buffer_layout],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
|
@ -146,18 +174,23 @@ impl ScalingRenderer {
|
|||
}),
|
||||
});
|
||||
|
||||
// Create clipping rectangle
|
||||
let clip_rect = matrix.clip_rect();
|
||||
|
||||
Self {
|
||||
vertex_buffer,
|
||||
uniform_buffer,
|
||||
bind_group,
|
||||
render_pipeline,
|
||||
width: texture_size.width as f32,
|
||||
height: texture_size.height as f32,
|
||||
clip_rect,
|
||||
render_texture_format,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the pixel buffer to the render target.
|
||||
pub fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView) {
|
||||
// Draw the updated texture to the render target
|
||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("pixels_scaling_renderer_render_pass"),
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
|
@ -172,27 +205,44 @@ impl ScalingRenderer {
|
|||
});
|
||||
rpass.set_pipeline(&self.render_pipeline);
|
||||
rpass.set_bind_group(0, &self.bind_group, &[]);
|
||||
rpass.draw(0..6, 0..1);
|
||||
rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
rpass.set_scissor_rect(
|
||||
self.clip_rect.0,
|
||||
self.clip_rect.1,
|
||||
self.clip_rect.2,
|
||||
self.clip_rect.3,
|
||||
);
|
||||
rpass.draw(0..3, 0..1);
|
||||
}
|
||||
|
||||
pub(crate) fn resize(&self, queue: &wgpu::Queue, width: u32, height: u32) {
|
||||
/// Get the clipping rectangle for the scaling renderer.
|
||||
///
|
||||
/// This rectangle defines the inner bounds of the surface texture, without the border.
|
||||
pub fn clip_rect(&self) -> (u32, u32, u32, u32) {
|
||||
self.clip_rect
|
||||
}
|
||||
|
||||
pub(crate) fn resize(&mut self, queue: &wgpu::Queue, width: u32, height: u32) {
|
||||
let matrix = ScalingMatrix::new((self.width, self.height), (width as f32, height as f32));
|
||||
let transform_bytes = matrix.as_bytes();
|
||||
queue.write_buffer(&self.uniform_buffer, 0, &transform_bytes);
|
||||
queue.write_buffer(&self.uniform_buffer, 0, transform_bytes);
|
||||
|
||||
self.clip_rect = matrix.clip_rect();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ScalingMatrix {
|
||||
pub(crate) transform: Mat4,
|
||||
clip_rect: (u32, u32, u32, u32),
|
||||
}
|
||||
|
||||
impl ScalingMatrix {
|
||||
// texture_size is the dimensions of the drawing texture
|
||||
// screen_size is the dimensions of the surface being drawn to
|
||||
pub(crate) fn new(texture_size: (f32, f32), screen_size: (f32, f32)) -> ScalingMatrix {
|
||||
let (screen_width, screen_height) = screen_size;
|
||||
let (texture_width, texture_height) = texture_size;
|
||||
let (screen_width, screen_height) = screen_size;
|
||||
|
||||
// Get smallest scale size
|
||||
let scale = (screen_width / texture_width)
|
||||
|
@ -200,9 +250,12 @@ impl ScalingMatrix {
|
|||
.max(1.0)
|
||||
.floor();
|
||||
|
||||
let scaled_width = texture_width * scale;
|
||||
let scaled_height = texture_height * scale;
|
||||
|
||||
// Update transformation matrix
|
||||
let sw = texture_width * scale / screen_width;
|
||||
let sh = texture_height * scale / screen_height;
|
||||
let sw = scaled_width / screen_width;
|
||||
let sh = scaled_height / screen_height;
|
||||
#[rustfmt::skip]
|
||||
let transform: [f32; 16] = [
|
||||
sw, 0.0, 0.0, 0.0,
|
||||
|
@ -211,12 +264,27 @@ impl ScalingMatrix {
|
|||
0.0, 0.0, 0.0, 1.0,
|
||||
];
|
||||
|
||||
// Create a clipping rectangle
|
||||
let x = (screen_width - scaled_width) / 2.0;
|
||||
let y = (screen_height - scaled_height) / 2.0;
|
||||
let clip_rect = (
|
||||
x as u32,
|
||||
y as u32,
|
||||
scaled_width as u32,
|
||||
scaled_height as u32,
|
||||
);
|
||||
|
||||
ScalingMatrix {
|
||||
transform: Mat4::from(transform),
|
||||
clip_rect,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self.transform.as_byte_slice()
|
||||
}
|
||||
|
||||
pub(crate) fn clip_rect(&self) -> (u32, u32, u32, u32) {
|
||||
self.clip_rect
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue