mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
Use surfman for managing GL surfaces
Co-authored-by: Alan Jeffrey <ajeffrey@mozilla.com> Co-authored-by: Zakor Gyula <gyula.zakor@h-lab.eu> Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
48d918dcde
commit
a358bca766
52 changed files with 1929 additions and 2195 deletions
|
@ -14,10 +14,11 @@ path = "lib.rs"
|
|||
canvas2d-azure = ["azure"]
|
||||
canvas2d-raqote = ["raqote"]
|
||||
webgl_backtrace = ["canvas_traits/webgl_backtrace"]
|
||||
no_wgl = ["offscreen_gl_context/no_wgl"]
|
||||
no-wgl = ["surfman/sm-no-wgl"]
|
||||
|
||||
[dependencies]
|
||||
azure = {git = "https://github.com/servo/rust-azure", optional = true}
|
||||
bitflags = "1.0"
|
||||
byteorder = "1"
|
||||
canvas_traits = {path = "../canvas_traits"}
|
||||
cssparser = "0.27.1"
|
||||
|
@ -29,12 +30,14 @@ half = "1"
|
|||
ipc-channel = "0.12"
|
||||
log = "0.4"
|
||||
num-traits = "0.2"
|
||||
offscreen_gl_context = {version = "0.25", features = ["serde", "osmesa"]}
|
||||
raqote = {git = "https://github.com/jrmuizel/raqote", optional = true}
|
||||
pixels = {path = "../pixels"}
|
||||
servo_config = {path = "../config"}
|
||||
sparkle = "0.1"
|
||||
sparkle = "0.1.8"
|
||||
webrender = {git = "https://github.com/servo/webrender"}
|
||||
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
|
||||
webrender_traits = {path = "../webrender_traits"}
|
||||
webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]}
|
||||
# NOTE: the sm-angle feature only enables angle on windows, not other platforms!
|
||||
surfman = { git = "https://github.com/pcwalton/surfman", features = ["sm-angle", "sm-osmesa"] }
|
||||
surfman-chains = { git = "https://github.com/asajeffrey/surfman-chains" }
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
/* 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 super::webgl_thread::{GLState, WebGLImpl};
|
||||
use canvas_traits::webgl::{
|
||||
GLContextAttributes, GLFormats, GLLimits, WebGLCommand, WebGLCommandBacktrace, WebGLVersion,
|
||||
};
|
||||
use euclid::default::Size2D;
|
||||
use offscreen_gl_context::{
|
||||
ColorAttachmentType, DrawBuffer, GLContext, GLContextAttributes as RawGLContextAttributes,
|
||||
GLContextDispatcher,
|
||||
};
|
||||
use offscreen_gl_context::{GLFormats as RawGLFormats, GLLimits as RawGLLimits, GLVersion};
|
||||
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle, NativeGLContextMethods};
|
||||
use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle};
|
||||
use sparkle::gl;
|
||||
|
||||
pub trait CloneableDispatcher: GLContextDispatcher {
|
||||
fn clone(&self) -> Box<dyn GLContextDispatcher>;
|
||||
}
|
||||
|
||||
/// The GLContextFactory is used to create shared GL contexts with the main thread GL context.
|
||||
/// Currently, shared textures are used to render WebGL textures into the WR compositor.
|
||||
/// In order to create a shared context, the GLContextFactory stores the handle of the main GL context.
|
||||
pub enum GLContextFactory {
|
||||
Native(
|
||||
NativeGLContextHandle,
|
||||
Option<Box<dyn CloneableDispatcher + Send>>,
|
||||
gl::GlType,
|
||||
),
|
||||
OSMesa(OSMesaContextHandle),
|
||||
}
|
||||
|
||||
impl GLContextFactory {
|
||||
/// Creates a new GLContextFactory that uses the currently bound GL context to create shared contexts.
|
||||
pub fn current_native_handle(
|
||||
dispatcher: Box<dyn CloneableDispatcher + Send>,
|
||||
api_type: gl::GlType,
|
||||
) -> Option<GLContextFactory> {
|
||||
let dispatcher = if cfg!(target_os = "windows") {
|
||||
// Used to dispatch functions from the GLContext thread to the main thread's
|
||||
// event loop. Required to allow WGL GLContext sharing in Windows.
|
||||
Some(dispatcher)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// FIXME(emilio): This assumes a single GL backend per platform which is
|
||||
// not true on Linux, we probably need a third `Egl` variant or abstract
|
||||
// it a bit more...
|
||||
NativeGLContext::current_handle()
|
||||
.map(|handle| GLContextFactory::Native(handle, dispatcher, api_type))
|
||||
}
|
||||
|
||||
/// Creates a new GLContextFactory that uses the currently bound OSMesa context to create shared contexts.
|
||||
pub fn current_osmesa_handle() -> Option<GLContextFactory> {
|
||||
OSMesaContext::current_handle().map(GLContextFactory::OSMesa)
|
||||
}
|
||||
|
||||
/// Creates a new shared GLContext with the main GLContext
|
||||
pub fn new_shared_context(
|
||||
&self,
|
||||
webgl_version: WebGLVersion,
|
||||
size: Size2D<u32>,
|
||||
attributes: GLContextAttributes,
|
||||
) -> Result<GLContextWrapper, &'static str> {
|
||||
let attributes = map_attrs(attributes);
|
||||
Ok(match *self {
|
||||
GLContextFactory::Native(ref handle, ref dispatcher, ref api_type) => {
|
||||
GLContextWrapper::Native(GLContext::new_shared_with_dispatcher(
|
||||
// FIXME(nox): Why are those i32 values?
|
||||
size.to_i32(),
|
||||
attributes,
|
||||
ColorAttachmentType::Texture,
|
||||
*api_type,
|
||||
Self::gl_version(webgl_version),
|
||||
Some(handle),
|
||||
dispatcher.as_ref().map(|d| (**d).clone()),
|
||||
)?)
|
||||
},
|
||||
GLContextFactory::OSMesa(ref handle) => {
|
||||
GLContextWrapper::OSMesa(GLContext::new_shared_with_dispatcher(
|
||||
// FIXME(nox): Why are those i32 values?
|
||||
size.to_i32(),
|
||||
attributes,
|
||||
ColorAttachmentType::Texture,
|
||||
gl::GlType::Gl,
|
||||
Self::gl_version(webgl_version),
|
||||
Some(handle),
|
||||
None,
|
||||
)?)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new non-shared GLContext
|
||||
pub fn new_context(
|
||||
&self,
|
||||
webgl_version: WebGLVersion,
|
||||
size: Size2D<u32>,
|
||||
attributes: GLContextAttributes,
|
||||
) -> Result<GLContextWrapper, &'static str> {
|
||||
let attributes = map_attrs(attributes);
|
||||
Ok(match *self {
|
||||
GLContextFactory::Native(_, _, ref api_type) => {
|
||||
GLContextWrapper::Native(GLContext::new_shared_with_dispatcher(
|
||||
// FIXME(nox): Why are those i32 values?
|
||||
size.to_i32(),
|
||||
attributes,
|
||||
ColorAttachmentType::Texture,
|
||||
*api_type,
|
||||
Self::gl_version(webgl_version),
|
||||
None,
|
||||
None,
|
||||
)?)
|
||||
},
|
||||
GLContextFactory::OSMesa(_) => {
|
||||
GLContextWrapper::OSMesa(GLContext::new_shared_with_dispatcher(
|
||||
// FIXME(nox): Why are those i32 values?
|
||||
size.to_i32(),
|
||||
attributes,
|
||||
ColorAttachmentType::Texture,
|
||||
gl::GlType::Gl,
|
||||
Self::gl_version(webgl_version),
|
||||
None,
|
||||
None,
|
||||
)?)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn gl_version(webgl_version: WebGLVersion) -> GLVersion {
|
||||
match webgl_version {
|
||||
WebGLVersion::WebGL1 => GLVersion::Major(2),
|
||||
WebGLVersion::WebGL2 => GLVersion::Major(3),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GLContextWrapper used to abstract NativeGLContext and OSMesaContext types
|
||||
pub enum GLContextWrapper {
|
||||
Native(GLContext<NativeGLContext>),
|
||||
OSMesa(GLContext<OSMesaContext>),
|
||||
}
|
||||
|
||||
impl GLContextWrapper {
|
||||
pub fn make_current(&self) {
|
||||
match *self {
|
||||
GLContextWrapper::Native(ref ctx) => {
|
||||
ctx.make_current().unwrap();
|
||||
},
|
||||
GLContextWrapper::OSMesa(ref ctx) => {
|
||||
ctx.make_current().unwrap();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_command(
|
||||
&self,
|
||||
cmd: WebGLCommand,
|
||||
use_apple_vertex_array: bool,
|
||||
backtrace: WebGLCommandBacktrace,
|
||||
state: &mut GLState,
|
||||
) {
|
||||
match *self {
|
||||
GLContextWrapper::Native(ref ctx) => {
|
||||
WebGLImpl::apply(ctx, state, use_apple_vertex_array, cmd, backtrace);
|
||||
},
|
||||
GLContextWrapper::OSMesa(ref ctx) => {
|
||||
WebGLImpl::apply(ctx, state, false, cmd, backtrace);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gl(&self) -> &gl::Gl {
|
||||
match *self {
|
||||
GLContextWrapper::Native(ref ctx) => ctx.gl(),
|
||||
GLContextWrapper::OSMesa(ref ctx) => ctx.gl(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn framebuffer(&self) -> gl::GLuint {
|
||||
match *self {
|
||||
GLContextWrapper::Native(ref ctx) => {
|
||||
ctx.borrow_draw_buffer().unwrap().get_framebuffer()
|
||||
},
|
||||
GLContextWrapper::OSMesa(ref ctx) => {
|
||||
ctx.borrow_draw_buffer().unwrap().get_framebuffer()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_info(&self) -> (Size2D<i32>, u32, GLLimits, GLFormats) {
|
||||
match *self {
|
||||
GLContextWrapper::Native(ref ctx) => {
|
||||
let (real_size, texture_id) = {
|
||||
let draw_buffer = ctx.borrow_draw_buffer().unwrap();
|
||||
(
|
||||
draw_buffer.size(),
|
||||
draw_buffer.get_bound_texture_id().unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
let limits = ctx.borrow_limits().clone();
|
||||
let formats = map_formats(ctx.borrow_formats());
|
||||
|
||||
(real_size, texture_id, map_limits(limits), formats)
|
||||
},
|
||||
GLContextWrapper::OSMesa(ref ctx) => {
|
||||
let (real_size, texture_id) = {
|
||||
let draw_buffer = ctx.borrow_draw_buffer().unwrap();
|
||||
(
|
||||
draw_buffer.size(),
|
||||
draw_buffer.get_bound_texture_id().unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
let limits = ctx.borrow_limits().clone();
|
||||
let formats = map_formats(ctx.borrow_formats());
|
||||
|
||||
(real_size, texture_id, map_limits(limits), formats)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: Size2D<u32>) -> Result<DrawBuffer, &'static str> {
|
||||
match *self {
|
||||
GLContextWrapper::Native(ref mut ctx) => {
|
||||
// FIXME(nox): Why are those i32 values?
|
||||
ctx.resize(size.to_i32())
|
||||
},
|
||||
GLContextWrapper::OSMesa(ref mut ctx) => {
|
||||
// FIXME(nox): Why are those i32 values?
|
||||
ctx.resize(size.to_i32())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_limits(limits: RawGLLimits) -> GLLimits {
|
||||
GLLimits {
|
||||
max_vertex_attribs: limits.max_vertex_attribs,
|
||||
max_tex_size: limits.max_tex_size,
|
||||
max_cube_map_tex_size: limits.max_cube_map_tex_size,
|
||||
max_combined_texture_image_units: limits.max_combined_texture_image_units,
|
||||
max_fragment_uniform_vectors: limits.max_fragment_uniform_vectors,
|
||||
max_renderbuffer_size: limits.max_renderbuffer_size,
|
||||
max_texture_image_units: limits.max_texture_image_units,
|
||||
max_varying_vectors: limits.max_varying_vectors,
|
||||
max_vertex_texture_image_units: limits.max_vertex_texture_image_units,
|
||||
max_vertex_uniform_vectors: limits.max_vertex_uniform_vectors,
|
||||
max_client_wait_timeout_webgl: std::time::Duration::new(1, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_attrs(attrs: GLContextAttributes) -> RawGLContextAttributes {
|
||||
RawGLContextAttributes {
|
||||
alpha: attrs.alpha,
|
||||
depth: attrs.depth,
|
||||
stencil: attrs.stencil,
|
||||
antialias: attrs.antialias,
|
||||
premultiplied_alpha: attrs.premultiplied_alpha,
|
||||
preserve_drawing_buffer: attrs.preserve_drawing_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_attrs_to_script_attrs(attrs: RawGLContextAttributes) -> GLContextAttributes {
|
||||
GLContextAttributes {
|
||||
alpha: attrs.alpha,
|
||||
depth: attrs.depth,
|
||||
stencil: attrs.stencil,
|
||||
antialias: attrs.antialias,
|
||||
premultiplied_alpha: attrs.premultiplied_alpha,
|
||||
preserve_drawing_buffer: attrs.preserve_drawing_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_formats(formats: &RawGLFormats) -> GLFormats {
|
||||
GLFormats {
|
||||
texture_format: formats.texture,
|
||||
texture_type: formats.texture_type,
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
|
@ -13,8 +15,10 @@ mod azure_backend;
|
|||
#[cfg(feature = "canvas2d-raqote")]
|
||||
mod raqote_backend;
|
||||
|
||||
pub use webgl_mode::WebGLComm;
|
||||
|
||||
pub mod canvas_data;
|
||||
pub mod canvas_paint_thread;
|
||||
pub mod gl_context;
|
||||
mod webgl_limits;
|
||||
mod webgl_mode;
|
||||
pub mod webgl_thread;
|
||||
|
|
96
components/canvas/webgl_limits.rs
Normal file
96
components/canvas/webgl_limits.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* 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;
|
||||
use sparkle::gl;
|
||||
use sparkle::gl::GLenum;
|
||||
use sparkle::gl::Gl;
|
||||
|
||||
pub trait GLLimitsDetect {
|
||||
fn detect(gl: &Gl) -> Self;
|
||||
}
|
||||
|
||||
impl GLLimitsDetect for GLLimits {
|
||||
fn detect(gl: &Gl) -> 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);
|
||||
match gl.try_get_integer(gl::MAX_FRAGMENT_UNIFORM_VECTORS) {
|
||||
Some(max_vectors) => {
|
||||
max_fragment_uniform_vectors = max_vectors;
|
||||
max_varying_vectors = gl.get_integer(gl::MAX_VARYING_VECTORS);
|
||||
max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_VECTORS);
|
||||
},
|
||||
None => {
|
||||
let max_fragment_uniform_components =
|
||||
gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS);
|
||||
let max_vertex_uniform_components =
|
||||
gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS);
|
||||
|
||||
let max_vertex_output_components = gl
|
||||
.try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS)
|
||||
.unwrap_or(0);
|
||||
let max_fragment_input_components = gl
|
||||
.try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS)
|
||||
.unwrap_or(0);
|
||||
let max_varying_components = max_vertex_output_components
|
||||
.min(max_fragment_input_components)
|
||||
.max(16);
|
||||
|
||||
max_fragment_uniform_vectors = max_fragment_uniform_components / 4;
|
||||
max_varying_vectors = max_varying_components / 4;
|
||||
max_vertex_uniform_vectors = max_vertex_uniform_components / 4;
|
||||
},
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait GLExt {
|
||||
fn try_get_integer(self, parameter: GLenum) -> Option<u32>;
|
||||
fn get_integer(self, parameter: GLenum) -> u32;
|
||||
}
|
||||
|
||||
impl<'a> GLExt for &'a Gl {
|
||||
#[allow(unsafe_code)]
|
||||
fn try_get_integer(self, parameter: GLenum) -> Option<u32> {
|
||||
let mut value = [0];
|
||||
unsafe {
|
||||
self.get_integer_v(parameter, &mut value);
|
||||
}
|
||||
if self.get_error() != gl::NO_ERROR {
|
||||
None
|
||||
} else {
|
||||
Some(value[0] as u32)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_integer(self, parameter: GLenum) -> u32 {
|
||||
self.try_get_integer(parameter).unwrap()
|
||||
}
|
||||
}
|
|
@ -1,224 +1,160 @@
|
|||
/* 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 crate::gl_context::GLContextFactory;
|
||||
use crate::webgl_thread::{WebGLMainThread, WebGLThread, WebGLThreadInit};
|
||||
use canvas_traits::webgl::webgl_channel;
|
||||
use canvas_traits::webgl::DOMToTextureCommand;
|
||||
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
|
||||
use canvas_traits::webgl::{WebGLSender, WebVRRenderHandler};
|
||||
use embedder_traits::EventLoopWaker;
|
||||
|
||||
use crate::webgl_thread::{WebGLThread, WebGLThreadInit};
|
||||
use canvas_traits::webgl::{webgl_channel, WebVRRenderHandler};
|
||||
use canvas_traits::webgl::{WebGLContextId, WebGLMsg, WebGLThreads};
|
||||
use euclid::default::Size2D;
|
||||
use fnv::FnvHashMap;
|
||||
use gleam::gl;
|
||||
use gleam;
|
||||
use servo_config::pref;
|
||||
use sparkle::gl;
|
||||
use sparkle::gl::GlType;
|
||||
use std::default::Default;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use surfman::platform::generic::universal::context::Context;
|
||||
use surfman::platform::generic::universal::device::Device;
|
||||
use surfman::platform::generic::universal::surface::SurfaceTexture;
|
||||
use surfman_chains::SwapChains;
|
||||
use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry};
|
||||
use webxr_api::WebGLExternalImageApi;
|
||||
use webxr_api::SwapChainId as WebXRSwapChainId;
|
||||
|
||||
/// WebGL Threading API entry point that lives in the constellation.
|
||||
pub struct WebGLThreads(WebGLSender<WebGLMsg>);
|
||||
|
||||
pub enum ThreadMode {
|
||||
MainThread(Box<dyn EventLoopWaker>),
|
||||
OffThread,
|
||||
pub struct WebGLComm {
|
||||
pub webgl_threads: WebGLThreads,
|
||||
pub webxr_swap_chains: SwapChains<WebXRSwapChainId>,
|
||||
pub image_handler: Box<dyn WebrenderExternalImageApi>,
|
||||
pub output_handler: Option<Box<dyn webrender::OutputImageHandler>>,
|
||||
}
|
||||
|
||||
impl WebGLThreads {
|
||||
/// Creates a new WebGLThreads object
|
||||
impl WebGLComm {
|
||||
/// Creates a new `WebGLComm` object.
|
||||
pub fn new(
|
||||
gl_factory: GLContextFactory,
|
||||
webrender_gl: Rc<dyn gl::Gl>,
|
||||
device: Device,
|
||||
context: Context,
|
||||
webrender_gl: Rc<dyn gleam::gl::Gl>,
|
||||
webrender_api_sender: webrender_api::RenderApiSender,
|
||||
webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
|
||||
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
|
||||
mode: ThreadMode,
|
||||
) -> (
|
||||
WebGLThreads,
|
||||
Option<Rc<WebGLMainThread>>,
|
||||
Box<dyn webxr_api::WebGLExternalImageApi>,
|
||||
Box<dyn WebrenderExternalImageApi>,
|
||||
Option<Box<dyn webrender::OutputImageHandler>>,
|
||||
) {
|
||||
api_type: GlType,
|
||||
) -> WebGLComm {
|
||||
debug!("WebGLThreads::new()");
|
||||
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
|
||||
let webrender_swap_chains = SwapChains::new();
|
||||
let webxr_swap_chains = SwapChains::new();
|
||||
|
||||
// This implementation creates a single `WebGLThread` for all the pipelines.
|
||||
let init = WebGLThreadInit {
|
||||
gl_factory,
|
||||
webrender_api_sender,
|
||||
webvr_compositor,
|
||||
external_images,
|
||||
sender: sender.clone(),
|
||||
receiver,
|
||||
webrender_swap_chains: webrender_swap_chains.clone(),
|
||||
webxr_swap_chains: webxr_swap_chains.clone(),
|
||||
connection: device.connection(),
|
||||
adapter: device.adapter(),
|
||||
api_type,
|
||||
};
|
||||
|
||||
let output_handler = if pref!(dom.webgl.dom_to_texture.enabled) {
|
||||
Some(Box::new(OutputHandler::new(
|
||||
webrender_gl.clone(),
|
||||
sender.clone(),
|
||||
)))
|
||||
Some(Box::new(OutputHandler::new(webrender_gl.clone())))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let external = WebGLExternalImages::new(webrender_gl, sender.clone());
|
||||
let external = WebGLExternalImages::new(device, context, webrender_swap_chains);
|
||||
|
||||
let webgl_thread = match mode {
|
||||
ThreadMode::MainThread(event_loop_waker) => {
|
||||
let thread = WebGLThread::run_on_current_thread(init, event_loop_waker);
|
||||
Some(thread)
|
||||
},
|
||||
ThreadMode::OffThread => {
|
||||
WebGLThread::run_on_own_thread(init);
|
||||
None
|
||||
},
|
||||
};
|
||||
WebGLThread::run_on_own_thread(init);
|
||||
|
||||
(
|
||||
WebGLThreads(sender),
|
||||
webgl_thread,
|
||||
external.sendable.clone_box(),
|
||||
Box::new(external),
|
||||
output_handler.map(|b| b as Box<_>),
|
||||
)
|
||||
}
|
||||
|
||||
/// Gets the WebGLThread handle for each script pipeline.
|
||||
pub fn pipeline(&self) -> WebGLPipeline {
|
||||
// This mode creates a single thread, so the existing WebGLChan is just cloned.
|
||||
WebGLPipeline(WebGLChan(self.0.clone()))
|
||||
}
|
||||
|
||||
/// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
|
||||
pub fn exit(&self) -> Result<(), &'static str> {
|
||||
self.0
|
||||
.send(WebGLMsg::Exit)
|
||||
.map_err(|_| "Failed to send Exit message")
|
||||
}
|
||||
}
|
||||
|
||||
/// Bridge between the webxr_api::ExternalImage callbacks and the WebGLThreads.
|
||||
struct SendableWebGLExternalImages {
|
||||
webgl_channel: WebGLSender<WebGLMsg>,
|
||||
// Used to avoid creating a new channel on each received WebRender request.
|
||||
lock_channel: (
|
||||
WebGLSender<(u32, Size2D<i32>, usize)>,
|
||||
WebGLReceiver<(u32, Size2D<i32>, usize)>,
|
||||
),
|
||||
}
|
||||
|
||||
impl SendableWebGLExternalImages {
|
||||
fn new(channel: WebGLSender<WebGLMsg>) -> Self {
|
||||
Self {
|
||||
webgl_channel: channel,
|
||||
lock_channel: webgl_channel().unwrap(),
|
||||
WebGLComm {
|
||||
webgl_threads: WebGLThreads(sender),
|
||||
webxr_swap_chains,
|
||||
image_handler: Box::new(external),
|
||||
output_handler: output_handler.map(|b| b as Box<_>),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SendableWebGLExternalImages {
|
||||
fn lock_and_get_current_texture(&self, id: usize) -> (u32, Size2D<i32>, Option<gl::GLsync>) {
|
||||
if let Some(main_thread) = WebGLMainThread::on_current_thread() {
|
||||
// If we're on the same thread as WebGL, we can get the data directly
|
||||
let (image_id, size) = main_thread
|
||||
.thread_data
|
||||
.borrow_mut()
|
||||
.handle_lock_unsync(WebGLContextId(id as usize));
|
||||
// We don't need a GLsync object if we're running on the main thread
|
||||
// Might be better to return an option?
|
||||
(image_id, size, None)
|
||||
} else {
|
||||
// WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue.
|
||||
// The WebGLMsg::Lock message inserts a fence in the WebGL command queue.
|
||||
self.webgl_channel
|
||||
.send(WebGLMsg::Lock(
|
||||
WebGLContextId(id as usize),
|
||||
self.lock_channel.0.clone(),
|
||||
))
|
||||
.unwrap();
|
||||
let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
|
||||
(image_id, size, Some(gl_sync as gl::GLsync))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl webxr_api::WebGLExternalImageApi for SendableWebGLExternalImages {
|
||||
fn lock(&self, id: usize) -> Option<gl::GLsync> {
|
||||
let (_, _, gl_sync) = self.lock_and_get_current_texture(id);
|
||||
gl_sync
|
||||
}
|
||||
|
||||
fn unlock(&self, id: usize) {
|
||||
if let Some(main_thread) = WebGLMainThread::on_current_thread() {
|
||||
// If we're on the same thread as WebGL, we can unlock directly
|
||||
main_thread
|
||||
.thread_data
|
||||
.borrow_mut()
|
||||
.handle_unlock(WebGLContextId(id as usize))
|
||||
} else {
|
||||
self.webgl_channel
|
||||
.send(WebGLMsg::Unlock(WebGLContextId(id as usize)))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn webxr_api::WebGLExternalImageApi> {
|
||||
Box::new(Self::new(self.webgl_channel.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
|
||||
struct WebGLExternalImages {
|
||||
webrender_gl: Rc<dyn gl::Gl>,
|
||||
sendable: SendableWebGLExternalImages,
|
||||
device: Device,
|
||||
context: Context,
|
||||
swap_chains: SwapChains<WebGLContextId>,
|
||||
locked_front_buffers: FnvHashMap<WebGLContextId, SurfaceTexture>,
|
||||
}
|
||||
|
||||
impl Drop for WebGLExternalImages {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.device.destroy_context(&mut self.context);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebGLExternalImages {
|
||||
fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
|
||||
fn new(device: Device, context: Context, swap_chains: SwapChains<WebGLContextId>) -> Self {
|
||||
Self {
|
||||
webrender_gl,
|
||||
sendable: SendableWebGLExternalImages::new(channel),
|
||||
device,
|
||||
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()?;
|
||||
|
||||
debug!("... getting texture for surface {:?}", front_buffer.id());
|
||||
let size = front_buffer.size();
|
||||
let front_buffer_texture = self
|
||||
.device
|
||||
.create_surface_texture(&mut self.context, front_buffer)
|
||||
.unwrap();
|
||||
let gl_texture = front_buffer_texture.gl_texture();
|
||||
|
||||
self.locked_front_buffers.insert(id, front_buffer_texture);
|
||||
|
||||
Some((gl_texture, size))
|
||||
}
|
||||
|
||||
fn unlock_swap_chain(&mut self, id: WebGLContextId) -> Option<()> {
|
||||
let locked_front_buffer = self.locked_front_buffers.remove(&id)?;
|
||||
let locked_front_buffer = self
|
||||
.device
|
||||
.destroy_surface_texture(&mut self.context, locked_front_buffer)
|
||||
.unwrap();
|
||||
|
||||
debug!("... unlocked chain {:?}", id);
|
||||
self.swap_chains
|
||||
.get(id)?
|
||||
.recycle_surface(locked_front_buffer);
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WebrenderExternalImageApi for WebGLExternalImages {
|
||||
fn lock(&mut self, id: u64) -> (u32, Size2D<i32>) {
|
||||
let (image_id, size, gl_sync) = self.sendable.lock_and_get_current_texture(id as usize);
|
||||
// The next glWaitSync call is run on the WR thread and it's used to synchronize the two
|
||||
// flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture.
|
||||
// glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem.
|
||||
if let Some(gl_sync) = gl_sync {
|
||||
self.webrender_gl.wait_sync(gl_sync, 0, gl::TIMEOUT_IGNORED);
|
||||
}
|
||||
(image_id, size)
|
||||
let id = WebGLContextId(id);
|
||||
self.lock_swap_chain(id).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn unlock(&mut self, id: u64) {
|
||||
self.sendable.unlock(id as usize);
|
||||
let id = WebGLContextId(id);
|
||||
self.unlock_swap_chain(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait.
|
||||
type OutputHandlerData = Option<(u32, Size2D<i32>)>;
|
||||
struct OutputHandler {
|
||||
webrender_gl: Rc<dyn gl::Gl>,
|
||||
webgl_channel: WebGLSender<WebGLMsg>,
|
||||
// Used to avoid creating a new channel on each received WebRender request.
|
||||
lock_channel: (
|
||||
WebGLSender<OutputHandlerData>,
|
||||
WebGLReceiver<OutputHandlerData>,
|
||||
),
|
||||
sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
|
||||
webrender_gl: Rc<dyn gleam::gl::Gl>,
|
||||
sync_objects: FnvHashMap<webrender_api::PipelineId, gleam::gl::GLsync>,
|
||||
}
|
||||
|
||||
impl OutputHandler {
|
||||
fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
|
||||
fn new(webrender_gl: Rc<dyn gleam::gl::Gl>) -> Self {
|
||||
OutputHandler {
|
||||
webrender_gl,
|
||||
webgl_channel: channel,
|
||||
lock_channel: webgl_channel().unwrap(),
|
||||
sync_objects: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -235,28 +171,8 @@ impl webrender::OutputImageHandler for OutputHandler {
|
|||
.webrender_gl
|
||||
.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
self.sync_objects.insert(id, gl_sync);
|
||||
|
||||
let result = if let Some(main_thread) = WebGLMainThread::on_current_thread() {
|
||||
main_thread
|
||||
.thread_data
|
||||
.borrow_mut()
|
||||
.handle_dom_to_texture_lock(id, gl_sync as usize)
|
||||
} else {
|
||||
// The lock command adds a WaitSync call on the WebGL command flow.
|
||||
let command =
|
||||
DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone());
|
||||
self.webgl_channel
|
||||
.send(WebGLMsg::DOMToTextureCommand(command))
|
||||
.unwrap();
|
||||
self.lock_channel.1.recv().unwrap()
|
||||
};
|
||||
|
||||
result.map(|(tex_id, size)| {
|
||||
(
|
||||
tex_id,
|
||||
webrender_api::units::FramebufferIntSize::new(size.width, size.height),
|
||||
)
|
||||
})
|
||||
// https://github.com/servo/servo/issues/24615
|
||||
None
|
||||
}
|
||||
|
||||
fn unlock(&mut self, id: webrender_api::PipelineId) {
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
mod inprocess;
|
||||
pub use self::inprocess::{ThreadMode, WebGLThreads};
|
||||
|
||||
pub use self::inprocess::WebGLComm;
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue