Split webgl into separate crate (#36718)

canvas crate was home for both webgl and 2dcanvas impl, but it makes
more sense to separate these two, by splitting webgl into standalone
crate (like we already have for webgpu).

Testing: Rust for fearless refactoring (but there are WPT tests just in
case)

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-04-30 07:27:44 +02:00 committed by GitHub
parent 0c0ee04b8e
commit 955149b194
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 79 additions and 33 deletions

View file

@ -0,0 +1,36 @@
[package]
name = "webgl"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
publish.workspace = true
rust-version.workspace = true
[lib]
name = "webgl"
path = "lib.rs"
[features]
webgl_backtrace = ["canvas_traits/webgl_backtrace"]
webxr = ["dep:webxr", "dep:webxr-api"]
[dependencies]
bitflags = { workspace = true }
byteorder = { workspace = true }
canvas_traits = { workspace = true }
compositing_traits = { workspace = true }
crossbeam-channel = { workspace = true }
euclid = { workspace = true }
fnv = { workspace = true }
glow = { workspace = true }
half = "2"
ipc-channel = { workspace = true }
log = { workspace = true }
pixels = { path = "../pixels" }
snapshot = { workspace = true }
surfman = { workspace = true }
webrender = { workspace = true }
webrender_api = { workspace = true }
webxr = { path = "../webxr", features = ["ipc"], optional = true }
webxr-api = { workspace = true, features = ["ipc"], optional = true }

13
components/webgl/lib.rs Normal file
View file

@ -0,0 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![deny(unsafe_code)]
pub use webgl_mode::WebGLComm;
mod webgl_limits;
mod webgl_mode;
pub mod webgl_thread;
#[cfg(feature = "webxr")]
mod webxr;

View file

@ -0,0 +1,259 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{GLLimits, WebGLVersion};
use glow::{self as gl, Context as Gl, HasContext};
type GLenum = u32;
pub trait GLLimitsDetect {
fn detect(gl: &Gl, webgl_version: WebGLVersion) -> Self;
}
impl GLLimitsDetect for GLLimits {
fn detect(gl: &Gl, webgl_version: WebGLVersion) -> GLLimits {
let max_vertex_attribs = gl.get_integer(gl::MAX_VERTEX_ATTRIBS);
let max_tex_size = gl.get_integer(gl::MAX_TEXTURE_SIZE);
let max_cube_map_tex_size = gl.get_integer(gl::MAX_CUBE_MAP_TEXTURE_SIZE);
let max_combined_texture_image_units = gl.get_integer(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS);
let max_renderbuffer_size = gl.get_integer(gl::MAX_RENDERBUFFER_SIZE);
let max_texture_image_units = gl.get_integer(gl::MAX_TEXTURE_IMAGE_UNITS);
let max_vertex_texture_image_units = gl.get_integer(gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS);
// TODO: better value for this?
let max_client_wait_timeout_webgl = std::time::Duration::new(1, 0);
// Based on:
// https://searchfox.org/mozilla-central/rev/5a744713370ec47969595e369fd5125f123e6d24/dom/canvas/WebGLContextValidate.cpp#523-558
let (
max_fragment_uniform_vectors,
max_varying_vectors,
max_vertex_uniform_vectors,
max_vertex_output_vectors,
max_fragment_input_vectors,
);
if gl.version().is_embedded {
max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_VECTORS);
max_varying_vectors = gl.get_integer(gl::MAX_VARYING_VECTORS);
max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_VECTORS);
max_vertex_output_vectors = gl
.try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
.map(|c| c / 4)
.unwrap_or(max_varying_vectors);
max_fragment_input_vectors = gl
.try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
.map(|c| c / 4)
.unwrap_or(max_vertex_output_vectors);
} else {
max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4;
max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS) / 4;
max_fragment_input_vectors = gl
.try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
.or_else(|| gl.try_get_integer(gl::MAX_VARYING_COMPONENTS))
.map(|c| c / 4)
.unwrap_or_else(|| gl.get_integer(gl::MAX_VARYING_VECTORS));
max_vertex_output_vectors = gl
.try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
.map(|c| c / 4)
.unwrap_or(max_fragment_input_vectors);
max_varying_vectors = max_vertex_output_vectors
.min(max_fragment_input_vectors)
.max(4);
};
let (
max_uniform_block_size,
max_uniform_buffer_bindings,
min_program_texel_offset,
max_program_texel_offset,
max_transform_feedback_separate_attribs,
max_draw_buffers,
max_color_attachments,
max_combined_uniform_blocks,
max_combined_vertex_uniform_components,
max_combined_fragment_uniform_components,
max_vertex_uniform_blocks,
max_vertex_uniform_components,
max_fragment_uniform_blocks,
max_fragment_uniform_components,
max_3d_texture_size,
max_array_texture_layers,
uniform_buffer_offset_alignment,
max_element_index,
max_elements_indices,
max_elements_vertices,
max_fragment_input_components,
max_samples,
max_server_wait_timeout,
max_texture_lod_bias,
max_varying_components,
max_vertex_output_components,
);
if webgl_version == WebGLVersion::WebGL2 {
max_uniform_block_size = gl.get_integer64(gl::MAX_UNIFORM_BLOCK_SIZE);
max_uniform_buffer_bindings = gl.get_integer(gl::MAX_UNIFORM_BUFFER_BINDINGS);
min_program_texel_offset = gl.get_signed_integer(gl::MIN_PROGRAM_TEXEL_OFFSET);
max_program_texel_offset = gl.get_integer(gl::MAX_PROGRAM_TEXEL_OFFSET);
max_transform_feedback_separate_attribs =
gl.get_integer(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS);
max_color_attachments = gl.get_integer(gl::MAX_COLOR_ATTACHMENTS);
max_draw_buffers = gl
.get_integer(gl::MAX_DRAW_BUFFERS)
.min(max_color_attachments);
max_combined_uniform_blocks = gl.get_integer(gl::MAX_COMBINED_UNIFORM_BLOCKS);
max_combined_vertex_uniform_components =
gl.get_integer64(gl::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS);
max_combined_fragment_uniform_components =
gl.get_integer64(gl::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS);
max_vertex_uniform_blocks = gl.get_integer(gl::MAX_VERTEX_UNIFORM_BLOCKS);
max_vertex_uniform_components = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS);
max_fragment_uniform_blocks = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_BLOCKS);
max_fragment_uniform_components = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS);
uniform_buffer_offset_alignment = gl.get_integer(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT);
max_3d_texture_size = gl.get_integer(gl::MAX_3D_TEXTURE_SIZE);
max_array_texture_layers = gl.get_integer(gl::MAX_ARRAY_TEXTURE_LAYERS);
max_element_index = gl
.try_get_integer64(gl::MAX_ELEMENT_INDEX)
.unwrap_or(u32::MAX as u64); // requires GL 4.3
max_elements_indices = gl.get_integer(gl::MAX_ELEMENTS_INDICES);
max_elements_vertices = gl.get_integer(gl::MAX_ELEMENTS_VERTICES);
max_fragment_input_components = gl.get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS);
max_samples = gl.get_integer(gl::MAX_SAMPLES);
max_server_wait_timeout =
std::time::Duration::from_nanos(gl.get_integer64(gl::MAX_SERVER_WAIT_TIMEOUT));
max_texture_lod_bias = gl.get_float(gl::MAX_TEXTURE_LOD_BIAS);
max_varying_components = gl.try_get_integer(gl::MAX_VARYING_COMPONENTS).unwrap_or(
// macOS Core Profile is buggy. The spec says this value is 4 * MAX_VARYING_VECTORS.
max_varying_vectors * 4,
);
max_vertex_output_components = gl.get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS);
} else {
max_uniform_block_size = 0;
max_uniform_buffer_bindings = 0;
min_program_texel_offset = 0;
max_program_texel_offset = 0;
max_transform_feedback_separate_attribs = 0;
max_color_attachments = 1;
max_draw_buffers = 1;
max_combined_uniform_blocks = 0;
max_combined_vertex_uniform_components = 0;
max_combined_fragment_uniform_components = 0;
max_vertex_uniform_blocks = 0;
max_vertex_uniform_components = 0;
max_fragment_uniform_blocks = 0;
max_fragment_uniform_components = 0;
uniform_buffer_offset_alignment = 0;
max_3d_texture_size = 0;
max_array_texture_layers = 0;
max_element_index = 0;
max_elements_indices = 0;
max_elements_vertices = 0;
max_fragment_input_components = 0;
max_samples = 0;
max_server_wait_timeout = std::time::Duration::default();
max_texture_lod_bias = 0.0;
max_varying_components = 0;
max_vertex_output_components = 0;
}
GLLimits {
max_vertex_attribs,
max_tex_size,
max_cube_map_tex_size,
max_combined_texture_image_units,
max_fragment_uniform_vectors,
max_renderbuffer_size,
max_texture_image_units,
max_varying_vectors,
max_vertex_texture_image_units,
max_vertex_uniform_vectors,
max_client_wait_timeout_webgl,
max_transform_feedback_separate_attribs,
max_vertex_output_vectors,
max_fragment_input_vectors,
max_uniform_buffer_bindings,
min_program_texel_offset,
max_program_texel_offset,
max_color_attachments,
max_draw_buffers,
max_uniform_block_size,
max_combined_uniform_blocks,
max_combined_vertex_uniform_components,
max_combined_fragment_uniform_components,
max_vertex_uniform_blocks,
max_vertex_uniform_components,
max_fragment_uniform_blocks,
max_fragment_uniform_components,
max_3d_texture_size,
max_array_texture_layers,
uniform_buffer_offset_alignment,
max_element_index,
max_elements_indices,
max_elements_vertices,
max_fragment_input_components,
max_samples,
max_server_wait_timeout,
max_texture_lod_bias,
max_varying_components,
max_vertex_output_components,
}
}
}
trait GLExt {
fn try_get_integer(self, parameter: GLenum) -> Option<u32>;
fn try_get_integer64(self, parameter: GLenum) -> Option<u64>;
fn try_get_signed_integer(self, parameter: GLenum) -> Option<i32>;
fn try_get_float(self, parameter: GLenum) -> Option<f32>;
fn get_integer(self, parameter: GLenum) -> u32;
fn get_integer64(self, parameter: GLenum) -> u64;
fn get_signed_integer(self, parameter: GLenum) -> i32;
fn get_float(self, parameter: GLenum) -> f32;
}
macro_rules! create_fun {
($tryer:ident, $getter:ident, $gltype:ty, $glcall:ident, $rstype:ty) => {
#[allow(unsafe_code)]
fn $tryer(self, parameter: GLenum) -> Option<$rstype> {
let mut value = [<$gltype>::default()];
unsafe {
self.$glcall(parameter, &mut value);
}
if unsafe { self.get_error() } != gl::NO_ERROR {
None
} else {
Some(value[0] as $rstype)
}
}
fn $getter(self, parameter: GLenum) -> $rstype {
self.$tryer(parameter).unwrap()
}
};
}
impl GLExt for &Gl {
create_fun!(
try_get_integer,
get_integer,
i32,
get_parameter_i32_slice,
u32
);
create_fun!(
try_get_integer64,
get_integer64,
i64,
get_parameter_i64_slice,
u64
);
create_fun!(
try_get_signed_integer,
get_signed_integer,
i32,
get_parameter_i32_slice,
i32
);
create_fun!(try_get_float, get_float, f32, get_parameter_f32_slice, f32);
}

