-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[app-surface] Support creating AppSurface directly with Canvas and Of…
…fscreenCanvas.
- Loading branch information
Showing
7 changed files
with
350 additions
and
6 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use super::SendSyncWrapper; | ||
use raw_window_handle::{ | ||
HasDisplayHandle, HasWindowHandle, RawWindowHandle, WebCanvasWindowHandle, WindowHandle, | ||
}; | ||
use std::{ops::Deref, ptr::NonNull}; | ||
use wasm_bindgen::{JsCast, JsValue}; | ||
|
||
#[derive(Debug)] | ||
pub struct CanvasWrapper(SendSyncWrapper<Canvas>); | ||
impl CanvasWrapper { | ||
pub fn new(canvas: Canvas) -> Self { | ||
CanvasWrapper(SendSyncWrapper(canvas)) | ||
} | ||
} | ||
|
||
impl Deref for CanvasWrapper { | ||
type Target = Canvas; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 .0 | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct Canvas { | ||
pub(crate) element: web_sys::HtmlCanvasElement, | ||
pub scale_factor: f32, | ||
pub(crate) handle: u32, | ||
} | ||
|
||
#[allow(dead_code)] | ||
impl Canvas { | ||
pub fn new(element_id: &str, handle: u32) -> Self { | ||
// 0 is reserved for window itself. | ||
assert!(handle > 0); | ||
|
||
// 添加 `raw-window-handle` 需要的属性/值. | ||
let (element, scale_factor) = Self::get_canvas_element(element_id); | ||
element | ||
.set_attribute("data-raw-handle", handle.to_string().as_str()) | ||
.unwrap(); | ||
|
||
Self { | ||
element, | ||
scale_factor, | ||
handle, | ||
} | ||
} | ||
|
||
pub fn get_canvas_element(element_id: &str) -> (web_sys::HtmlCanvasElement, f32) { | ||
let window = web_sys::window().unwrap(); | ||
let document = window.document().unwrap(); | ||
let element = document | ||
.get_element_by_id(element_id) | ||
.expect("页面中找不到 canvas 元素 "); | ||
|
||
let canvas = element.dyn_into::<web_sys::HtmlCanvasElement>().unwrap(); | ||
let scale_factor = window.device_pixel_ratio() as f32; | ||
|
||
(canvas, scale_factor) | ||
} | ||
|
||
#[inline] | ||
pub fn handle(&self) -> u32 { | ||
self.handle | ||
} | ||
|
||
pub fn logical_resolution(&self) -> (u32, u32) { | ||
let width = self.element.width(); | ||
let height = self.element.height(); | ||
(width, height) | ||
} | ||
} | ||
|
||
impl Deref for Canvas { | ||
type Target = web_sys::HtmlCanvasElement; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.element | ||
} | ||
} | ||
|
||
impl HasWindowHandle for Canvas { | ||
fn window_handle( | ||
&self, | ||
) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> { | ||
let value: &JsValue = &self.element; | ||
let obj: NonNull<std::ffi::c_void> = NonNull::from(value).cast(); | ||
let handle = WebCanvasWindowHandle::new(obj); | ||
let raw = RawWindowHandle::WebCanvas(handle); | ||
unsafe { Ok(WindowHandle::borrow_raw(raw)) } | ||
} | ||
} | ||
|
||
impl HasDisplayHandle for Canvas { | ||
fn display_handle( | ||
&self, | ||
) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> { | ||
use raw_window_handle::{DisplayHandle, RawDisplayHandle, WebDisplayHandle}; | ||
let handle = WebDisplayHandle::new(); | ||
let raw = RawDisplayHandle::Web(handle); | ||
unsafe { Ok(DisplayHandle::borrow_raw(raw)) } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use crate::IASDQContext; | ||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; | ||
use std::ops::Deref; | ||
|
||
mod canvas; | ||
pub use canvas::*; | ||
|
||
mod offscreen_canvas; | ||
pub use offscreen_canvas::*; | ||
|
||
pub struct AppSurface { | ||
pub view: ViewObj, | ||
pub scale_factor: f32, | ||
pub ctx: IASDQContext, | ||
} | ||
|
||
impl AppSurface { | ||
pub async fn new(view: ViewObj) -> Self { | ||
let (scale_factor, logical_size) = match view { | ||
ViewObj::Canvas(ref canvas) => (canvas.scale_factor, canvas.logical_resolution()), | ||
ViewObj::Offscreen(ref offscreen) => { | ||
(offscreen.scale_factor, offscreen.logical_resolution()) | ||
} | ||
}; | ||
let physical_size = ( | ||
(logical_size.0 as f32 * scale_factor) as u32, | ||
(logical_size.1 as f32 * scale_factor) as u32, | ||
); | ||
|
||
let backends = wgpu::Backends::BROWSER_WEBGPU; | ||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { | ||
backends, | ||
..Default::default() | ||
}); | ||
let surface = unsafe { | ||
instance | ||
.create_surface_unsafe(match view { | ||
ViewObj::Canvas(ref canvas) => wgpu::SurfaceTargetUnsafe::RawHandle { | ||
raw_display_handle: canvas.display_handle().unwrap().into(), | ||
raw_window_handle: canvas.window_handle().unwrap().into(), | ||
}, | ||
ViewObj::Offscreen(ref offscreen) => wgpu::SurfaceTargetUnsafe::RawHandle { | ||
raw_display_handle: offscreen.display_handle().unwrap().into(), | ||
raw_window_handle: offscreen.window_handle().unwrap().into(), | ||
}, | ||
}) | ||
.expect("Surface creation failed") | ||
}; | ||
|
||
let ctx = crate::create_iasdq_context(instance, surface, physical_size).await; | ||
|
||
Self { | ||
view, | ||
scale_factor, | ||
ctx, | ||
} | ||
} | ||
|
||
pub fn get_view_size(&self) -> (u32, u32) { | ||
let (scale_factor, logical_size) = match self.view { | ||
ViewObj::Canvas(ref canvas) => (canvas.scale_factor, canvas.logical_resolution()), | ||
ViewObj::Offscreen(ref offscreen) => { | ||
(offscreen.scale_factor, offscreen.logical_resolution()) | ||
} | ||
}; | ||
( | ||
(logical_size.0 as f32 * scale_factor) as u32, | ||
(logical_size.1 as f32 * scale_factor) as u32, | ||
) | ||
} | ||
} | ||
|
||
/// 用 Canvas id 创建 AppSurface | ||
/// | ||
/// element_id: 存在于当前页面中的 canvas 元素的 id | ||
/// handle: 用于 WebGPU 的 raw handle number, 0 是保留的值, 不能使用 | ||
pub async fn app_surface_from_canvas(element_id: &str, handle: u32) -> AppSurface { | ||
let wrapper = CanvasWrapper::new(Canvas::new(element_id, handle)); | ||
AppSurface::new(ViewObj::Canvas(wrapper)).await | ||
} | ||
|
||
// 封装 ViewObj 来同时支持 Canvas 与 Offscreen | ||
#[derive(Debug)] | ||
pub enum ViewObj { | ||
Canvas(CanvasWrapper), | ||
Offscreen(OffscreenCanvasWrapper), | ||
} | ||
|
||
impl ViewObj { | ||
pub fn from_canvas(canvas: Canvas) -> Self { | ||
ViewObj::Canvas(CanvasWrapper::new(canvas)) | ||
} | ||
|
||
pub fn from_offscreen_canvas(canvas: OffscreenCanvas) -> Self { | ||
ViewObj::Offscreen(OffscreenCanvasWrapper::new(canvas)) | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub(crate) struct SendSyncWrapper<T>(pub(crate) T); | ||
|
||
unsafe impl<T> Send for SendSyncWrapper<T> {} | ||
unsafe impl<T> Sync for SendSyncWrapper<T> {} | ||
|
||
impl Deref for AppSurface { | ||
type Target = IASDQContext; | ||
fn deref(&self) -> &Self::Target { | ||
&self.ctx | ||
} | ||
} | ||
|
||
impl crate::SurfaceFrame for AppSurface { | ||
fn view_size(&self) -> crate::ViewSize { | ||
let size = self.get_view_size(); | ||
crate::ViewSize { | ||
width: size.0, | ||
height: size.1, | ||
} | ||
} | ||
|
||
fn resize_surface(&mut self) { | ||
let size = self.get_view_size(); | ||
self.ctx.config.width = size.0; | ||
self.ctx.config.height = size.1; | ||
self.surface.configure(&self.device, &self.config); | ||
} | ||
|
||
fn resize_surface_by_size(&mut self, size: (u32, u32)) { | ||
self.ctx.config.width = size.0; | ||
self.ctx.config.height = size.1; | ||
self.surface.configure(&self.device, &self.config); | ||
} | ||
|
||
fn normalize_touch_point(&self, touch_point_x: f32, touch_point_y: f32) -> (f32, f32) { | ||
let size = self.get_view_size(); | ||
( | ||
touch_point_x * self.scale_factor / size.0 as f32, | ||
touch_point_y * self.scale_factor / size.1 as f32, | ||
) | ||
} | ||
|
||
fn get_current_frame_view( | ||
&self, | ||
view_format: Option<wgpu::TextureFormat>, | ||
) -> (wgpu::SurfaceTexture, wgpu::TextureView) { | ||
self.create_current_frame_view(&self.device, &self.surface, &self.config, view_format) | ||
} | ||
} |
Oops, something went wrong.