Vendor wgpu-hal 29.0.4 and i-slint-renderer-skia 1.17.1, unmodified
The Android develop view reads its frame back through memory (TD-1) because wgpu's Vulkan swapchain never pre-rotates, and a portrait window on this tablet's landscape panel then tears. The fix is a small patch to each of these two crates, and this commit is only the ground it lands on: both are byte-for-byte the crates.io sources the lockfile already resolved, so the commits that follow are the patch and nothing else. third_party/ is excluded from the workspace, or every path dependency under the root would become a member and `--workspace` would test and lint upstream code as ours. The README says how to carry the patches across a Slint or wgpu bump, which matters because a stale version here does not fail the build — cargo just warns and uses the unpatched crate.
This commit is contained in:
+396
@@ -0,0 +1,396 @@
|
||||
//! 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(
|
||||
target_arch = "wasm32",
|
||||
target_os = "ios",
|
||||
target_os = "visionos",
|
||||
target_env = "ohos"
|
||||
)))]
|
||||
fn main() {
|
||||
use std::ffi::CString;
|
||||
|
||||
use glutin::{
|
||||
config::GlConfig as _,
|
||||
context::{NotCurrentGlContext as _, PossiblyCurrentGlContext as _, Version},
|
||||
display::{GetGlDisplay as _, GlDisplay as _},
|
||||
surface::GlSurface as _,
|
||||
};
|
||||
use glutin_winit::GlWindow as _;
|
||||
use raw_window_handle::HasWindowHandle as _;
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::{KeyEvent, WindowEvent},
|
||||
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
|
||||
keyboard::{Key, NamedKey},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
env_logger::init();
|
||||
println!("Initializing external GL context");
|
||||
|
||||
/// Find the config with the maximum number of samples, so our triangle will be
|
||||
/// smooth.
|
||||
fn gl_config_picker(
|
||||
configs: Box<dyn Iterator<Item = glutin::config::Config> + '_>,
|
||||
) -> glutin::config::Config {
|
||||
configs
|
||||
.reduce(|accum, config| {
|
||||
if config.num_samples() > accum.num_samples() {
|
||||
config
|
||||
} else {
|
||||
accum
|
||||
}
|
||||
})
|
||||
.expect("Failed to find a matching config")
|
||||
}
|
||||
|
||||
struct App {
|
||||
gl_config: Option<glutin::config::Config>,
|
||||
not_current_gl_context: Option<glutin::context::NotCurrentContext>,
|
||||
state: Option<(
|
||||
glutin::context::PossiblyCurrentContext,
|
||||
glutin::surface::Surface<glutin::surface::WindowSurface>,
|
||||
Window,
|
||||
)>,
|
||||
exposed: Option<hal::ExposedAdapter<hal::api::Gles>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
gl_config: None,
|
||||
not_current_gl_context: None,
|
||||
state: None,
|
||||
exposed: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
// First call: create display, pick config, create GL context.
|
||||
if self.gl_config.is_none() {
|
||||
// Only Windows requires the window to be present before creating the display.
|
||||
// Other platforms don't really need one.
|
||||
let window_attributes = cfg!(windows).then(|| {
|
||||
Window::default_attributes()
|
||||
.with_title("wgpu raw GLES example (press Escape to exit)")
|
||||
});
|
||||
|
||||
let template = glutin::config::ConfigTemplateBuilder::new();
|
||||
|
||||
let display_builder =
|
||||
glutin_winit::DisplayBuilder::new().with_window_attributes(window_attributes);
|
||||
|
||||
let (window, gl_config) = display_builder
|
||||
.build(event_loop, template, gl_config_picker)
|
||||
.expect("Failed to build window and config from display");
|
||||
|
||||
println!("Picked a config with {} samples", gl_config.num_samples());
|
||||
|
||||
let raw_window_handle = window
|
||||
.as_ref()
|
||||
.and_then(|window| window.window_handle().ok())
|
||||
.map(|handle| handle.as_raw());
|
||||
|
||||
let gl_display = gl_config.display();
|
||||
|
||||
// Glutin tries to create an OpenGL context by default.
|
||||
// Force it to use any version of GLES.
|
||||
let context_attributes = glutin::context::ContextAttributesBuilder::new()
|
||||
// wgpu expects GLES 3.0+.
|
||||
.with_context_api(glutin::context::ContextApi::Gles(Some(Version::new(3, 0))))
|
||||
.build(raw_window_handle);
|
||||
|
||||
let gl_context = unsafe {
|
||||
gl_display
|
||||
.create_context(&gl_config, &context_attributes)
|
||||
.expect("failed to create context")
|
||||
};
|
||||
|
||||
self.not_current_gl_context = Some(gl_context);
|
||||
self.gl_config = Some(gl_config);
|
||||
|
||||
// On Windows, the window was already created by DisplayBuilder.
|
||||
if let Some(window) = window {
|
||||
self.create_surface(event_loop, window);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create window + surface (non-Windows first call, or Android re-resume).
|
||||
if self.state.is_none() {
|
||||
let gl_config = self.gl_config.as_ref().unwrap();
|
||||
let window = glutin_winit::finalize_window(
|
||||
event_loop,
|
||||
Window::default_attributes()
|
||||
.with_title("wgpu raw GLES example (press Escape to exit)"),
|
||||
gl_config,
|
||||
)
|
||||
.unwrap();
|
||||
self.create_surface(event_loop, window);
|
||||
}
|
||||
}
|
||||
|
||||
fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
// This event is only raised on Android, where the backing NativeWindow for a GL
|
||||
// Surface can appear and disappear at any moment.
|
||||
println!("Android window removed");
|
||||
|
||||
// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
|
||||
// the window back to the system.
|
||||
if let Some((gl_context, ..)) = self.state.take() {
|
||||
assert!(self
|
||||
.not_current_gl_context
|
||||
.replace(gl_context.make_not_current().unwrap())
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
event_loop.set_control_flow(ControlFlow::Wait);
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => event_loop.exit(),
|
||||
WindowEvent::Resized(size) => {
|
||||
if size.width != 0 && size.height != 0 {
|
||||
// Some platforms like EGL require resizing GL surface to update the size.
|
||||
// Notable platforms here are Wayland and macOS, other don't require it
|
||||
// and the function is no-op, but it's wise to resize it for portability
|
||||
// reasons.
|
||||
if let Some((gl_context, gl_surface, window)) = &self.state {
|
||||
window.resize_surface(gl_surface, gl_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let (Some(exposed), Some((gl_context, gl_surface, window))) =
|
||||
(&self.exposed, &self.state)
|
||||
{
|
||||
let inner_size = window.inner_size();
|
||||
|
||||
fill_screen(exposed, inner_size.width, inner_size.height);
|
||||
|
||||
println!("Showing the window");
|
||||
gl_surface
|
||||
.swap_buffers(gl_context)
|
||||
.expect("Failed to swap buffers");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn create_surface(&mut self, _event_loop: &ActiveEventLoop, window: Window) {
|
||||
let gl_config = self.gl_config.as_ref().unwrap();
|
||||
|
||||
let attrs = window
|
||||
.build_surface_attributes(Default::default())
|
||||
.expect("Failed to build surface attributes");
|
||||
let gl_surface = unsafe {
|
||||
gl_config
|
||||
.display()
|
||||
.create_window_surface(gl_config, &attrs)
|
||||
.expect("Cannot create GL WindowSurface")
|
||||
};
|
||||
|
||||
// Make it current.
|
||||
let gl_context = self
|
||||
.not_current_gl_context
|
||||
.take()
|
||||
.unwrap()
|
||||
.make_current(&gl_surface)
|
||||
.expect("GL context cannot be made current with WindowSurface");
|
||||
|
||||
// The context needs to be current for the Renderer to set up shaders and
|
||||
// buffers. It also performs function loading, which needs a current context on
|
||||
// WGL.
|
||||
println!("Hooking up to wgpu-hal");
|
||||
self.exposed.get_or_insert_with(|| {
|
||||
unsafe {
|
||||
<hal::api::Gles as hal::Api>::Adapter::new_external(
|
||||
|name| {
|
||||
// XXX: On WGL this should only be called after the context was
|
||||
// made current
|
||||
gl_config
|
||||
.display()
|
||||
.get_proc_address(&CString::new(name).expect(name))
|
||||
},
|
||||
wgpu_types::GlBackendOptions::default(),
|
||||
)
|
||||
}
|
||||
.expect("GL adapter can't be initialized")
|
||||
});
|
||||
|
||||
window.request_redraw();
|
||||
self.state = Some((gl_context, gl_surface, window));
|
||||
}
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let mut app = App::new();
|
||||
event_loop
|
||||
.run_app(&mut app)
|
||||
.expect("Couldn't run event loop");
|
||||
}
|
||||
|
||||
#[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(
|
||||
all(target_arch = "wasm32", not(target_os = "emscripten")),
|
||||
target_os = "ios",
|
||||
target_os = "visionos",
|
||||
target_env = "ohos"
|
||||
))]
|
||||
fn main() {
|
||||
eprintln!("This example is not supported on this platform")
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
all(target_arch = "wasm32", not(target_os = "emscripten")),
|
||||
target_os = "ios",
|
||||
target_os = "visionos"
|
||||
)))]
|
||||
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(
|
||||
wgpu_types::Features::empty(),
|
||||
&wgpu_types::Limits::downlevel_defaults(),
|
||||
&wgpu_types::MemoryHints::default(),
|
||||
)
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
let format = wgpu_types::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: wgpu_types::TextureViewDimension::D2,
|
||||
usage: wgpu_types::TextureUses::COLOR_TARGET,
|
||||
range: wgpu_types::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: wgpu_types::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
sample_count: 1,
|
||||
color_attachments: &[Some(hal::ColorAttachment {
|
||||
target: hal::Attachment {
|
||||
view: &view,
|
||||
usage: wgpu_types::TextureUses::COLOR_TARGET,
|
||||
},
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,
|
||||
clear_value: wgpu_types::Color::BLUE,
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
multiview_mask: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
};
|
||||
unsafe {
|
||||
encoder.begin_encoding(None).unwrap();
|
||||
encoder.begin_render_pass(&rp_desc).unwrap();
|
||||
encoder.end_render_pass();
|
||||
let cmd_buf = encoder.end_encoding().unwrap();
|
||||
od.queue.submit(&[&cmd_buf], &[], (&mut fence, 0)).unwrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user