View file

@ -0,0 +1,147 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::default::Default;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use canvas_traits::webgl::{GlType, WebGLContextId, WebGLMsg, WebGLThreads, webgl_channel};
use compositing_traits::rendering_context::RenderingContext;
use compositing_traits::{
WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource,
};
use euclid::default::Size2D;
use fnv::FnvHashMap;
use log::debug;
use surfman::chains::{SwapChainAPI, SwapChains, SwapChainsAPI};
use surfman::{Device, SurfaceTexture};
use webrender::RenderApiSender;
use webrender_api::DocumentId;
#[cfg(feature = "webxr")]
use webxr::SurfmanGL as WebXRSurfman;
#[cfg(feature = "webxr")]
use webxr_api::LayerGrandManager as WebXRLayerGrandManager;
use crate::webgl_thread::{WebGLThread, WebGLThreadInit};
pub struct WebGLComm {
pub webgl_threads: WebGLThreads,
pub image_handler: Box<dyn WebrenderExternalImageApi>,
#[cfg(feature = "webxr")]
pub webxr_layer_grand_manager: WebXRLayerGrandManager<WebXRSurfman>,
}
impl WebGLComm {
/// Creates a new `WebGLComm` object.
pub fn new(
rendering_context: Rc<dyn RenderingContext>,
webrender_api_sender: RenderApiSender,
webrender_doc: DocumentId,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
api_type: GlType,
) -> WebGLComm {
debug!("WebGLThreads::new()");
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
let webrender_swap_chains = SwapChains::new();
#[cfg(feature = "webxr")]
let webxr_init = crate::webxr::WebXRBridgeInit::new(sender.clone());
#[cfg(feature = "webxr")]
let webxr_layer_grand_manager = webxr_init.layer_grand_manager();
let connection = rendering_context
.connection()
.expect("Failed to get connection");
let adapter = connection
.create_adapter()
.expect("Failed to create adapter");
// This implementation creates a single `WebGLThread` for all the pipelines.
let init = WebGLThreadInit {
webrender_api_sender,
webrender_doc,
external_images,
sender: sender.clone(),
receiver,
webrender_swap_chains: webrender_swap_chains.clone(),
connection,
adapter,
api_type,
#[cfg(feature = "webxr")]
webxr_init,
};
let external = WebGLExternalImages::new(rendering_context, webrender_swap_chains);
WebGLThread::run_on_own_thread(init);
WebGLComm {
webgl_threads: WebGLThreads(sender),
image_handler: Box::new(external),
#[cfg(feature = "webxr")]
webxr_layer_grand_manager,
}
}
}
/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
struct WebGLExternalImages {
rendering_context: Rc<dyn RenderingContext>,
swap_chains: SwapChains<WebGLContextId, Device>,
locked_front_buffers: FnvHashMap<WebGLContextId, SurfaceTexture>,
}
impl WebGLExternalImages {
fn new(
rendering_context: Rc<dyn RenderingContext>,
swap_chains: SwapChains<WebGLContextId, Device>,
) -> Self {
Self {
rendering_context,
swap_chains,
locked_front_buffers: FnvHashMap::default(),
}
}
fn lock_swap_chain(&mut self, id: WebGLContextId) -> Option<(u32, Size2D<i32>)> {
debug!("... locking chain {:?}", id);
let front_buffer = self.swap_chains.get(id)?.take_surface()?;
if let Some((surface_texture, gl_texture, size)) =
self.rendering_context.create_texture(front_buffer)
{
self.locked_front_buffers.insert(id, surface_texture);
Some((gl_texture, size))
} else {
None
}
}
fn unlock_swap_chain(&mut self, id: WebGLContextId) -> Option<()> {
debug!("... unlocked chain {:?}", id);
let locked_front_buffer = self.locked_front_buffers.remove(&id)?;
if let Some(locked_front_buffer) =
self.rendering_context.destroy_texture(locked_front_buffer)
{
self.swap_chains
.get(id)?
.recycle_surface(locked_front_buffer);
Some(())
} else {
None
}
}
}
impl WebrenderExternalImageApi for WebGLExternalImages {
fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) {
let id = WebGLContextId(id);
let (texture_id, size) = self.lock_swap_chain(id).unwrap_or_default();
(WebrenderImageSource::TextureHandle(texture_id), size)
}
fn unlock(&mut self, id: u64) {
let id = WebGLContextId(id);
self.unlock_swap_chain(id);
}
}

View file

@ -0,0 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
mod inprocess;
pub use self::inprocess::WebGLComm;

File diff suppressed because it is too large Load diff

337
components/webgl/webxr.rs Normal file
View file

@ -0,0 +1,337 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::num::NonZeroU32;
use canvas_traits::webgl::{
WebGLContextId, WebGLMsg, WebGLSender, WebXRCommand, WebXRLayerManagerId, webgl_channel,
};
use fnv::FnvHashMap;
use surfman::{Context, Device};
use webxr::SurfmanGL as WebXRSurfman;
use webxr_api::{
ContextId as WebXRContextId, Error as WebXRError, GLContexts as WebXRContexts,
GLTypes as WebXRTypes, LayerGrandManager as WebXRLayerGrandManager,
LayerGrandManagerAPI as WebXRLayerGrandManagerAPI, LayerId as WebXRLayerId,
LayerInit as WebXRLayerInit, LayerManager as WebXRLayerManager,
LayerManagerAPI as WebXRLayerManagerAPI, LayerManagerFactory as WebXRLayerManagerFactory,
SubImages as WebXRSubImages,
};
use crate::webgl_thread::{GLContextData, WebGLThread};
/// Bridge between WebGL and WebXR
pub(crate) struct WebXRBridge {
factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>,
managers: HashMap<WebXRLayerManagerId, Box<dyn WebXRLayerManagerAPI<WebXRSurfman>>>,
next_manager_id: NonZeroU32,
}
impl WebXRBridge {
pub(crate) fn new(init: WebXRBridgeInit) -> WebXRBridge {
let WebXRBridgeInit {
factory_receiver, ..
} = init;
let managers = HashMap::new();
let next_manager_id = NonZeroU32::MIN;
WebXRBridge {
factory_receiver,
managers,
next_manager_id,
}
}
}
impl WebXRBridge {
#[allow(unsafe_code)]
pub(crate) fn create_layer_manager(
&mut self,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
) -> Result<WebXRLayerManagerId, WebXRError> {
let factory = self
.factory_receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?;
let manager = factory.build(device, contexts)?;
let manager_id = WebXRLayerManagerId::new(self.next_manager_id);
self.next_manager_id = self
.next_manager_id
.checked_add(1)
.expect("next_manager_id should not overflow");
self.managers.insert(manager_id, manager);
Ok(manager_id)
}
pub(crate) fn destroy_layer_manager(&mut self, manager_id: WebXRLayerManagerId) {
self.managers.remove(&manager_id);
}
pub(crate) fn create_layer(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
layer_init: WebXRLayerInit,
) -> Result<WebXRLayerId, WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.create_layer(device, contexts, context_id, layer_init)
}
pub(crate) fn destroy_layer(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
layer_id: WebXRLayerId,
) {
if let Some(manager) = self.managers.get_mut(&manager_id) {
manager.destroy_layer(device, contexts, context_id, layer_id);
}
}
pub(crate) fn destroy_all_layers(
&mut self,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
) {
for manager in self.managers.values_mut() {
#[allow(clippy::unnecessary_to_owned)] // Needs mutable borrow later in destroy
for (other_id, layer_id) in manager.layers().to_vec() {
if other_id == context_id {
manager.destroy_layer(device, contexts, context_id, layer_id);
}
}
}
}
pub(crate) fn begin_frame(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<Vec<WebXRSubImages>, WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.begin_frame(device, contexts, layers)
}
pub(crate) fn end_frame(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<(), WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.end_frame(device, contexts, layers)
}
}
pub(crate) struct WebXRBridgeInit {
sender: WebGLSender<WebGLMsg>,
factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>,
factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>,
}
impl WebXRBridgeInit {
pub(crate) fn new(sender: WebGLSender<WebGLMsg>) -> WebXRBridgeInit {
let (factory_sender, factory_receiver) = crossbeam_channel::unbounded();
WebXRBridgeInit {
sender,
factory_sender,
factory_receiver,
}
}
pub(crate) fn layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> {
WebXRLayerGrandManager::new(WebXRBridgeGrandManager {
sender: self.sender.clone(),
factory_sender: self.factory_sender.clone(),
})
}
}
struct WebXRBridgeGrandManager {
sender: WebGLSender<WebGLMsg>,
// WebXR layer manager factories use generic trait objects under the
// hood, which aren't deserializable (even using typetag)
// so we can't send them over the regular webgl channel.
// Fortunately, the webgl thread runs in the same process as
// the webxr threads, so we can use a crossbeam channel to send
// factories.
factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>,
}
impl WebXRLayerGrandManagerAPI<WebXRSurfman> for WebXRBridgeGrandManager {
fn create_layer_manager(
&self,
factory: WebXRLayerManagerFactory<WebXRSurfman>,
) -> Result<WebXRLayerManager, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self.factory_sender.send(factory);
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayerManager(
sender,
)));
let sender = self.sender.clone();
let manager_id = receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)??;
let layers = Vec::new();
Ok(WebXRLayerManager::new(WebXRBridgeManager {
manager_id,
sender,
layers,
}))
}
fn clone_layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> {
WebXRLayerGrandManager::new(WebXRBridgeGrandManager {
sender: self.sender.clone(),
factory_sender: self.factory_sender.clone(),
})
}
}
struct WebXRBridgeManager {
sender: WebGLSender<WebGLMsg>,
manager_id: WebXRLayerManagerId,
layers: Vec<(WebXRContextId, WebXRLayerId)>,
}
impl<GL: WebXRTypes> WebXRLayerManagerAPI<GL> for WebXRBridgeManager {
fn create_layer(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
context_id: WebXRContextId,
init: WebXRLayerInit,
) -> Result<WebXRLayerId, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayer(
self.manager_id,
context_id,
init,
sender,
)));
let layer_id = receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)??;
self.layers.push((context_id, layer_id));
Ok(layer_id)
}
fn destroy_layer(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
context_id: WebXRContextId,
layer_id: WebXRLayerId,
) {
self.layers.retain(|&ids| ids != (context_id, layer_id));
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayer(
self.manager_id,
context_id,
layer_id,
)));
}
fn layers(&self) -> &[(WebXRContextId, WebXRLayerId)] {
&self.layers[..]
}
fn begin_frame(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<Vec<WebXRSubImages>, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::BeginFrame(
self.manager_id,
layers.to_vec(),
sender,
)));
receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?
}
fn end_frame(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<(), WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::EndFrame(
self.manager_id,
layers.to_vec(),
sender,
)));
receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?
}
}
impl Drop for WebXRBridgeManager {
fn drop(&mut self) {
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayerManager(
self.manager_id,
)));
}
}
pub(crate) struct WebXRBridgeContexts<'a> {
pub(crate) contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>,
pub(crate) bound_context_id: &'a mut Option<WebGLContextId>,
}
impl WebXRContexts<WebXRSurfman> for WebXRBridgeContexts<'_> {
fn context(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&mut Context> {
let data = WebGLThread::make_current_if_needed_mut(
device,
WebGLContextId::from(context_id),
self.contexts,
self.bound_context_id,
)?;
Some(&mut data.ctx)
}
fn bindings(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&glow::Context> {
let data = WebGLThread::make_current_if_needed(
device,
WebGLContextId::from(context_id),
self.contexts,
self.bound_context_id,
)?;
Some(&data.gl)
}
}