Fix screen scaling when window is resized (#25)
* Refactor window creation and size handling * Require pixel aspect ratio to be > 0 * Fix screen scaling when window is resized - Ensure the screen retains its correct pixel aspect ratio - Updated public API on `RenderPass` ... this will continue to be unstable until the initial release - Add build instructions for the internal shaders
This commit is contained in:
parent
cc04f14a3d
commit
8bebb29a06
|
@ -3,6 +3,7 @@ use std::time::Instant;
|
||||||
|
|
||||||
use pixels::{Error, Pixels, SurfaceTexture};
|
use pixels::{Error, Pixels, SurfaceTexture};
|
||||||
use simple_invaders::{Controls, Direction, World, SCREEN_HEIGHT, SCREEN_WIDTH};
|
use simple_invaders::{Controls, Direction, World, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||||
|
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
|
||||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
@ -18,29 +19,8 @@ fn main() -> Result<(), Error> {
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
let (window, surface, width, height, mut hidpi_factor) = {
|
let (window, surface, width, height, mut hidpi_factor) =
|
||||||
let scale = 3.0;
|
create_window("pixel invaders", &event_loop);
|
||||||
let width = SCREEN_WIDTH as f64 * scale;
|
|
||||||
let height = SCREEN_HEIGHT as f64 * scale;
|
|
||||||
|
|
||||||
let window = winit::window::WindowBuilder::new()
|
|
||||||
.with_inner_size(winit::dpi::LogicalSize::new(width, height))
|
|
||||||
.with_title("pixel invaders")
|
|
||||||
.build(&event_loop)
|
|
||||||
.unwrap();
|
|
||||||
let surface = pixels::wgpu::Surface::create(&window);
|
|
||||||
let hidpi_factor = window.hidpi_factor();
|
|
||||||
let size = window.inner_size().to_physical(hidpi_factor);
|
|
||||||
|
|
||||||
(
|
|
||||||
window,
|
|
||||||
surface,
|
|
||||||
size.width.round() as u32,
|
|
||||||
size.height.round() as u32,
|
|
||||||
hidpi_factor,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let surface_texture = SurfaceTexture::new(width, height, surface);
|
let surface_texture = SurfaceTexture::new(width, height, surface);
|
||||||
let mut pixels = Pixels::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, surface_texture)?;
|
let mut pixels = Pixels::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, surface_texture)?;
|
||||||
let mut invaders = World::new(debug);
|
let mut invaders = World::new(debug);
|
||||||
|
@ -103,3 +83,56 @@ fn main() -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a window for the game.
|
||||||
|
///
|
||||||
|
/// Automatically scales the window to cover about 2/3 of the monitor height.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Tuple of `(window, surface, width, height, hidpi_factor)`
|
||||||
|
/// `width` and `height` are in `LogicalSize` units.
|
||||||
|
fn create_window(
|
||||||
|
title: &str,
|
||||||
|
event_loop: &EventLoop<()>,
|
||||||
|
) -> (winit::window::Window, wgpu::Surface, u32, u32, f64) {
|
||||||
|
// Create a hidden window so we can estimate a good default window size
|
||||||
|
let window = winit::window::WindowBuilder::new()
|
||||||
|
.with_visible(false)
|
||||||
|
.with_title(title)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
let hidpi_factor = window.hidpi_factor();
|
||||||
|
|
||||||
|
// Get dimensions
|
||||||
|
let width = SCREEN_WIDTH as f64;
|
||||||
|
let height = SCREEN_HEIGHT as f64;
|
||||||
|
let (monitor_width, monitor_height) = {
|
||||||
|
let size = window.current_monitor().size();
|
||||||
|
(size.width / hidpi_factor, size.height / hidpi_factor)
|
||||||
|
};
|
||||||
|
let scale = (monitor_height / height * 2.0 / 3.0).round();
|
||||||
|
|
||||||
|
// Resize, center, and display the window
|
||||||
|
let min_size = PhysicalSize::new(width, height).to_logical(hidpi_factor);
|
||||||
|
let default_size = LogicalSize::new(width * scale, height * scale);
|
||||||
|
let center = LogicalPosition::new(
|
||||||
|
(monitor_width - width * scale) / 2.0,
|
||||||
|
(monitor_height - height * scale) / 2.0,
|
||||||
|
);
|
||||||
|
window.set_inner_size(default_size);
|
||||||
|
window.set_min_inner_size(Some(min_size));
|
||||||
|
window.set_outer_position(center);
|
||||||
|
window.set_visible(true);
|
||||||
|
|
||||||
|
let surface = pixels::wgpu::Surface::create(&window);
|
||||||
|
let size = default_size.to_physical(hidpi_factor);
|
||||||
|
|
||||||
|
(
|
||||||
|
window,
|
||||||
|
surface,
|
||||||
|
size.width.round() as u32,
|
||||||
|
size.height.round() as u32,
|
||||||
|
hidpi_factor,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
13
shaders/README.md
Normal file
13
shaders/README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Shaders
|
||||||
|
|
||||||
|
The GLSL shader source is not compiled as part of the normal cargo build process. This was a conscious decision sparked by the current state of the ecosystem; compiling GLSL-to-SPIR-V requires a C++ toolchain including CMake, which is an unacceptable constraint for a simple crate providing a pixel buffer.
|
||||||
|
|
||||||
|
If you need to modify the GLSL sources, you must also recompile the SPIR-V as well. This can be done with `glslang`, `glslc`, etc.
|
||||||
|
|
||||||
|
Compile shaders with `glslangValidator`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
glslangValidator -V shader.frag && glslangValidator -V shader.vert
|
||||||
|
```
|
||||||
|
|
||||||
|
For mor information, see https://github.com/parasyte/pixels/issues/9
|
|
@ -9,6 +9,10 @@ out gl_PerVertex {
|
||||||
|
|
||||||
layout(location = 0) out vec2 v_TexCoord;
|
layout(location = 0) out vec2 v_TexCoord;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 2) uniform Locals {
|
||||||
|
mat4 u_Transform;
|
||||||
|
};
|
||||||
|
|
||||||
const vec2 positions[6] = vec2[6](
|
const vec2 positions[6] = vec2[6](
|
||||||
// Upper left triangle
|
// Upper left triangle
|
||||||
vec2(-1.0, -1.0),
|
vec2(-1.0, -1.0),
|
||||||
|
@ -35,5 +39,5 @@ const vec2 uv[6] = vec2[6](
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
v_TexCoord = uv[gl_VertexIndex];
|
v_TexCoord = uv[gl_VertexIndex];
|
||||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
gl_Position = u_Transform * vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
BIN
shaders/vert.spv
BIN
shaders/vert.spv
Binary file not shown.
60
src/lib.rs
60
src/lib.rs
|
@ -17,7 +17,7 @@ use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub use wgpu;
|
pub use wgpu;
|
||||||
use wgpu::TextureView;
|
use wgpu::{Extent3d, TextureView};
|
||||||
|
|
||||||
mod render_pass;
|
mod render_pass;
|
||||||
pub use render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
pub use render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
||||||
|
@ -25,7 +25,7 @@ pub use render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
||||||
mod renderers;
|
mod renderers;
|
||||||
use renderers::Renderer;
|
use renderers::Renderer;
|
||||||
|
|
||||||
type RenderPassFactory = Box<dyn Fn(Device, Queue, &TextureView) -> BoxedRenderPass>;
|
type RenderPassFactory = Box<dyn Fn(Device, Queue, &TextureView, &Extent3d) -> BoxedRenderPass>;
|
||||||
|
|
||||||
/// A logical texture for a window surface.
|
/// A logical texture for a window surface.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -148,7 +148,7 @@ impl Pixels {
|
||||||
/// Call this method in response to a resize event from your window manager. The size expected
|
/// Call this method in response to a resize event from your window manager. The size expected
|
||||||
/// is in physical pixel units.
|
/// is in physical pixel units.
|
||||||
pub fn resize(&mut self, width: u32, height: u32) {
|
pub fn resize(&mut self, width: u32, height: u32) {
|
||||||
// TODO: Scaling with a uniform transformation matrix
|
// TODO: Call `update_bindings` on each render pass to create a texture chain
|
||||||
|
|
||||||
// Update SurfaceTexture dimensions
|
// Update SurfaceTexture dimensions
|
||||||
self.surface_texture.width = width;
|
self.surface_texture.width = width;
|
||||||
|
@ -165,6 +165,16 @@ impl Pixels {
|
||||||
present_mode: wgpu::PresentMode::Vsync,
|
present_mode: wgpu::PresentMode::Vsync,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update state for all render passes
|
||||||
|
let mut encoder = self
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
|
||||||
|
for renderer in self.renderers.iter_mut() {
|
||||||
|
renderer.resize(&mut encoder, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.queue.borrow_mut().submit(&[encoder.finish()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw this pixel buffer to the configured [`SurfaceTexture`].
|
/// Draw this pixel buffer to the configured [`SurfaceTexture`].
|
||||||
|
@ -205,7 +215,7 @@ impl Pixels {
|
||||||
// Execute all render passes
|
// Execute all render passes
|
||||||
for renderer in self.renderers.iter() {
|
for renderer in self.renderers.iter() {
|
||||||
// TODO: Create a texture chain so that each pass receives the texture drawn by the previous
|
// TODO: Create a texture chain so that each pass receives the texture drawn by the previous
|
||||||
renderer.render_pass(&mut encoder, &frame.view);
|
renderer.render(&mut encoder, &frame.view);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.borrow_mut().submit(&[encoder.finish()]);
|
self.queue.borrow_mut().submit(&[encoder.finish()]);
|
||||||
|
@ -247,13 +257,13 @@ impl PixelsBuilder {
|
||||||
///
|
///
|
||||||
/// impl pixels::RenderPass for MyRenderPass {
|
/// impl pixels::RenderPass for MyRenderPass {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// # fn update_bindings(&mut self, _: &wgpu::TextureView) {}
|
/// # fn update_bindings(&mut self, _: &wgpu::TextureView, _: &wgpu::Extent3d) {}
|
||||||
/// # fn render_pass(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
|
/// # fn render(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
||||||
/// .pixel_aspect_ratio(8.0 / 7.0)
|
/// .pixel_aspect_ratio(8.0 / 7.0)
|
||||||
/// .add_render_pass(|device, queue, texture| {
|
/// .add_render_pass(|device, queue, texture, texture_size| {
|
||||||
/// // Create reources for MyRenderPass here
|
/// // Create reources for MyRenderPass here
|
||||||
/// Box::new(MyRenderPass {
|
/// Box::new(MyRenderPass {
|
||||||
/// // ...
|
/// // ...
|
||||||
|
@ -306,7 +316,13 @@ impl PixelsBuilder {
|
||||||
/// factor.
|
/// factor.
|
||||||
///
|
///
|
||||||
/// E.g. set this to `8.0 / 7.0` for an 8:7 pixel aspect ratio.
|
/// E.g. set this to `8.0 / 7.0` for an 8:7 pixel aspect ratio.
|
||||||
pub const fn pixel_aspect_ratio(mut self, pixel_aspect_ratio: f64) -> PixelsBuilder {
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// The aspect ratio must be > 0.
|
||||||
|
pub fn pixel_aspect_ratio(mut self, pixel_aspect_ratio: f64) -> PixelsBuilder {
|
||||||
|
assert!(pixel_aspect_ratio > 0.0);
|
||||||
|
|
||||||
self.pixel_aspect_ratio = pixel_aspect_ratio;
|
self.pixel_aspect_ratio = pixel_aspect_ratio;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -330,13 +346,14 @@ impl PixelsBuilder {
|
||||||
/// * `device` - A reference-counted [`wgpu::Device`] which allows you to create GPU resources.
|
/// * `device` - A reference-counted [`wgpu::Device`] which allows you to create GPU resources.
|
||||||
/// * `queue` - A reference-counted [`wgpu::Queue`] which can execute command buffers.
|
/// * `queue` - A reference-counted [`wgpu::Queue`] which can execute command buffers.
|
||||||
/// * `texture` - A [`wgpu::TextureView`] reference that is used as the texture input for the
|
/// * `texture` - A [`wgpu::TextureView`] reference that is used as the texture input for the
|
||||||
/// render pass.
|
/// render pass.
|
||||||
|
/// * `texture_size` - A [`wgpu::Extent3d`] providing the input texture size.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use pixels::{BoxedRenderPass, Device, PixelsBuilder, Queue, RenderPass};
|
/// use pixels::{BoxedRenderPass, Device, PixelsBuilder, Queue, RenderPass};
|
||||||
/// use wgpu::TextureView;
|
/// use wgpu::{Extent3d, TextureView};
|
||||||
///
|
///
|
||||||
/// struct MyRenderPass {
|
/// struct MyRenderPass {
|
||||||
/// device: Device,
|
/// device: Device,
|
||||||
|
@ -344,7 +361,12 @@ impl PixelsBuilder {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl MyRenderPass {
|
/// impl MyRenderPass {
|
||||||
/// fn factory(device: Device, queue: Queue, texture: &TextureView) -> BoxedRenderPass {
|
/// fn factory(
|
||||||
|
/// device: Device,
|
||||||
|
/// queue: Queue,
|
||||||
|
/// texture: &TextureView,
|
||||||
|
/// texture_size: &Extent3d,
|
||||||
|
/// ) -> BoxedRenderPass {
|
||||||
/// // Create a bind group, pipeline, etc. and store all of the necessary state...
|
/// // Create a bind group, pipeline, etc. and store all of the necessary state...
|
||||||
/// Box::new(MyRenderPass { device, queue })
|
/// Box::new(MyRenderPass { device, queue })
|
||||||
/// }
|
/// }
|
||||||
|
@ -352,8 +374,8 @@ impl PixelsBuilder {
|
||||||
///
|
///
|
||||||
/// impl RenderPass for MyRenderPass {
|
/// impl RenderPass for MyRenderPass {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// # fn update_bindings(&mut self, _: &wgpu::TextureView) {}
|
/// # fn update_bindings(&mut self, _: &wgpu::TextureView, _: &wgpu::Extent3d) {}
|
||||||
/// # fn render_pass(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
|
/// # fn render(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
||||||
|
@ -365,7 +387,7 @@ impl PixelsBuilder {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_render_pass(
|
pub fn add_render_pass(
|
||||||
mut self,
|
mut self,
|
||||||
factory: impl Fn(Device, Queue, &TextureView) -> BoxedRenderPass + 'static,
|
factory: impl Fn(Device, Queue, &TextureView, &Extent3d) -> BoxedRenderPass + 'static,
|
||||||
) -> PixelsBuilder {
|
) -> PixelsBuilder {
|
||||||
self.renderer_factories.push(Box::new(factory));
|
self.renderer_factories.push(Box::new(factory));
|
||||||
self
|
self
|
||||||
|
@ -430,20 +452,26 @@ impl PixelsBuilder {
|
||||||
device.clone(),
|
device.clone(),
|
||||||
queue.clone(),
|
queue.clone(),
|
||||||
&texture_view,
|
&texture_view,
|
||||||
|
&texture_extent,
|
||||||
)];
|
)];
|
||||||
|
|
||||||
// Create all render passes
|
// Create all render passes
|
||||||
renderers.extend(self.renderer_factories.iter().map(|f| {
|
renderers.extend(self.renderer_factories.iter().map(|f| {
|
||||||
// TODO: Create a texture chain so that each pass recieves the texture drawn by the previous
|
// TODO: Create a texture chain so that each pass recieves the texture drawn by the previous
|
||||||
f(device.clone(), queue.clone(), &texture_view)
|
f(
|
||||||
|
device.clone(),
|
||||||
|
queue.clone(),
|
||||||
|
&texture_view,
|
||||||
|
&texture_extent,
|
||||||
|
)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Ok(Pixels {
|
Ok(Pixels {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
swap_chain,
|
swap_chain,
|
||||||
renderers,
|
|
||||||
surface_texture,
|
surface_texture,
|
||||||
|
renderers,
|
||||||
texture,
|
texture,
|
||||||
texture_extent,
|
texture_extent,
|
||||||
texture_format_size,
|
texture_format_size,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wgpu::TextureView;
|
use wgpu::{Extent3d, TextureView};
|
||||||
|
|
||||||
/// A reference-counted [`wgpu::Device`]
|
/// A reference-counted [`wgpu::Device`]
|
||||||
pub type Device = Rc<wgpu::Device>;
|
pub type Device = Rc<wgpu::Device>;
|
||||||
|
@ -29,17 +29,6 @@ pub type BoxedRenderPass = Box<dyn RenderPass>;
|
||||||
///
|
///
|
||||||
/// [`Pixels`]: ./struct.Pixels.html
|
/// [`Pixels`]: ./struct.Pixels.html
|
||||||
pub trait RenderPass {
|
pub trait RenderPass {
|
||||||
/// This method will be called when the input [`wgpu::TextureView`] needs to be rebinded.
|
|
||||||
///
|
|
||||||
/// A [`wgpu::TextureView`] is provided to the `RenderPass` factory as an input texture with
|
|
||||||
/// the original [`SurfaceTexture`] size. This method is called in response to resizing the
|
|
||||||
/// [`SurfaceTexture`], where your `RenderPass` impl can update its input texture for the new
|
|
||||||
/// size.
|
|
||||||
///
|
|
||||||
/// [`Pixels`]: ./struct.Pixels.html
|
|
||||||
/// [`SurfaceTexture`]: ./struct.SurfaceTexture.html
|
|
||||||
fn update_bindings(&mut self, input_texture: &TextureView);
|
|
||||||
|
|
||||||
/// Called when it is time to execute this render pass. Use the `encoder` to encode all
|
/// Called when it is time to execute this render pass. Use the `encoder` to encode all
|
||||||
/// commands related to this render pass. The result must be stored to the `render_target`.
|
/// commands related to this render pass. The result must be stored to the `render_target`.
|
||||||
///
|
///
|
||||||
|
@ -47,7 +36,34 @@ pub trait RenderPass {
|
||||||
/// * `encoder` - Command encoder for the render pass
|
/// * `encoder` - Command encoder for the render pass
|
||||||
/// * `render_target` - A reference to the output texture
|
/// * `render_target` - A reference to the output texture
|
||||||
/// * `texels` - The byte slice passed to `Pixels::render`
|
/// * `texels` - The byte slice passed to `Pixels::render`
|
||||||
fn render_pass(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView);
|
fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView);
|
||||||
|
|
||||||
|
/// This method will be called when the input [`wgpu::TextureView`] needs to be rebinded.
|
||||||
|
///
|
||||||
|
/// A [`wgpu::TextureView`] is provided to the `RenderPass` factory as an input texture with
|
||||||
|
/// the original [`SurfaceTexture`] size. This method is called in response to resizing the
|
||||||
|
/// [`SurfaceTexture`], where your `RenderPass` impl can update its input texture for the new
|
||||||
|
/// size.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `input_texture` - A reference to the `TextureView` for this render pass's input
|
||||||
|
/// * `input_texture_size` - The `input_texture` size
|
||||||
|
///
|
||||||
|
/// [`Pixels`]: ./struct.Pixels.html
|
||||||
|
/// [`SurfaceTexture`]: ./struct.SurfaceTexture.html
|
||||||
|
fn update_bindings(&mut self, input_texture: &TextureView, input_texture_size: &Extent3d);
|
||||||
|
|
||||||
|
/// When the window is resized, this method will be called, allowing the render pass to
|
||||||
|
/// customize itself to the display size.
|
||||||
|
///
|
||||||
|
/// The default implementation is a no-op.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `encoder` - Command encoder for the render pass
|
||||||
|
/// * `width` - Render target width in physical pixel units
|
||||||
|
/// * `height` - Render target height in physical pixel units
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn resize(&mut self, encoder: &mut wgpu::CommandEncoder, width: u32, height: u32) {}
|
||||||
|
|
||||||
/// This function implements [`Debug`](fmt::Debug) for trait objects.
|
/// This function implements [`Debug`](fmt::Debug) for trait objects.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wgpu::{self, TextureView};
|
use wgpu::{self, Extent3d, TextureView};
|
||||||
|
|
||||||
use crate::render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
use crate::render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
||||||
|
|
||||||
|
@ -9,8 +9,11 @@ use crate::render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Renderer {
|
pub(crate) struct Renderer {
|
||||||
device: Rc<wgpu::Device>,
|
device: Rc<wgpu::Device>,
|
||||||
|
uniform_buffer: wgpu::Buffer,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
render_pipeline: wgpu::RenderPipeline,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
|
@ -19,6 +22,7 @@ impl Renderer {
|
||||||
device: Device,
|
device: Device,
|
||||||
_queue: Queue,
|
_queue: Queue,
|
||||||
texture_view: &TextureView,
|
texture_view: &TextureView,
|
||||||
|
texture_size: &Extent3d,
|
||||||
) -> BoxedRenderPass {
|
) -> BoxedRenderPass {
|
||||||
let vert_spv = include_bytes!("../shaders/vert.spv");
|
let vert_spv = include_bytes!("../shaders/vert.spv");
|
||||||
let mut vert = Vec::new();
|
let mut vert = Vec::new();
|
||||||
|
@ -52,6 +56,18 @@ impl Renderer {
|
||||||
compare_function: wgpu::CompareFunction::Always,
|
compare_function: wgpu::CompareFunction::Always,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create uniform buffer
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let transform: [f32; 16] = [
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
|
];
|
||||||
|
let uniform_buffer = device
|
||||||
|
.create_buffer_mapped(16, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST)
|
||||||
|
.fill_from_slice(&transform);
|
||||||
|
|
||||||
// Create bind group
|
// Create bind group
|
||||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
bindings: &[
|
bindings: &[
|
||||||
|
@ -68,6 +84,11 @@ impl Renderer {
|
||||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||||
ty: wgpu::BindingType::Sampler,
|
ty: wgpu::BindingType::Sampler,
|
||||||
},
|
},
|
||||||
|
wgpu::BindGroupLayoutBinding {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStage::VERTEX,
|
||||||
|
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
@ -81,6 +102,13 @@ impl Renderer {
|
||||||
binding: 1,
|
binding: 1,
|
||||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||||
},
|
},
|
||||||
|
wgpu::Binding {
|
||||||
|
binding: 2,
|
||||||
|
resource: wgpu::BindingResource::Buffer {
|
||||||
|
buffer: &uniform_buffer,
|
||||||
|
range: 0..64,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -122,16 +150,17 @@ impl Renderer {
|
||||||
|
|
||||||
Box::new(Renderer {
|
Box::new(Renderer {
|
||||||
device,
|
device,
|
||||||
|
uniform_buffer,
|
||||||
bind_group,
|
bind_group,
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
|
width: texture_size.width as f32,
|
||||||
|
height: texture_size.height as f32,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderPass for Renderer {
|
impl RenderPass for Renderer {
|
||||||
fn update_bindings(&mut self, _input_texture: &TextureView) {}
|
fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView) {
|
||||||
|
|
||||||
fn render_pass(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView) {
|
|
||||||
// Draw the updated texture to the render target
|
// 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 {
|
||||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||||
|
@ -148,6 +177,38 @@ impl RenderPass for Renderer {
|
||||||
rpass.draw(0..6, 0..1);
|
rpass.draw(0..6, 0..1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, encoder: &mut wgpu::CommandEncoder, width: u32, height: u32) {
|
||||||
|
let width = width as f32;
|
||||||
|
let height = height as f32;
|
||||||
|
|
||||||
|
// Get smallest scale size
|
||||||
|
let scale = (width / self.width)
|
||||||
|
.min(height / self.height)
|
||||||
|
.max(1.0)
|
||||||
|
.floor();
|
||||||
|
|
||||||
|
// Update transformation matrix
|
||||||
|
let sw = self.width * scale / width;
|
||||||
|
let sh = self.height * scale / height;
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let transform: [f32; 16] = [
|
||||||
|
sw, 0.0, 0.0, 0.0,
|
||||||
|
0.0, sh, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let temp_buf = self
|
||||||
|
.device
|
||||||
|
.create_buffer_mapped(16, wgpu::BufferUsage::COPY_SRC)
|
||||||
|
.fill_from_slice(&transform);
|
||||||
|
encoder.copy_buffer_to_buffer(&temp_buf, 0, &self.uniform_buffer, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't actually have to rebind the TextureView here.
|
||||||
|
// It's guaranteed that the initial texture never changes.
|
||||||
|
fn update_bindings(&mut self, _input_texture: &TextureView, _input_texture_size: &Extent3d) {}
|
||||||
|
|
||||||
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{:?}", self)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue