Skip to content

Commit

Permalink
feat: Add a function for retrieving the window contents
Browse files Browse the repository at this point in the history
This function is useful for testing the window contents in certain cases. In addition,
this means that we can now have reliable tests for softbuffer's actual functionality.

Signed-off-by: John Nunley <[email protected]>
Co-authored-by: dAxpeDDa <[email protected]>
  • Loading branch information
notgull and daxpedda authored Jun 2, 2023
1 parent daf304a commit 4424847
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[alias]
run-wasm = ["run", "--release", "--package", "run-wasm", "--"]

[target.wasm32-unknown-unknown]
runner = "wasm-bindgen-test-runner"
31 changes: 20 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ jobs:
- { target: x86_64-unknown-freebsd, os: ubuntu-latest, }
- { target: x86_64-unknown-netbsd, os: ubuntu-latest, }
- { target: x86_64-apple-darwin, os: macos-latest, }
# We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web
# doesn't currently work on Linux.
- { target: wasm32-unknown-unknown, os: windows-latest, }
- { target: wasm32-unknown-unknown, os: ubuntu-latest, }
include:
- rust_version: nightly
platform: { target: wasm32-unknown-unknown, os: windows-latest, options: "-Zbuild-std=panic_abort,std", rustflags: "-Ctarget-feature=+atomics,+bulk-memory" }
platform: { target: wasm32-unknown-unknown, os: ubuntu-latest, options: "-Zbuild-std=panic_abort,std", rustflags: "-Ctarget-feature=+atomics,+bulk-memory" }

env:
RUST_BACKTRACE: 1
Expand All @@ -67,12 +65,10 @@ jobs:
steps:
- uses: actions/checkout@v3

# Used to cache cargo-web
- name: Cache cargo folder
uses: actions/cache@v3
- uses: taiki-e/install-action@v2
if: matrix.platform.target == 'wasm32-unknown-unknown'
with:
path: ~/.cargo
key: ${{ matrix.platform.target }}-cargo-${{ matrix.rust_version }}
tool: wasm-bindgen-cli

- uses: hecrj/setup-rust-action@v1
with:
Expand Down Expand Up @@ -102,12 +98,25 @@ jobs:
shell: bash
if: >
!((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) &&
!contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
!contains(matrix.platform.target, 'freebsd') &&
!contains(matrix.platform.target, 'netbsd')
!contains(matrix.platform.target, 'netbsd') &&
!contains(matrix.platform.target, 'linux')
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES

# TODO: We should also be using Wayland for testing here.
- name: Run tests using Xvfb
shell: bash
if: >
!((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) &&
!contains(matrix.platform.target, 'redox') &&
!contains(matrix.platform.target, 'freebsd') &&
!contains(matrix.platform.target, 'netbsd') &&
contains(matrix.platform.target, 'linux') &&
!contains(matrix.platform.options, '--no-default-features') &&
!contains(matrix.platform.features, 'wayland')
run: xvfb-run cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES

- name: Lint with clippy
shell: bash
if: >
Expand Down
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ cfg_aliases = "0.1.1"
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
instant = "0.1.12"
winit = "0.28.1"
winit-test = "0.1.0"

[dev-dependencies.image]
version = "0.24.6"
Expand All @@ -81,11 +82,19 @@ features = ["jpeg"]
image = "0.24.6"
rayon = "1.5.1"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3"

[workspace]
members = [
"run-wasm",
]

[[test]]
name = "present_and_fetch"
path = "tests/present_and_fetch.rs"
harness = false

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Expand Down
5 changes: 5 additions & 0 deletions src/cg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ impl CGImpl {
imp: self,
})
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

pub struct BufferImpl<'a> {
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub enum SoftBufferError {

#[error("Platform error")]
PlatformError(Option<String>, Option<Box<dyn Error>>),

#[error("This function is unimplemented on this platform")]
Unimplemented,
}

/// Convenient wrapper to cast errors into SoftBufferError.
Expand Down
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ macro_rules! make_dispatch {
)*
}
}

pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.fetch(),
)*
}
}
}

enum BufferDispatch<'a> {
Expand Down Expand Up @@ -306,6 +315,18 @@ impl Surface {
self.surface_impl.resize(width, height)
}

/// Copies the window contents into a buffer.
///
/// ## Platform Dependent Behavior
///
/// - On X11, the window must be visible.
/// - On macOS, Redox and Wayland, this function is unimplemented.
/// - On Web, this will fail if the content was supplied by
/// a different origin depending on the sites CORS rules.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
self.surface_impl.fetch()
}

/// Return a [`Buffer`] that the next frame should be rendered into. The size must
/// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or
/// may contain a previous frame.
Expand Down
5 changes: 5 additions & 0 deletions src/orbital.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ impl OrbitalImpl {
// Tell orbital to show the latest window data
syscall::fsync(self.window_fd()).expect("failed to sync orbital window");
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

enum Pixels {
Expand Down
5 changes: 5 additions & 0 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ impl WaylandImpl {
Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() })
})?))
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

pub struct BufferImpl<'a>(util::BorrowStack<'a, WaylandImpl, [u32]>);
Expand Down
32 changes: 29 additions & 3 deletions src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ pub struct WebDisplayImpl {
impl WebDisplayImpl {
pub(super) fn new() -> Result<Self, SoftBufferError> {
let document = web_sys::window()
.swbuf_err("`window` is not present in this runtime")?
.swbuf_err("`Window` is not present in this runtime")?
.document()
.swbuf_err("`document` is not present in this runtime")?;
.swbuf_err("`Document` is not present in this runtime")?;

Ok(Self { document })
}
Expand All @@ -44,6 +44,9 @@ pub struct WebImpl {

/// The current width of the canvas.
width: u32,

/// The current height of the canvas.
height: u32,
}

impl WebImpl {
Expand Down Expand Up @@ -76,6 +79,7 @@ impl WebImpl {
ctx,
buffer: Vec::new(),
width: 0,
height: 0,
})
}

Expand All @@ -92,18 +96,40 @@ impl WebImpl {
self.canvas.set_width(width);
self.canvas.set_height(height);
self.width = width;
self.height = height;
Ok(())
}

/// Get a pointer to the mutable buffer.
pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl, SoftBufferError> {
Ok(BufferImpl { imp: self })
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
let image_data = self
.ctx
.get_image_data(0., 0., self.width.into(), self.height.into())
.ok()
// TODO: Can also error if width or height are 0.
.swbuf_err("`Canvas` contains pixels from a different origin")?;

Ok(image_data
.data()
.0
.chunks_exact(4)
.map(|chunk| u32::from_be_bytes([0, chunk[0], chunk[1], chunk[2]]))
.collect())
}
}

/// Extension methods for the Wasm target on [`Surface`](crate::Surface).
pub trait SurfaceExtWeb: Sized {
/// Creates a new instance of this struct, using the provided [`HtmlCanvasElement`].
///
/// # Errors
/// - If the canvas was already controlled by an `OffscreenCanvas`.
/// - If a another context then "2d" was already created for this canvas.
fn from_canvas(canvas: HtmlCanvasElement) -> Result<Self, SoftBufferError>;
}

Expand Down Expand Up @@ -171,7 +197,7 @@ impl<'a> BufferImpl<'a> {
let image_data = result.unwrap();

// This can only throw an error if `data` is detached, which is impossible.
self.imp.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap();
self.imp.ctx.put_image_data(&image_data, 0., 0.).unwrap();

Ok(())
}
Expand Down
28 changes: 28 additions & 0 deletions src/win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,34 @@ impl Win32Impl {

Ok(BufferImpl(self))
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
let buffer = self.buffer.as_ref().unwrap();
let temp_buffer = Buffer::new(self.dc, buffer.width, buffer.height);

// Just go the other way.
unsafe {
Gdi::BitBlt(
temp_buffer.dc,
0,
0,
temp_buffer.width.get(),
temp_buffer.height.get(),
self.dc,
0,
0,
Gdi::SRCCOPY,
);
}

// Flush the operation so that it happens immediately.
unsafe {
Gdi::GdiFlush();
}

Ok(temp_buffer.pixels().to_vec())
}
}

pub struct BufferImpl<'a>(&'a mut Win32Impl);
Expand Down
Loading

0 comments on commit 4424847

Please sign in to comment.