wgpu/wgpu-hal/examples/raw-gles.rs
Connor Fitzgerald c7458638d1
[hal/vk] Rework Submission and Surface Synchronization (#5681)
Fix two major synchronization issues in `wgpu_val::vulkan`:

- Properly order queue command buffer submissions. Due to Mesa bugs, two semaphores are required even though the Vulkan spec says that only one should be necessary.

- Properly manage surface texture acquisition and presentation:

    - Acquiring a surface texture can return while the presentation engine is still displaying the texture. Applications must wait for a semaphore to be signaled before using the acquired texture.

    - Presenting a surface texture requires a semaphore to ensure that drawing is complete before presentation occurs.

Co-authored-by: Jim Blandy <jimb@red-bean.com>
2024-05-30 13:53:34 -07:00

190 lines
6.2 KiB
Rust

//! This example shows interop with raw GLES contexts -
//! the ability to hook up wgpu-hal to an existing context and draw into it.
//!
//! Emscripten build:
//! 1. install emsdk
//! 2. build this example with cargo:
//! EMCC_CFLAGS="-g -s ERROR_ON_UNDEFINED_SYMBOLS=0 --no-entry -s FULL_ES3=1" cargo build --example raw-gles --target wasm32-unknown-emscripten
//! 3. copy raw-gles.em.html into target directory and open it in browser:
//! cp wgpu-hal/examples/raw-gles.em.html target/wasm32-unknown-emscripten/debug/examples
extern crate wgpu_hal as hal;
#[cfg(not(any(windows, target_arch = "wasm32")))]
fn main() {
env_logger::init();
println!("Initializing external GL context");
let event_loop = glutin::event_loop::EventLoop::new();
let window_builder = glutin::window::WindowBuilder::new();
let gl_context = unsafe {
glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)))
.build_windowed(window_builder, &event_loop)
.unwrap()
.make_current()
.unwrap()
};
let inner_size = gl_context.window().inner_size();
println!("Hooking up to wgpu-hal");
let exposed = unsafe {
<hal::api::Gles as hal::Api>::Adapter::new_external(|name| {
gl_context.get_proc_address(name)
})
}
.expect("GL adapter can't be initialized");
fill_screen(&exposed, inner_size.width, inner_size.height);
println!("Showing the window");
gl_context.swap_buffers().unwrap();
event_loop.run(move |event, _, control_flow| {
use glutin::{
event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
};
*control_flow = ControlFlow::Wait;
match event {
Event::LoopDestroyed => (),
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
_ => (),
},
_ => (),
}
});
}
#[cfg(target_os = "emscripten")]
fn main() {
env_logger::init();
println!("Initializing external GL context");
let egl = khronos_egl::Instance::new(khronos_egl::Static);
let display = unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) }.unwrap();
egl.initialize(display)
.expect("unable to initialize display");
let attributes = [
khronos_egl::RED_SIZE,
8,
khronos_egl::GREEN_SIZE,
8,
khronos_egl::BLUE_SIZE,
8,
khronos_egl::NONE,
];
let config = egl
.choose_first_config(display, &attributes)
.unwrap()
.expect("unable to choose config");
let surface = unsafe {
let window = std::ptr::null_mut::<std::ffi::c_void>();
egl.create_window_surface(display, config, window, None)
}
.expect("unable to create surface");
let context_attributes = [khronos_egl::CONTEXT_CLIENT_VERSION, 3, khronos_egl::NONE];
let gl_context = egl
.create_context(display, config, None, &context_attributes)
.expect("unable to create context");
egl.make_current(display, Some(surface), Some(surface), Some(gl_context))
.expect("can't make context current");
println!("Hooking up to wgpu-hal");
let exposed = unsafe {
<hal::api::Gles as hal::Api>::Adapter::new_external(|name| {
egl.get_proc_address(name)
.map_or(std::ptr::null(), |p| p as *const _)
})
}
.expect("GL adapter can't be initialized");
fill_screen(&exposed, 640, 400);
}
#[cfg(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten"))))]
fn main() {}
#[cfg(any(not(any(windows, target_arch = "wasm32")), target_os = "emscripten"))]
fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height: u32) {
use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
let od = unsafe {
exposed
.adapter
.open(wgt::Features::empty(), &wgt::Limits::downlevel_defaults())
}
.unwrap();
let format = wgt::TextureFormat::Rgba8UnormSrgb;
let texture = <hal::api::Gles as hal::Api>::Texture::default_framebuffer(format);
let view = unsafe {
od.device
.create_texture_view(
&texture,
&hal::TextureViewDescriptor {
label: None,
format,
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
},
)
.unwrap()
};
println!("Filling the screen");
let mut encoder = unsafe {
od.device
.create_command_encoder(&hal::CommandEncoderDescriptor {
label: None,
queue: &od.queue,
})
.unwrap()
};
let mut fence = unsafe { od.device.create_fence().unwrap() };
let rp_desc = hal::RenderPassDescriptor {
label: None,
extent: wgt::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
sample_count: 1,
color_attachments: &[Some(hal::ColorAttachment {
target: hal::Attachment {
view: &view,
usage: hal::TextureUses::COLOR_TARGET,
},
resolve_target: None,
ops: hal::AttachmentOps::STORE,
clear_value: wgt::Color::BLUE,
})],
depth_stencil_attachment: None,
multiview: None,
timestamp_writes: None,
occlusion_query_set: None,
};
unsafe {
encoder.begin_encoding(None).unwrap();
encoder.begin_render_pass(&rp_desc);
encoder.end_render_pass();
let cmd_buf = encoder.end_encoding().unwrap();
od.queue.submit(&[&cmd_buf], &[], (&mut fence, 0)).unwrap();
}
}