Experimental wgpu WebGL backend support
- Added missing `draw_cache_align_4x4` call for `brush_glyph` on wasm32 target - Added WebGL support to `integratio_wgpu` example - Fixed test.yml CI workflow - Removed spir-v shader in `integration_wgpu`; Fixed formatting - Removed redundant `BoxStream` typedef
This commit is contained in:
parent
c75ed37148
commit
bdca20fc4a
21 changed files with 414 additions and 86 deletions
2
examples/integration_wgpu/.gitignore
vendored
Normal file
2
examples/integration_wgpu/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*.wasm
|
||||
*.js
|
||||
|
|
@ -7,5 +7,16 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
iced_winit = { path = "../../winit" }
|
||||
iced_wgpu = { path = "../../wgpu", features = ["spirv"] }
|
||||
iced_wgpu = { path = "../../wgpu", features = ["webgl"] }
|
||||
env_logger = "0.8"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "0.2.0"
|
||||
log = "0.4"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["Element", "HtmlCanvasElement", "Window", "Document"] }
|
||||
# This dependency a little bit quirky, it is deep in the tree and without `js` feature it
|
||||
# refuses to work with `wasm32-unknown-unknown target`. Unfortunately, we need this patch
|
||||
# to make it work
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
|
|
|
|||
|
|
@ -15,5 +15,22 @@ You can run it with `cargo run`:
|
|||
cargo run --package integration
|
||||
```
|
||||
|
||||
### How to run this example with WebGL backend
|
||||
NOTE: Currently, WebGL backend is is still experimental, so expect bugs.
|
||||
|
||||
```sh
|
||||
# 0. Install prerequisites
|
||||
cargo install wasm-bindgen-cli https
|
||||
# 1. cd to the current folder
|
||||
# 2. Compile wasm module
|
||||
cargo build -p integration_wgpu --target wasm32-unknown-unknown
|
||||
# 3. Invoke wasm-bindgen
|
||||
wasm-bindgen ../../target/wasm32-unknown-unknown/debug/integration_wgpu.wasm --out-dir . --target web --no-typescript
|
||||
# 4. run http server
|
||||
http
|
||||
# 5. Open 127.0.0.1:8000 in browser
|
||||
```
|
||||
|
||||
|
||||
[`main`]: src/main.rs
|
||||
[`wgpu`]: https://github.com/gfx-rs/wgpu
|
||||
|
|
|
|||
21
examples/integration_wgpu/index.html
Normal file
21
examples/integration_wgpu/index.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
||||
<title>Iced - wgpu + WebGL integration</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>integration_wgpu</h1>
|
||||
<canvas id="iced_canvas"></canvas>
|
||||
<script type="module">
|
||||
import init from "./integration_wgpu.js";
|
||||
init('./integration_wgpu_bg.wasm');
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,23 +1,29 @@
|
|||
use iced_wgpu::Renderer;
|
||||
use iced_winit::widget::slider::{self, Slider};
|
||||
use iced_winit::widget::text_input::{self, TextInput};
|
||||
use iced_winit::widget::{Column, Row, Text};
|
||||
use iced_winit::{Alignment, Color, Command, Element, Length, Program};
|
||||
|
||||
pub struct Controls {
|
||||
background_color: Color,
|
||||
text: String,
|
||||
sliders: [slider::State; 3],
|
||||
text_input: text_input::State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
BackgroundColorChanged(Color),
|
||||
TextChanged(String),
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
pub fn new() -> Controls {
|
||||
Controls {
|
||||
background_color: Color::BLACK,
|
||||
text: Default::default(),
|
||||
sliders: Default::default(),
|
||||
text_input: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -35,6 +41,9 @@ impl Program for Controls {
|
|||
Message::BackgroundColorChanged(color) => {
|
||||
self.background_color = color;
|
||||
}
|
||||
Message::TextChanged(text) => {
|
||||
self.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
Command::none()
|
||||
|
|
@ -42,7 +51,9 @@ impl Program for Controls {
|
|||
|
||||
fn view(&mut self) -> Element<Message, Renderer> {
|
||||
let [r, g, b] = &mut self.sliders;
|
||||
let t = &mut self.text_input;
|
||||
let background_color = self.background_color;
|
||||
let text = &self.text;
|
||||
|
||||
let sliders = Row::new()
|
||||
.width(Length::Units(500))
|
||||
|
|
@ -96,7 +107,13 @@ impl Program for Controls {
|
|||
Text::new(format!("{:?}", background_color))
|
||||
.size(14)
|
||||
.color(Color::WHITE),
|
||||
),
|
||||
)
|
||||
.push(TextInput::new(
|
||||
t,
|
||||
"Placeholder",
|
||||
text,
|
||||
move |text| Message::TextChanged(text),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
|
|
|
|||
|
|
@ -14,11 +14,39 @@ use winit::{
|
|||
event_loop::{ControlFlow, EventLoop},
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::JsCast;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::HtmlCanvasElement;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
|
||||
pub fn main() {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let canvas_element = {
|
||||
console_log::init_with_level(log::Level::Debug)
|
||||
.expect("could not initialize logger");
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
||||
web_sys::window()
|
||||
.and_then(|win| win.document())
|
||||
.and_then(|doc| doc.get_element_by_id("iced_canvas"))
|
||||
.and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok())
|
||||
.expect("Canvas with id `iced_canvas` is missing")
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
env_logger::init();
|
||||
|
||||
// Initialize winit
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_canvas(Some(canvas_element))
|
||||
.build(&event_loop)
|
||||
.expect("Failed to build winit window");
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||
|
||||
let physical_size = window.inner_size();
|
||||
|
|
@ -31,18 +59,35 @@ pub fn main() {
|
|||
let mut clipboard = Clipboard::connect(&window);
|
||||
|
||||
// Initialize wgpu
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let default_backend = wgpu::Backends::GL;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let default_backend = wgpu::Backends::PRIMARY;
|
||||
|
||||
let backend =
|
||||
wgpu::util::backend_bits_from_env().unwrap_or(default_backend);
|
||||
|
||||
let instance = wgpu::Instance::new(backend);
|
||||
let surface = unsafe { instance.create_surface(&window) };
|
||||
|
||||
let (format, (mut device, queue)) = futures::executor::block_on(async {
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
})
|
||||
.await
|
||||
.expect("Request adapter");
|
||||
let adapter = wgpu::util::initialize_adapter_from_env_or_default(
|
||||
&instance,
|
||||
backend,
|
||||
Some(&surface),
|
||||
)
|
||||
.await
|
||||
.expect("No suitable GPU adapters found on the system!");
|
||||
|
||||
let adapter_features = adapter.features();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let needed_limits = wgpu::Limits::downlevel_webgl2_defaults()
|
||||
.using_resolution(adapter.limits());
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let needed_limits = wgpu::Limits::default();
|
||||
|
||||
(
|
||||
surface
|
||||
|
|
@ -52,8 +97,8 @@ pub fn main() {
|
|||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features: wgpu::Features::empty(),
|
||||
limits: wgpu::Limits::default(),
|
||||
features: adapter_features & wgpu::Features::default(),
|
||||
limits: needed_limits,
|
||||
},
|
||||
None,
|
||||
)
|
||||
|
|
@ -62,20 +107,17 @@ pub fn main() {
|
|||
)
|
||||
});
|
||||
|
||||
{
|
||||
let size = window.inner_size();
|
||||
surface.configure(
|
||||
&device,
|
||||
&wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format,
|
||||
width: physical_size.width,
|
||||
height: physical_size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
},
|
||||
);
|
||||
|
||||
surface.configure(
|
||||
&device,
|
||||
&wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
},
|
||||
)
|
||||
};
|
||||
let mut resized = false;
|
||||
|
||||
// Initialize staging belt and local pool
|
||||
|
|
@ -83,7 +125,7 @@ pub fn main() {
|
|||
let mut local_pool = futures::executor::LocalPool::new();
|
||||
|
||||
// Initialize scene and GUI controls
|
||||
let scene = Scene::new(&mut device);
|
||||
let scene = Scene::new(&mut device, format);
|
||||
let controls = Controls::new();
|
||||
|
||||
// Initialize iced
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ pub struct Scene {
|
|||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(device: &wgpu::Device) -> Scene {
|
||||
let pipeline = build_pipeline(device);
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
texture_format: wgpu::TextureFormat,
|
||||
) -> Scene {
|
||||
let pipeline = build_pipeline(device, texture_format);
|
||||
|
||||
Scene { pipeline }
|
||||
}
|
||||
|
|
@ -47,12 +50,14 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
|
||||
let vs_module =
|
||||
device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv"));
|
||||
|
||||
let fs_module =
|
||||
device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv"));
|
||||
fn build_pipeline(
|
||||
device: &wgpu::Device,
|
||||
texture_format: wgpu::TextureFormat,
|
||||
) -> wgpu::RenderPipeline {
|
||||
let (vs_module, fs_module) = (
|
||||
device.create_shader_module(&wgpu::include_wgsl!("shader/vert.wgsl")),
|
||||
device.create_shader_module(&wgpu::include_wgsl!("shader/frag.wgsl")),
|
||||
);
|
||||
|
||||
let pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
|
|
@ -74,7 +79,7 @@ fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
|
|||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
format: texture_format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent::REPLACE,
|
||||
alpha: wgpu::BlendComponent::REPLACE,
|
||||
|
|
|
|||
Binary file not shown.
4
examples/integration_wgpu/src/shader/frag.wgsl
Normal file
4
examples/integration_wgpu/src/shader/frag.wgsl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[[stage(fragment)]]
|
||||
fn main() -> [[location(0)]] vec4<f32> {
|
||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
Binary file not shown.
6
examples/integration_wgpu/src/shader/vert.wgsl
Normal file
6
examples/integration_wgpu/src/shader/vert.wgsl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[[stage(vertex)]]
|
||||
fn main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {
|
||||
let x = f32(1 - i32(in_vertex_index)) * 0.5;
|
||||
let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5;
|
||||
return vec4<f32>(x, y, 0.0, 1.0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue