Owned pixel buffer for no-copy presentation

This is based on the API that will be used for no-copy presentation. But
wraps it in `set_buffer`.

This also fixes the Wayland buffer code to set `self.width` and
`self.height` on resize, and set the length of the shared memory file
when the buffer is created.

Co-authored-by: jtnunley <jtnunley01@gmail.com>
This commit is contained in:
Ian Douglas Scott 2023-04-06 00:30:59 -07:00 committed by GitHub
parent e5d546ff9e
commit a09e4cf679
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1176 additions and 438 deletions

View file

@ -1,3 +1,7 @@
//! Implementation of software buffering for web targets.
#![allow(clippy::uninlined_format_args)]
use raw_window_handle::WebWindowHandle;
use wasm_bindgen::Clamped;
use wasm_bindgen::JsCast;
@ -6,6 +10,8 @@ use web_sys::HtmlCanvasElement;
use web_sys::ImageData;
use crate::SoftBufferError;
use std::convert::TryInto;
use std::num::NonZeroU32;
/// Display implementation for the web platform.
///
@ -36,8 +42,17 @@ impl WebDisplayImpl {
}
pub struct WebImpl {
/// The handle to the canvas that we're drawing to.
canvas: HtmlCanvasElement,
/// The 2D rendering context for the canvas.
ctx: CanvasRenderingContext2d,
/// The buffer that we're drawing to.
buffer: Vec<u32>,
/// The current width of the canvas.
width: u32,
}
impl WebImpl {
@ -73,24 +88,82 @@ impl WebImpl {
.dyn_into()
.expect("`getContext(\"2d\") didn't return a `CanvasRenderingContext2d`");
Ok(Self { canvas, ctx })
Ok(Self {
canvas,
ctx,
buffer: Vec::new(),
width: 0,
})
}
pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
self.canvas.set_width(width.into());
self.canvas.set_height(height.into());
/// Resize the canvas to the given dimensions.
pub(crate) fn resize(
&mut self,
width: NonZeroU32,
height: NonZeroU32,
) -> Result<(), SoftBufferError> {
let width = width.get();
let height = height.get();
let bitmap: Vec<_> = buffer
self.buffer.resize(total_len(width, height), 0);
self.canvas.set_width(width);
self.canvas.set_height(height);
self.width = width;
Ok(())
}
/// Get a pointer to the mutable buffer.
pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl, SoftBufferError> {
Ok(BufferImpl { imp: self })
}
}
pub struct BufferImpl<'a> {
imp: &'a mut WebImpl,
}
impl<'a> BufferImpl<'a> {
pub fn pixels(&self) -> &[u32] {
&self.imp.buffer
}
pub fn pixels_mut(&mut self) -> &mut [u32] {
&mut self.imp.buffer
}
/// Push the buffer to the canvas.
pub fn present(self) -> Result<(), SoftBufferError> {
// Create a bitmap from the buffer.
let bitmap: Vec<_> = self
.imp
.buffer
.iter()
.copied()
.flat_map(|pixel| [(pixel >> 16) as u8, (pixel >> 8) as u8, pixel as u8, 255])
.collect();
// This should only throw an error if the buffer we pass's size is incorrect, which is checked in the outer `set_buffer` call.
// This should only throw an error if the buffer we pass's size is incorrect.
let image_data =
ImageData::new_with_u8_clamped_array(Clamped(&bitmap), width.into()).unwrap();
ImageData::new_with_u8_clamped_array(Clamped(&bitmap), self.imp.width).unwrap();
// This can only throw an error if `data` is detached, which is impossible.
self.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap();
self.imp.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap();
Ok(())
}
}
#[inline(always)]
fn total_len(width: u32, height: u32) -> usize {
// Convert width and height to `usize`, then multiply.
width
.try_into()
.ok()
.and_then(|w: usize| height.try_into().ok().and_then(|h| w.checked_mul(h)))
.unwrap_or_else(|| {
panic!(
"Overflow when calculating total length of buffer: {}x{}",
width, height
);
})
}