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]
|
[dependencies]
|
||||||
|
bytemuck = "1.7"
|
||||||
pollster = "0.2"
|
pollster = "0.2"
|
||||||
raw-window-handle = "0.3"
|
raw-window-handle = "0.3"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
ultraviolet = "0.8"
|
ultraviolet = "0.8"
|
||||||
wgpu = "0.8.1"
|
wgpu = "0.9"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pixels-mocks = { path = "internals/pixels-mocks" }
|
pixels-mocks = { path = "internals/pixels-mocks" }
|
||||||
|
|
|
@ -142,7 +142,7 @@ fn create_window(
|
||||||
let window = winit::window::WindowBuilder::new()
|
let window = winit::window::WindowBuilder::new()
|
||||||
.with_visible(false)
|
.with_visible(false)
|
||||||
.with_title(title)
|
.with_title(title)
|
||||||
.build(&event_loop)
|
.build(event_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let hidpi_factor = window.scale_factor();
|
let hidpi_factor = window.scale_factor();
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ optimize = ["log/release_max_level_warn"]
|
||||||
default = ["optimize"]
|
default = ["optimize"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bytemuck = "1.7"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pixels = { path = "../.." }
|
pixels = { path = "../.." }
|
||||||
|
|
|
@ -5,35 +5,14 @@ struct VertexOutput {
|
||||||
[[builtin(position)]] position: vec4<f32>;
|
[[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)]]
|
[[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;
|
var out: VertexOutput;
|
||||||
out.tex_coord = uv[vertex_index];
|
out.tex_coord = tex_coord;
|
||||||
out.position = vec4<f32>(positions[vertex_index], 0.0, 1.0);
|
out.position = vec4<f32>(position, 0.0, 1.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +43,8 @@ fn random_vec2(st: vec2<f32>) -> f32 {
|
||||||
|
|
||||||
[[stage(fragment)]]
|
[[stage(fragment)]]
|
||||||
fn fs_main([[location(0)]] tex_coord: vec2<f32>) -> [[location(0)]] vec4<f32> {
|
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 sampled_color = textureSample(r_tex_color, r_tex_sampler, tex_coord);
|
||||||
let noise_color: vec3<f32> = vec3<f32>(random_vec2(
|
let noise_color = vec3<f32>(random_vec2(tex_coord.xy * vec2<f32>(r_locals.time % tau + bias)));
|
||||||
tex_coord.xy * vec2<f32>(r_locals.time % tau + bias)
|
|
||||||
));
|
|
||||||
|
|
||||||
return vec4<f32>(sampled_color.rgb * noise_color, sampled_color.a);
|
return vec4<f32>(sampled_color.rgb * noise_color, sampled_color.a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use crate::renderers::NoiseRenderer;
|
use crate::renderers::NoiseRenderer;
|
||||||
use log::error;
|
use log::error;
|
||||||
use pixels::{wgpu, Error, Pixels, SurfaceTexture};
|
use pixels::{Error, Pixels, SurfaceTexture};
|
||||||
use winit::dpi::LogicalSize;
|
use winit::dpi::LogicalSize;
|
||||||
use winit::event::{Event, VirtualKeyCode};
|
use winit::event::{Event, VirtualKeyCode};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
@ -44,9 +44,8 @@ fn main() -> Result<(), Error> {
|
||||||
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
||||||
};
|
};
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
let mut time = 0.0;
|
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| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
|
@ -54,12 +53,13 @@ fn main() -> Result<(), Error> {
|
||||||
world.draw(pixels.get_frame());
|
world.draw(pixels.get_frame());
|
||||||
|
|
||||||
let render_result = pixels.render_with(|encoder, render_target, context| {
|
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);
|
noise_renderer.update(&context.queue, time);
|
||||||
time += 0.01;
|
time += 0.01;
|
||||||
|
|
||||||
noise_renderer.render(encoder, render_target);
|
noise_renderer.render(encoder, render_target, context.scaling_renderer.clip_rect());
|
||||||
});
|
});
|
||||||
|
|
||||||
if render_result
|
if render_result
|
||||||
|
@ -82,6 +82,7 @@ fn main() -> Result<(), Error> {
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
pixels.resize_surface(size.width, size.height);
|
||||||
|
noise_renderer.resize(pixels.device(), size.width, size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal state and request a redraw
|
// Update internal state and request a redraw
|
||||||
|
@ -117,7 +118,7 @@ impl World {
|
||||||
|
|
||||||
/// Draw the `World` state to the frame buffer.
|
/// 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]) {
|
fn draw(&self, frame: &mut [u8]) {
|
||||||
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
||||||
let x = (i % WIDTH as usize) as i16;
|
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 pixels::wgpu::{self, util::DeviceExt};
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
pub(crate) struct NoiseRenderer {
|
pub(crate) struct NoiseRenderer {
|
||||||
|
texture_view: wgpu::TextureView,
|
||||||
|
sampler: wgpu::Sampler,
|
||||||
|
bind_group_layout: wgpu::BindGroupLayout,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
render_pipeline: wgpu::RenderPipeline,
|
||||||
time_buffer: wgpu::Buffer,
|
time_buffer: wgpu::Buffer,
|
||||||
|
vertex_buffer: wgpu::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoiseRenderer {
|
impl NoiseRenderer {
|
||||||
pub(crate) fn new(device: &wgpu::Device, texture_view: &wgpu::TextureView) -> Self {
|
pub(crate) fn new(device: &wgpu::Device, width: u32, height: u32) -> Self {
|
||||||
let shader = wgpu::ShaderModuleDescriptor {
|
let shader = wgpu::include_wgsl!("../shaders/noise.wgsl");
|
||||||
label: Some("custom_noise_shader"),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/noise.wgsl"))),
|
|
||||||
flags: wgpu::ShaderFlags::VALIDATION,
|
|
||||||
};
|
|
||||||
let module = device.create_shader_module(&shader);
|
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
|
// Create a texture sampler with nearest neighbor
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some("NoiseRenderer sampler"),
|
label: Some("NoiseRenderer sampler"),
|
||||||
|
@ -32,6 +35,37 @@ impl NoiseRenderer {
|
||||||
border_color: None,
|
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
|
// Create uniform buffer
|
||||||
let time_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let time_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("NoiseRenderer u_Time"),
|
label: Some("NoiseRenderer u_Time"),
|
||||||
|
@ -74,28 +108,13 @@ impl NoiseRenderer {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let bind_group = create_bind_group(
|
||||||
label: None,
|
device,
|
||||||
layout: &bind_group_layout,
|
&bind_group_layout,
|
||||||
entries: &[
|
&texture_view,
|
||||||
wgpu::BindGroupEntry {
|
&sampler,
|
||||||
binding: 0,
|
&time_buffer,
|
||||||
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,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create pipeline
|
// Create pipeline
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
@ -109,7 +128,7 @@ impl NoiseRenderer {
|
||||||
vertex: wgpu::VertexState {
|
vertex: wgpu::VertexState {
|
||||||
module: &module,
|
module: &module,
|
||||||
entry_point: "vs_main",
|
entry_point: "vs_main",
|
||||||
buffers: &[],
|
buffers: &[vertex_buffer_layout],
|
||||||
},
|
},
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
|
@ -129,12 +148,31 @@ impl NoiseRenderer {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
texture_view,
|
||||||
|
sampler,
|
||||||
|
bind_group_layout,
|
||||||
bind_group,
|
bind_group,
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
time_buffer,
|
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) {
|
pub(crate) fn update(&self, queue: &wgpu::Queue, time: f32) {
|
||||||
queue.write_buffer(&self.time_buffer, 0, &time.to_ne_bytes());
|
queue.write_buffer(&self.time_buffer, 0, &time.to_ne_bytes());
|
||||||
}
|
}
|
||||||
|
@ -143,6 +181,7 @@ impl NoiseRenderer {
|
||||||
&self,
|
&self,
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
render_target: &wgpu::TextureView,
|
render_target: &wgpu::TextureView,
|
||||||
|
clip_rect: (u32, u32, u32, u32),
|
||||||
) {
|
) {
|
||||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("NoiseRenderer render pass"),
|
label: Some("NoiseRenderer render pass"),
|
||||||
|
@ -158,6 +197,59 @@ impl NoiseRenderer {
|
||||||
});
|
});
|
||||||
rpass.set_pipeline(&self.render_pipeline);
|
rpass.set_pipeline(&self.render_pipeline);
|
||||||
rpass.set_bind_group(0, &self.bind_group, &[]);
|
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]
|
[dependencies]
|
||||||
egui = "0.12"
|
egui = "0.12"
|
||||||
|
egui_wgpu_backend = "0.9"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pixels = { path = "../.." }
|
pixels = { path = "../.." }
|
||||||
winit = "0.25"
|
winit = "0.25"
|
||||||
winit_input_helper = "0.10"
|
winit_input_helper = "0.10"
|
||||||
|
|
||||||
[dependencies.egui_wgpu_backend]
|
|
||||||
git = "https://github.com/hasenbanck/egui_wgpu_backend.git"
|
|
||||||
rev = "63a002c6a9b6c016e45806dd065864431caab621"
|
|
||||||
|
|
||||||
[dependencies.egui_winit_platform]
|
[dependencies.egui_winit_platform]
|
||||||
git = "https://github.com/hasenbanck/egui_winit_platform.git"
|
git = "https://github.com/hasenbanck/egui_winit_platform.git"
|
||||||
rev = "f9a0814a395a5cc1cc997081f33ee09503a4a307"
|
rev = "ab576591949a2c0ac1494af52713a1771ff8326a"
|
||||||
features = ["webbrowser"]
|
features = ["webbrowser"]
|
||||||
|
|
|
@ -12,9 +12,12 @@ default = ["optimize"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
imgui = "0.7"
|
imgui = "0.7"
|
||||||
imgui-wgpu = "0.15.1"
|
|
||||||
imgui-winit-support = { version = "0.7.1", default-features = false, features = ["winit-25"] }
|
imgui-winit-support = { version = "0.7.1", default-features = false, features = ["winit-25"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pixels = { path = "../.." }
|
pixels = { path = "../.." }
|
||||||
winit = "0.25"
|
winit = "0.25"
|
||||||
winit_input_helper = "0.10"
|
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,
|
texture_format,
|
||||||
..Default::default()
|
..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
|
// Return GUI context
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -102,7 +102,7 @@ impl Collision {
|
||||||
|
|
||||||
for (i, shield_rect) in shield_rects.iter().enumerate() {
|
for (i, shield_rect) in shield_rects.iter().enumerate() {
|
||||||
// broad phase collision detection
|
// broad phase collision detection
|
||||||
if bullet_rect.intersects(&shield_rect) {
|
if bullet_rect.intersects(shield_rect) {
|
||||||
// TODO: Narrow phase (per-pixel) collision detection
|
// TODO: Narrow phase (per-pixel) collision detection
|
||||||
// TODO: Break shield
|
// TODO: Break shield
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ impl Collision {
|
||||||
|
|
||||||
for (i, shield_rect) in shield_rects.iter().enumerate() {
|
for (i, shield_rect) in shield_rects.iter().enumerate() {
|
||||||
// broad phase collision detection
|
// broad phase collision detection
|
||||||
if laser_rect.intersects(&shield_rect) {
|
if laser_rect.intersects(shield_rect) {
|
||||||
// TODO: Narrow phase (per-pixel) collision detection
|
// TODO: Narrow phase (per-pixel) collision detection
|
||||||
// TODO: Break shield
|
// TODO: Break shield
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ fn create_window(
|
||||||
let window = winit::window::WindowBuilder::new()
|
let window = winit::window::WindowBuilder::new()
|
||||||
.with_visible(false)
|
.with_visible(false)
|
||||||
.with_title(title)
|
.with_title(title)
|
||||||
.build(&event_loop)
|
.build(event_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let hidpi_factor = window.scale_factor();
|
let hidpi_factor = window.scale_factor();
|
||||||
|
|
||||||
|
|
|
@ -10,35 +10,14 @@ struct VertexOutput {
|
||||||
};
|
};
|
||||||
[[group(0), binding(2)]] var r_locals: Locals;
|
[[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)]]
|
[[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;
|
var out: VertexOutput;
|
||||||
out.tex_coord = uv[vertex_index];
|
out.tex_coord = tex_coord;
|
||||||
out.position = r_locals.transform * vec4<f32>(positions[vertex_index], 0.0, 1.0);
|
out.position = r_locals.transform * vec4<f32>(position, 0.0, 1.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,7 @@ pub(crate) fn create_swap_chain(
|
||||||
present_mode: wgpu::PresentMode,
|
present_mode: wgpu::PresentMode,
|
||||||
) -> wgpu::SwapChain {
|
) -> wgpu::SwapChain {
|
||||||
device.create_swap_chain(
|
device.create_swap_chain(
|
||||||
&surface,
|
surface,
|
||||||
&wgpu::SwapChainDescriptor {
|
&wgpu::SwapChainDescriptor {
|
||||||
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||||
format,
|
format,
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
use crate::SurfaceSize;
|
use crate::SurfaceSize;
|
||||||
use std::borrow::Cow;
|
|
||||||
use ultraviolet::Mat4;
|
use ultraviolet::Mat4;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
/// The default renderer that scales your frame to the screen size.
|
/// The default renderer that scales your frame to the screen size.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ScalingRenderer {
|
pub struct ScalingRenderer {
|
||||||
|
vertex_buffer: wgpu::Buffer,
|
||||||
uniform_buffer: wgpu::Buffer,
|
uniform_buffer: wgpu::Buffer,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
render_pipeline: wgpu::RenderPipeline,
|
||||||
width: f32,
|
width: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
|
clip_rect: (u32, u32, u32, u32),
|
||||||
render_texture_format: wgpu::TextureFormat,
|
render_texture_format: wgpu::TextureFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +23,7 @@ impl ScalingRenderer {
|
||||||
surface_size: &SurfaceSize,
|
surface_size: &SurfaceSize,
|
||||||
render_texture_format: wgpu::TextureFormat,
|
render_texture_format: wgpu::TextureFormat,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let shader = wgpu::ShaderModuleDescriptor {
|
let shader = wgpu::include_wgsl!("../shaders/scale.wgsl");
|
||||||
label: Some("pixels_scaling_renderer_shader"),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/scale.wgsl"))),
|
|
||||||
flags: wgpu::ShaderFlags::VALIDATION,
|
|
||||||
};
|
|
||||||
let module = device.create_shader_module(&shader);
|
let module = device.create_shader_module(&shader);
|
||||||
|
|
||||||
// Create a texture sampler with nearest neighbor
|
// Create a texture sampler with nearest neighbor
|
||||||
|
@ -45,6 +42,37 @@ impl ScalingRenderer {
|
||||||
border_color: None,
|
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
|
// Create uniform buffer
|
||||||
let matrix = ScalingMatrix::new(
|
let matrix = ScalingMatrix::new(
|
||||||
(texture_size.width as f32, texture_size.height as f32),
|
(texture_size.width as f32, texture_size.height as f32),
|
||||||
|
@ -53,7 +81,7 @@ impl ScalingRenderer {
|
||||||
let transform_bytes = matrix.as_bytes();
|
let transform_bytes = matrix.as_bytes();
|
||||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("pixels_scaling_renderer_matrix_uniform_buffer"),
|
label: Some("pixels_scaling_renderer_matrix_uniform_buffer"),
|
||||||
contents: &transform_bytes,
|
contents: transform_bytes,
|
||||||
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -127,7 +155,7 @@ impl ScalingRenderer {
|
||||||
vertex: wgpu::VertexState {
|
vertex: wgpu::VertexState {
|
||||||
module: &module,
|
module: &module,
|
||||||
entry_point: "vs_main",
|
entry_point: "vs_main",
|
||||||
buffers: &[],
|
buffers: &[vertex_buffer_layout],
|
||||||
},
|
},
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
|
@ -146,18 +174,23 @@ impl ScalingRenderer {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create clipping rectangle
|
||||||
|
let clip_rect = matrix.clip_rect();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
vertex_buffer,
|
||||||
uniform_buffer,
|
uniform_buffer,
|
||||||
bind_group,
|
bind_group,
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
width: texture_size.width as f32,
|
width: texture_size.width as f32,
|
||||||
height: texture_size.height as f32,
|
height: texture_size.height as f32,
|
||||||
|
clip_rect,
|
||||||
render_texture_format,
|
render_texture_format,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw the pixel buffer to the render target.
|
||||||
pub fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView) {
|
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 {
|
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("pixels_scaling_renderer_render_pass"),
|
label: Some("pixels_scaling_renderer_render_pass"),
|
||||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||||
|
@ -172,27 +205,44 @@ impl ScalingRenderer {
|
||||||
});
|
});
|
||||||
rpass.set_pipeline(&self.render_pipeline);
|
rpass.set_pipeline(&self.render_pipeline);
|
||||||
rpass.set_bind_group(0, &self.bind_group, &[]);
|
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 matrix = ScalingMatrix::new((self.width, self.height), (width as f32, height as f32));
|
||||||
let transform_bytes = matrix.as_bytes();
|
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)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ScalingMatrix {
|
pub(crate) struct ScalingMatrix {
|
||||||
pub(crate) transform: Mat4,
|
pub(crate) transform: Mat4,
|
||||||
|
clip_rect: (u32, u32, u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScalingMatrix {
|
impl ScalingMatrix {
|
||||||
// texture_size is the dimensions of the drawing texture
|
// texture_size is the dimensions of the drawing texture
|
||||||
// screen_size is the dimensions of the surface being drawn to
|
// screen_size is the dimensions of the surface being drawn to
|
||||||
pub(crate) fn new(texture_size: (f32, f32), screen_size: (f32, f32)) -> ScalingMatrix {
|
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 (texture_width, texture_height) = texture_size;
|
||||||
|
let (screen_width, screen_height) = screen_size;
|
||||||
|
|
||||||
// Get smallest scale size
|
// Get smallest scale size
|
||||||
let scale = (screen_width / texture_width)
|
let scale = (screen_width / texture_width)
|
||||||
|
@ -200,9 +250,12 @@ impl ScalingMatrix {
|
||||||
.max(1.0)
|
.max(1.0)
|
||||||
.floor();
|
.floor();
|
||||||
|
|
||||||
|
let scaled_width = texture_width * scale;
|
||||||
|
let scaled_height = texture_height * scale;
|
||||||
|
|
||||||
// Update transformation matrix
|
// Update transformation matrix
|
||||||
let sw = texture_width * scale / screen_width;
|
let sw = scaled_width / screen_width;
|
||||||
let sh = texture_height * scale / screen_height;
|
let sh = scaled_height / screen_height;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let transform: [f32; 16] = [
|
let transform: [f32; 16] = [
|
||||||
sw, 0.0, 0.0, 0.0,
|
sw, 0.0, 0.0, 0.0,
|
||||||
|
@ -211,12 +264,27 @@ impl ScalingMatrix {
|
||||||
0.0, 0.0, 0.0, 1.0,
|
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 {
|
ScalingMatrix {
|
||||||
transform: Mat4::from(transform),
|
transform: Mat4::from(transform),
|
||||||
|
clip_rect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_bytes(&self) -> &[u8] {
|
fn as_bytes(&self) -> &[u8] {
|
||||||
self.transform.as_byte_slice()
|
self.transform.as_byte_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clip_rect(&self) -> (u32, u32, u32, u32) {
|
||||||
|
self.clip_rect
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue