Initial WebGL2 support. (#218)
Co-authored-by: MarkAnthonyM <37249494+MarkAnthonyM@users.noreply.github.com>
This commit is contained in:
parent
acd42495f2
commit
b185ec32e9
81
.github/workflows/ci.yml
vendored
81
.github/workflows/ci.yml
vendored
|
@ -32,33 +32,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
args: --all
|
args: --all
|
||||||
tests:
|
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- 1.52.0
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Update apt repos
|
|
||||||
run: sudo apt-get -y update
|
|
||||||
- name: Install dependencies
|
|
||||||
run: sudo apt -y install libsdl2-dev
|
|
||||||
- name: Install toolchain
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: ${{ matrix.rust }}
|
|
||||||
override: true
|
|
||||||
- name: Cargo test
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --all
|
|
||||||
lints:
|
lints:
|
||||||
name: Lints
|
name: Lints
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -91,3 +64,57 @@ jobs:
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
args: --all --tests -- -D warnings
|
args: --all --tests -- -D warnings
|
||||||
|
tests:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [checks, lints]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
rust:
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
- 1.52.0
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Update apt repos
|
||||||
|
run: sudo apt-get -y update
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt -y install libsdl2-dev
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
override: true
|
||||||
|
- name: Cargo test
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --all
|
||||||
|
wasm:
|
||||||
|
name: WASM
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [checks, lints]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- minimal-web
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Update apt repos
|
||||||
|
run: sudo apt-get -y update
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt -y install libsdl2-dev
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: wasm32-unknown-unknown
|
||||||
|
override: true
|
||||||
|
- name: Install tools
|
||||||
|
run: cargo install --locked wasm-bindgen-cli just
|
||||||
|
- name: Just build
|
||||||
|
run: just build ${{ matrix.example }}
|
||||||
|
|
|
@ -20,12 +20,14 @@ include = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck = "1.7"
|
bytemuck = "1.7"
|
||||||
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.11"
|
wgpu = "0.11"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
pollster = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pixels-mocks = { path = "internals/pixels-mocks" }
|
pixels-mocks = { path = "internals/pixels-mocks" }
|
||||||
winit = "0.25"
|
winit = "0.25"
|
||||||
|
|
|
@ -32,6 +32,7 @@ The Minimum Supported Rust Version for `pixels` will always be made available in
|
||||||
- [Custom Shader](./examples/custom-shader)
|
- [Custom Shader](./examples/custom-shader)
|
||||||
- [Dear ImGui example with `winit`](./examples/imgui-winit)
|
- [Dear ImGui example with `winit`](./examples/imgui-winit)
|
||||||
- [Egui example with `winit`](./examples/minimal-egui)
|
- [Egui example with `winit`](./examples/minimal-egui)
|
||||||
|
- [Minimal example for WebGL2](./examples/minimal-web)
|
||||||
- [Minimal example with SDL2](./examples/minimal-sdl2)
|
- [Minimal example with SDL2](./examples/minimal-sdl2)
|
||||||
- [Minimal example with `winit`](./examples/minimal-winit)
|
- [Minimal example with `winit`](./examples/minimal-winit)
|
||||||
- [Minimal example with `fltk`](./examples/minimal-fltk)
|
- [Minimal example with `fltk`](./examples/minimal-fltk)
|
||||||
|
|
30
examples/minimal-web/Cargo.toml
Normal file
30
examples/minimal-web/Cargo.toml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[package]
|
||||||
|
name = "minimal-web"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jay Oster <jay@kodewerx.org>"]
|
||||||
|
edition = "2018"
|
||||||
|
resolver = "2"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
optimize = ["log/release_max_level_warn"]
|
||||||
|
web = ["wgpu/webgl", "winit/web-sys"]
|
||||||
|
default = ["optimize"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
pixels = { path = "../.." }
|
||||||
|
wgpu = "0.11"
|
||||||
|
winit = "0.25"
|
||||||
|
winit_input_helper = "0.10"
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
console_log = "0.2"
|
||||||
|
wasm-bindgen = "0.2.78"
|
||||||
|
wasm-bindgen-futures = "0.4"
|
||||||
|
web-sys = "0.3"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
env_logger = "0.9"
|
||||||
|
pollster = "0.2"
|
42
examples/minimal-web/README.md
Normal file
42
examples/minimal-web/README.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Hello Pixels + Web
|
||||||
|
|
||||||
|
![Hello Pixels + Web](../../img/minimal-web.png)
|
||||||
|
|
||||||
|
Minimal example for WebGL2.
|
||||||
|
|
||||||
|
## Install build dependencies
|
||||||
|
|
||||||
|
Install the WASM32 target and a few CLI tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
cargo install --locked wasm-bindgen-cli just miniserve
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running on the Web
|
||||||
|
|
||||||
|
Build the project and start a local server to host it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just serve minimal-web
|
||||||
|
```
|
||||||
|
|
||||||
|
Open http://localhost:8080/ in your browser to run the example.
|
||||||
|
|
||||||
|
To build the project without serving it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just build minimal-web
|
||||||
|
```
|
||||||
|
|
||||||
|
The build files are stored in `./target/minimal-web/`.
|
||||||
|
|
||||||
|
## Running on native targets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --release --package minimal-web
|
||||||
|
```
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
This example is based on `minimal-winit`, demonstrating how to build your app for WebGL2 targets.
|
24
examples/minimal-web/index.html
Normal file
24
examples/minimal-web/index.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background-color: #000;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>Hello Pixels + Web</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import init from "./minimal-web.js";
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
190
examples/minimal-web/src/main.rs
Normal file
190
examples/minimal-web/src/main.rs
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
#![deny(clippy::all)]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use pixels::{Pixels, SurfaceTexture};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
use winit::event::{Event, VirtualKeyCode};
|
||||||
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use winit::window::WindowBuilder;
|
||||||
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
|
const WIDTH: u32 = 320;
|
||||||
|
const HEIGHT: u32 = 240;
|
||||||
|
const BOX_SIZE: i16 = 64;
|
||||||
|
|
||||||
|
/// Representation of the application state. In this example, a box will bounce around the screen.
|
||||||
|
struct World {
|
||||||
|
box_x: i16,
|
||||||
|
box_y: i16,
|
||||||
|
velocity_x: i16,
|
||||||
|
velocity_y: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
|
console_log::init_with_level(log::Level::Trace).expect("error initializing logger");
|
||||||
|
|
||||||
|
wasm_bindgen_futures::spawn_local(run());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
pollster::block_on(run());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run() {
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let window = {
|
||||||
|
let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
|
||||||
|
WindowBuilder::new()
|
||||||
|
.with_title("Hello Pixels + Web")
|
||||||
|
.with_inner_size(size)
|
||||||
|
.with_min_inner_size(size)
|
||||||
|
.build(&event_loop)
|
||||||
|
.expect("WindowBuilder error")
|
||||||
|
};
|
||||||
|
|
||||||
|
let window = Rc::new(window);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use winit::platform::web::WindowExtWebSys;
|
||||||
|
|
||||||
|
// Retrieve current width and height dimensions of browser client window
|
||||||
|
let get_window_size = || {
|
||||||
|
let client_window = web_sys::window().unwrap();
|
||||||
|
LogicalSize::new(
|
||||||
|
client_window.inner_width().unwrap().as_f64().unwrap(),
|
||||||
|
client_window.inner_height().unwrap().as_f64().unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let window = Rc::clone(&window);
|
||||||
|
|
||||||
|
// Initialize winit window with current dimensions of browser client
|
||||||
|
window.set_inner_size(get_window_size());
|
||||||
|
|
||||||
|
let client_window = web_sys::window().unwrap();
|
||||||
|
|
||||||
|
// Attach winit canvas to body element
|
||||||
|
web_sys::window()
|
||||||
|
.and_then(|win| win.document())
|
||||||
|
.and_then(|doc| doc.body())
|
||||||
|
.and_then(|body| {
|
||||||
|
body.append_child(&web_sys::Element::from(window.canvas()))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.expect("couldn't append canvas to document body");
|
||||||
|
|
||||||
|
// Listen for resize event on browser client. Adjust winit window dimensions
|
||||||
|
// on event trigger
|
||||||
|
let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| {
|
||||||
|
let size = get_window_size();
|
||||||
|
window.set_inner_size(size)
|
||||||
|
}) as Box<dyn FnMut(_)>);
|
||||||
|
client_window
|
||||||
|
.add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref())
|
||||||
|
.unwrap();
|
||||||
|
closure.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut input = WinitInputHelper::new();
|
||||||
|
let mut pixels = {
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
let surface_texture =
|
||||||
|
SurfaceTexture::new(window_size.width, window_size.height, window.as_ref());
|
||||||
|
Pixels::new_async(WIDTH, HEIGHT, surface_texture)
|
||||||
|
.await
|
||||||
|
.expect("Pixels error")
|
||||||
|
};
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
// Draw the current frame
|
||||||
|
if let Event::RedrawRequested(_) = event {
|
||||||
|
world.draw(pixels.get_frame());
|
||||||
|
if pixels
|
||||||
|
.render()
|
||||||
|
.map_err(|e| error!("pixels.render() failed: {}", e))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle input events
|
||||||
|
if input.update(&event) {
|
||||||
|
// Close events
|
||||||
|
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize the window
|
||||||
|
if let Some(size) = input.window_resized() {
|
||||||
|
pixels.resize_surface(size.width, size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update internal state and request a redraw
|
||||||
|
world.update();
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
impl World {
|
||||||
|
/// Create a new `World` instance that can draw a moving box.
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
box_x: 24,
|
||||||
|
box_y: 16,
|
||||||
|
velocity_x: 1,
|
||||||
|
velocity_y: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the `World` internal state; bounce the box around the screen.
|
||||||
|
fn update(&mut self) {
|
||||||
|
if self.box_x <= 0 || self.box_x + BOX_SIZE > WIDTH as i16 {
|
||||||
|
self.velocity_x *= -1;
|
||||||
|
}
|
||||||
|
if self.box_y <= 0 || self.box_y + BOX_SIZE > HEIGHT as i16 {
|
||||||
|
self.velocity_y *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.box_x += self.velocity_x;
|
||||||
|
self.box_y += self.velocity_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw the `World` state to the frame buffer.
|
||||||
|
///
|
||||||
|
/// Assumes the default texture format: `wgpu::TextureFormat::Rgba8UnormSrgb`
|
||||||
|
fn draw(&self, frame: &mut [u8]) {
|
||||||
|
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
||||||
|
let x = (i % WIDTH as usize) as i16;
|
||||||
|
let y = (i / WIDTH as usize) as i16;
|
||||||
|
|
||||||
|
let inside_the_box = x >= self.box_x
|
||||||
|
&& x < self.box_x + BOX_SIZE
|
||||||
|
&& y >= self.box_y
|
||||||
|
&& y < self.box_y + BOX_SIZE;
|
||||||
|
|
||||||
|
let rgba = if inside_the_box {
|
||||||
|
[0x5e, 0x48, 0xe8, 0xff]
|
||||||
|
} else {
|
||||||
|
[0x48, 0xb2, 0xe8, 0xff]
|
||||||
|
};
|
||||||
|
|
||||||
|
pixel.copy_from_slice(&rgba);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
img/minimal-web.png
Normal file
BIN
img/minimal-web.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
11
justfile
Normal file
11
justfile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
serve package: (build package)
|
||||||
|
miniserve --index index.html ./target/{{package}}/
|
||||||
|
|
||||||
|
build package:
|
||||||
|
mkdir -p ./target/{{package}}/
|
||||||
|
cp ./examples/{{package}}/index.html ./target/{{package}}/
|
||||||
|
cargo build --release --package {{package}} --target wasm32-unknown-unknown --features web
|
||||||
|
wasm-bindgen --target web --no-typescript --out-dir ./target/{{package}}/ ./target/wasm32-unknown-unknown/release/{{package}}.wasm
|
||||||
|
|
||||||
|
clean package:
|
||||||
|
rm -rf ./target/{{package}}/
|
|
@ -23,12 +23,14 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
/// use pixels::wgpu::{PowerPreference, RequestAdapterOptions};
|
||||||
|
///
|
||||||
/// # use pixels::PixelsBuilder;
|
/// # use pixels::PixelsBuilder;
|
||||||
/// # let window = pixels_mocks::Rwh;
|
/// # let window = pixels_mocks::Rwh;
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &window);
|
/// # let surface_texture = pixels::SurfaceTexture::new(256, 240, &window);
|
||||||
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
||||||
/// .request_adapter_options(wgpu::RequestAdapterOptions {
|
/// .request_adapter_options(RequestAdapterOptions {
|
||||||
/// power_preference: wgpu::PowerPreference::HighPerformance,
|
/// power_preference: PowerPreference::HighPerformance,
|
||||||
/// force_fallback_adapter: false,
|
/// force_fallback_adapter: false,
|
||||||
/// compatible_surface: None,
|
/// compatible_surface: None,
|
||||||
/// })
|
/// })
|
||||||
|
@ -47,7 +49,17 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
|
||||||
Self {
|
Self {
|
||||||
request_adapter_options: None,
|
request_adapter_options: None,
|
||||||
device_descriptor: None,
|
device_descriptor: None,
|
||||||
backend: wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::PRIMARY),
|
backend: wgpu::util::backend_bits_from_env().unwrap_or({
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
wgpu::Backends::PRIMARY
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
wgpu::Backends::all()
|
||||||
|
}
|
||||||
|
}),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
_pixel_aspect_ratio: 1.0,
|
_pixel_aspect_ratio: 1.0,
|
||||||
|
@ -166,20 +178,24 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
|
||||||
|
|
||||||
/// Create a pixel buffer from the options builder.
|
/// Create a pixel buffer from the options builder.
|
||||||
///
|
///
|
||||||
|
/// This is the private implementation shared by [`PixelsBuilder::build`] and
|
||||||
|
/// [`PixelsBuilder::build_async`].
|
||||||
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error when a [`wgpu::Adapter`] cannot be found.
|
/// Returns an error when a [`wgpu::Adapter`] cannot be found.
|
||||||
pub fn build(self) -> Result<Pixels, Error> {
|
async fn build_impl(self) -> Result<Pixels, Error> {
|
||||||
let instance = wgpu::Instance::new(self.backend);
|
let instance = wgpu::Instance::new(self.backend);
|
||||||
|
|
||||||
// TODO: Use `options.pixel_aspect_ratio` to stretch the scaled texture
|
// TODO: Use `options.pixel_aspect_ratio` to stretch the scaled texture
|
||||||
let surface = unsafe { instance.create_surface(self.surface_texture.window) };
|
let surface = unsafe { instance.create_surface(self.surface_texture.window) };
|
||||||
let compatible_surface = Some(&surface);
|
let compatible_surface = Some(&surface);
|
||||||
let request_adapter_options = &self.request_adapter_options;
|
let request_adapter_options = &self.request_adapter_options;
|
||||||
let adapter =
|
let adapter = match wgpu::util::initialize_adapter_from_env(&instance, self.backend) {
|
||||||
wgpu::util::initialize_adapter_from_env(&instance, self.backend).or_else(|| {
|
Some(adapter) => Some(adapter),
|
||||||
let future =
|
None => {
|
||||||
instance.request_adapter(&request_adapter_options.as_ref().map_or_else(
|
instance
|
||||||
|
.request_adapter(&request_adapter_options.as_ref().map_or_else(
|
||||||
|| wgpu::RequestAdapterOptions {
|
|| wgpu::RequestAdapterOptions {
|
||||||
compatible_surface,
|
compatible_surface,
|
||||||
force_fallback_adapter: false,
|
force_fallback_adapter: false,
|
||||||
|
@ -188,13 +204,14 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
|
||||||
},
|
},
|
||||||
|rao| wgpu::RequestAdapterOptions {
|
|rao| wgpu::RequestAdapterOptions {
|
||||||
compatible_surface: rao.compatible_surface.or(compatible_surface),
|
compatible_surface: rao.compatible_surface.or(compatible_surface),
|
||||||
force_fallback_adapter: false,
|
force_fallback_adapter: rao.force_fallback_adapter,
|
||||||
power_preference: rao.power_preference,
|
power_preference: rao.power_preference,
|
||||||
},
|
},
|
||||||
));
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pollster::block_on(future)
|
|
||||||
});
|
|
||||||
let adapter = adapter.ok_or(Error::AdapterNotFound)?;
|
let adapter = adapter.ok_or(Error::AdapterNotFound)?;
|
||||||
|
|
||||||
let device_descriptor = self
|
let device_descriptor = self
|
||||||
|
@ -204,7 +221,9 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
|
||||||
..wgpu::DeviceDescriptor::default()
|
..wgpu::DeviceDescriptor::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let (device, queue) = pollster::block_on(adapter.request_device(&device_descriptor, None))
|
let (device, queue) = adapter
|
||||||
|
.request_device(&device_descriptor, None)
|
||||||
|
.await
|
||||||
.map_err(Error::DeviceNotFound)?;
|
.map_err(Error::DeviceNotFound)?;
|
||||||
|
|
||||||
let present_mode = self.present_mode;
|
let present_mode = self.present_mode;
|
||||||
|
@ -256,6 +275,45 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
|
||||||
|
|
||||||
Ok(pixels)
|
Ok(pixels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a pixel buffer from the options builder.
|
||||||
|
///
|
||||||
|
/// This method blocks the current thread, making it unusable on Web targets. Use
|
||||||
|
/// [`PixelsBuilder::build_async`] for a non-blocking alternative.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error when a [`wgpu::Adapter`] or [`wgpu::Device`] cannot be found.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub fn build(self) -> Result<Pixels, Error> {
|
||||||
|
pollster::block_on(self.build_impl())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a pixel buffer from the options builder without blocking the current thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use pixels::wgpu::{Backends, DeviceDescriptor, Limits};
|
||||||
|
///
|
||||||
|
/// # async fn test() -> Result<(), pixels::Error> {
|
||||||
|
/// # use pixels::PixelsBuilder;
|
||||||
|
/// # let window = pixels_mocks::Rwh;
|
||||||
|
/// # let surface_texture = pixels::SurfaceTexture::new(256, 240, &window);
|
||||||
|
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
||||||
|
/// .enable_vsync(false)
|
||||||
|
/// .build_async()
|
||||||
|
/// .await?;
|
||||||
|
/// # Ok::<(), pixels::Error>(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error when a [`wgpu::Adapter`] or [`wgpu::Device`] cannot be found.
|
||||||
|
pub async fn build_async(self) -> Result<Pixels, Error> {
|
||||||
|
self.build_impl().await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn create_backing_texture(
|
pub(crate) fn create_backing_texture(
|
||||||
|
|
66
src/lib.rs
66
src/lib.rs
|
@ -142,10 +142,7 @@ impl<'win, W: HasRawWindowHandle> SurfaceTexture<'win, W> {
|
||||||
/// let window = Window::new(&event_loop).unwrap();
|
/// let window = Window::new(&event_loop).unwrap();
|
||||||
/// let size = window.inner_size();
|
/// let size = window.inner_size();
|
||||||
///
|
///
|
||||||
/// let width = size.width;
|
/// let surface_texture = SurfaceTexture::new(size.width, size.height, &window);
|
||||||
/// let height = size.height;
|
|
||||||
///
|
|
||||||
/// let surface_texture = SurfaceTexture::new(width, height, &window);
|
|
||||||
/// # Ok::<(), pixels::Error>(())
|
/// # Ok::<(), pixels::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -173,12 +170,15 @@ impl Pixels {
|
||||||
/// `320x240`, `640x480`, `960x720`, etc. without adding a border because these are exactly
|
/// `320x240`, `640x480`, `960x720`, etc. without adding a border because these are exactly
|
||||||
/// 1x, 2x, and 3x scales, respectively.
|
/// 1x, 2x, and 3x scales, respectively.
|
||||||
///
|
///
|
||||||
|
/// This method blocks the current thread, making it unusable on Web targets. Use
|
||||||
|
/// [`Pixels::new_async`] for a non-blocking alternative.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use pixels::Pixels;
|
/// # use pixels::Pixels;
|
||||||
/// # let window = pixels_mocks::Rwh;
|
/// # let window = pixels_mocks::Rwh;
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &window);
|
/// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window);
|
||||||
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
/// # Ok::<(), pixels::Error>(())
|
/// # Ok::<(), pixels::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -190,6 +190,7 @@ impl Pixels {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics when `width` or `height` are 0.
|
/// Panics when `width` or `height` are 0.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub fn new<W: HasRawWindowHandle>(
|
pub fn new<W: HasRawWindowHandle>(
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
@ -198,6 +199,39 @@ impl Pixels {
|
||||||
PixelsBuilder::new(width, height, surface_texture).build()
|
PixelsBuilder::new(width, height, surface_texture).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asynchronously create a pixel buffer instance with default options.
|
||||||
|
///
|
||||||
|
/// See [`Pixels::new`] for more information.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # async fn test() -> Result<(), pixels::Error> {
|
||||||
|
/// # use pixels::Pixels;
|
||||||
|
/// # let window = pixels_mocks::Rwh;
|
||||||
|
/// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window);
|
||||||
|
/// let mut pixels = Pixels::new_async(320, 240, surface_texture).await?;
|
||||||
|
/// # Ok::<(), pixels::Error>(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error when a [`wgpu::Adapter`] cannot be found.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics when `width` or `height` are 0.
|
||||||
|
pub async fn new_async<W: HasRawWindowHandle>(
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
surface_texture: SurfaceTexture<'_, W>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
PixelsBuilder::new(width, height, surface_texture)
|
||||||
|
.build_async()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Resize the pixel buffer and zero its contents.
|
/// Resize the pixel buffer and zero its contents.
|
||||||
///
|
///
|
||||||
/// This does not resize the surface upon which the pixel buffer texture is rendered. Use
|
/// This does not resize the surface upon which the pixel buffer texture is rendered. Use
|
||||||
|
@ -291,7 +325,7 @@ impl Pixels {
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use pixels::Pixels;
|
/// # use pixels::Pixels;
|
||||||
/// # let window = pixels_mocks::Rwh;
|
/// # let window = pixels_mocks::Rwh;
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &window);
|
/// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window);
|
||||||
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
///
|
///
|
||||||
/// // Clear the pixel buffer
|
/// // Clear the pixel buffer
|
||||||
|
@ -336,7 +370,7 @@ impl Pixels {
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use pixels::Pixels;
|
/// # use pixels::Pixels;
|
||||||
/// # let window = pixels_mocks::Rwh;
|
/// # let window = pixels_mocks::Rwh;
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &window);
|
/// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window);
|
||||||
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
///
|
///
|
||||||
/// // Clear the pixel buffer
|
/// // Clear the pixel buffer
|
||||||
|
@ -449,16 +483,15 @@ impl Pixels {
|
||||||
/// the screen, using isize instead of usize.
|
/// the screen, using isize instead of usize.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
/// use winit::dpi::PhysicalPosition;
|
||||||
|
///
|
||||||
/// # use pixels::Pixels;
|
/// # use pixels::Pixels;
|
||||||
/// # let window = pixels_mocks::Rwh;
|
/// # let window = pixels_mocks::Rwh;
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &window);
|
/// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window);
|
||||||
/// const WIDTH: u32 = 320;
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
/// const HEIGHT: u32 = 240;
|
|
||||||
///
|
|
||||||
/// let mut pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?;
|
|
||||||
///
|
///
|
||||||
/// // A cursor position in physical units
|
/// // A cursor position in physical units
|
||||||
/// let cursor_position: (f32, f32) = winit::dpi::PhysicalPosition::new(0.0, 0.0).into();
|
/// let cursor_position: (f32, f32) = PhysicalPosition::new(0.0, 0.0).into();
|
||||||
///
|
///
|
||||||
/// // Convert it to a pixel location
|
/// // Convert it to a pixel location
|
||||||
/// let pixel_position: (usize, usize) = pixels.window_pos_to_pixel(cursor_position)
|
/// let pixel_position: (usize, usize) = pixels.window_pos_to_pixel(cursor_position)
|
||||||
|
@ -509,11 +542,8 @@ impl Pixels {
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use pixels::Pixels;
|
/// # use pixels::Pixels;
|
||||||
/// # let window = pixels_mocks::Rwh;
|
/// # let window = pixels_mocks::Rwh;
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &window);
|
/// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window);
|
||||||
/// const WIDTH: u32 = 320;
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
/// const HEIGHT: u32 = 240;
|
|
||||||
///
|
|
||||||
/// let mut pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?;
|
|
||||||
///
|
///
|
||||||
/// let pixel_pos = pixels.clamp_pixel_pos((-19, 20));
|
/// let pixel_pos = pixels.clamp_pixel_pos((-19, 20));
|
||||||
/// assert_eq!(pixel_pos, (0, 20));
|
/// assert_eq!(pixel_pos, (0, 20));
|
||||||
|
|
Loading…
Reference in a new issue