Update public API to provide &mut [u8] instead of accepting &[u8] from the app (#22)

- Closes #19
This commit is contained in:
Jay Oster 2019-10-21 23:17:58 -07:00 committed by GitHub
parent b18690f19f
commit 8801bad58a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 39 deletions

View file

@ -42,7 +42,7 @@ fn main() -> Result<(), Error> {
}; };
let surface_texture = SurfaceTexture::new(width, height, surface); let surface_texture = SurfaceTexture::new(width, height, surface);
let mut pixels = Pixels::new(224, 256, 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);
let mut time = Instant::now(); let mut time = Instant::now();
let mut controls = Controls::default(); let mut controls = Controls::default();
@ -54,7 +54,8 @@ fn main() -> Result<(), Error> {
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
.. ..
} => { } => {
pixels.render(invaders.draw()); invaders.draw(pixels.get_frame());
pixels.render();
} }
_ => (), _ => (),
} }

View file

@ -48,7 +48,6 @@ pub struct World {
collision: Collision, collision: Collision,
score: u32, score: u32,
assets: Assets, assets: Assets,
screen: Vec<u8>,
dt: Duration, dt: Duration,
gameover: bool, gameover: bool,
random: OsRng, random: OsRng,
@ -149,10 +148,6 @@ impl World {
let collision = Collision::default(); let collision = Collision::default();
let score = 0; let score = 0;
// Create a screen with the correct size
let mut screen = Vec::new();
screen.resize_with(SCREEN_WIDTH * SCREEN_HEIGHT * 4, Default::default);
let dt = Duration::default(); let dt = Duration::default();
let gameover = false; let gameover = false;
let random = OsRng; let random = OsRng;
@ -166,7 +161,6 @@ impl World {
collision, collision,
score, score,
assets, assets,
screen,
dt, dt,
gameover, gameover,
random, random,
@ -261,47 +255,45 @@ impl World {
/// Draw the internal state to the screen. /// Draw the internal state to the screen.
/// ///
/// Calling this method more than once without an `update` call between is a no-op. /// Calling this method more than once without an `update` call between is a no-op.
pub fn draw(&mut self) -> &[u8] { pub fn draw(&mut self, screen: &mut [u8]) {
// Clear the screen // Clear the screen
self.clear(); clear(screen);
// Draw the invaders // Draw the invaders
for row in &self.invaders.grid { for row in &self.invaders.grid {
for col in row { for col in row {
if let Some(invader) = col { if let Some(invader) = col {
blit(&mut self.screen, &invader.pos, &invader.sprite); blit(screen, &invader.pos, &invader.sprite);
} }
} }
} }
// Draw the shields // Draw the shields
for shield in &self.shields { for shield in &self.shields {
blit(&mut self.screen, &shield.pos, &shield.sprite); blit(screen, &shield.pos, &shield.sprite);
} }
// Draw the player // Draw the player
blit(&mut self.screen, &self.player.pos, &self.player.sprite); blit(screen, &self.player.pos, &self.player.sprite);
// Draw the bullet // Draw the bullet
if let Some(bullet) = &self.bullet { if let Some(bullet) = &self.bullet {
blit(&mut self.screen, &bullet.pos, &bullet.sprite); blit(screen, &bullet.pos, &bullet.sprite);
} }
// Draw lasers // Draw lasers
for laser in self.lasers.iter() { for laser in self.lasers.iter() {
blit(&mut self.screen, &laser.pos, &laser.sprite); blit(screen, &laser.pos, &laser.sprite);
} }
// Draw debug information // Draw debug information
if self.debug { if self.debug {
debug::draw_invaders(&mut self.screen, &self.invaders, &self.collision); debug::draw_invaders(screen, &self.invaders, &self.collision);
debug::draw_bullet(&mut self.screen, self.bullet.as_ref()); debug::draw_bullet(screen, self.bullet.as_ref());
debug::draw_lasers(&mut self.screen, &self.lasers); debug::draw_lasers(screen, &self.lasers);
debug::draw_player(&mut self.screen, &self.player, &self.collision); debug::draw_player(screen, &self.player, &self.collision);
debug::draw_shields(&mut self.screen, &self.shields, &self.collision); debug::draw_shields(screen, &self.shields, &self.collision);
} }
&self.screen
} }
fn step_invaders(&mut self) { fn step_invaders(&mut self) {
@ -406,13 +398,6 @@ impl World {
}); });
} }
} }
/// Clear the screen
fn clear(&mut self) {
for (i, byte) in self.screen.iter_mut().enumerate() {
*byte = if i % 4 == 3 { 255 } else { 0 };
}
}
} }
impl Default for World { impl Default for World {
@ -522,6 +507,13 @@ impl Default for Bounds {
} }
} }
/// Clear the screen
fn clear(screen: &mut [u8]) {
for (i, byte) in screen.iter_mut().enumerate() {
*byte = if i % 4 == 3 { 255 } else { 0 };
}
}
/// Create a grid of invaders. /// Create a grid of invaders.
fn make_invader_grid(assets: &Assets) -> Vec<Vec<Option<Invader>>> { fn make_invader_grid(assets: &Assets) -> Vec<Vec<Option<Invader>>> {
use Frame::*; use Frame::*;

View file

@ -53,6 +53,7 @@ pub struct Pixels {
texture: wgpu::Texture, texture: wgpu::Texture,
texture_extent: wgpu::Extent3d, texture_extent: wgpu::Extent3d,
texture_format_size: u32, texture_format_size: u32,
pixels: Vec<u8>,
} }
/// A builder to help create customized pixel buffers. /// A builder to help create customized pixel buffers.
@ -124,7 +125,7 @@ impl Pixels {
/// # use pixels::Pixels; /// # use pixels::Pixels;
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH); /// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface); /// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface);
/// let fb = Pixels::new(320, 240, surface_texture)?; /// let mut pixels = Pixels::new(320, 240, surface_texture)?;
/// # Ok::<(), pixels::Error>(()) /// # Ok::<(), pixels::Error>(())
/// ``` /// ```
/// ///
@ -169,12 +170,7 @@ impl Pixels {
/// Draw this pixel buffer to the configured [`SurfaceTexture`]. /// Draw this pixel buffer to the configured [`SurfaceTexture`].
/// ///
/// This executes all render passes in sequence. See [`RenderPass`]. /// This executes all render passes in sequence. See [`RenderPass`].
/// pub fn render(&mut self) {
/// # Arguments
///
/// * `texels` - Byte slice of texture pixels (AKA texels) to draw. The texture format can be
/// configured with [`PixelsBuilder`].
pub fn render(&mut self, texels: &[u8]) {
// TODO: Center frame buffer in surface // TODO: Center frame buffer in surface
let frame = self.swap_chain.get_next_texture(); let frame = self.swap_chain.get_next_texture();
let mut encoder = self let mut encoder = self
@ -184,8 +180,8 @@ impl Pixels {
// Update the pixel buffer texture view // Update the pixel buffer texture view
let buffer = self let buffer = self
.device .device
.create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC) .create_buffer_mapped(self.pixels.len(), wgpu::BufferUsage::COPY_SRC)
.fill_from_slice(texels); .fill_from_slice(&self.pixels);
encoder.copy_buffer_to_texture( encoder.copy_buffer_to_texture(
wgpu::BufferCopyView { wgpu::BufferCopyView {
buffer: &buffer, buffer: &buffer,
@ -214,6 +210,26 @@ impl Pixels {
self.queue.borrow_mut().submit(&[encoder.finish()]); self.queue.borrow_mut().submit(&[encoder.finish()]);
} }
/// Get a mutable byte slice for the pixel buffer. The buffer is _not_ cleared for you; it will
/// retain the previous frame's contents until you clear it yourself.
///
/// # Example
///
/// ```no_run
/// # use pixels::Pixels;
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface);
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
/// let frame = pixels.get_frame();
/// for pixel in frame {
/// *pixel = 0;
/// }
/// # Ok::<(), pixels::Error>(())
/// ```
pub fn get_frame(&mut self) -> &mut [u8] {
&mut self.pixels
}
} }
impl PixelsBuilder { impl PixelsBuilder {
@ -235,7 +251,7 @@ impl PixelsBuilder {
/// # fn render_pass(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {} /// # fn render_pass(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
/// } /// }
/// ///
/// let fb = 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| {
/// // Create reources for MyRenderPass here /// // Create reources for MyRenderPass here
@ -391,6 +407,11 @@ impl PixelsBuilder {
let texture_view = texture.create_default_view(); let texture_view = texture.create_default_view();
let texture_format_size = get_texture_format_size(self.texture_format); let texture_format_size = get_texture_format_size(self.texture_format);
// Create the pixel buffer
let capacity = (width * height * texture_format_size) as usize;
let mut pixels = Vec::with_capacity(capacity);
pixels.resize_with(capacity, Default::default);
// Create swap chain // Create swap chain
let surface_texture = self.surface_texture; let surface_texture = self.surface_texture;
let swap_chain = device.create_swap_chain( let swap_chain = device.create_swap_chain(
@ -426,6 +447,7 @@ impl PixelsBuilder {
texture, texture,
texture_extent, texture_extent,
texture_format_size, texture_format_size,
pixels,
}) })
} }
} }