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:
Patrick Walton 2019-10-15 12:57:00 -05:00 committed by Alan Jeffrey
parent 48d918dcde
commit a358bca766
52 changed files with 1929 additions and 2195 deletions

View file

@ -25,7 +25,7 @@ crossbeam-channel = "0.3"
lazy_static = "1.0"
[target.'cfg(target_os = "macos")'.dependencies]
mach = "0.2.3"
mach = "0.3"
[target.'cfg(all(target_os = "linux", not(any(target_arch = "arm", target_arch = "aarch64"))))'.dependencies]
nix = "0.14"

View file

@ -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" }

View file

@ -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,
}
}

View file

@ -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;

View 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()
}
}

View file

@ -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) {

View file

@ -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

View file

@ -27,3 +27,4 @@ servo_config = {path = "../config"}
sparkle = "0.1"
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
webvr_traits = {path = "../webvr_traits"}
webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]}

View file

@ -5,6 +5,7 @@
use euclid::default::{Rect, Size2D};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSharedMemory};
use pixels::PixelFormat;
use serde::{Deserialize, Serialize};
use sparkle::gl;
use sparkle::gl::Gl;
use std::borrow::Cow;
@ -13,6 +14,7 @@ use std::num::{NonZeroU32, NonZeroU64};
use std::ops::Deref;
use webrender_api::{DocumentId, ImageKey, PipelineId};
use webvr_traits::WebVRPoseInformation;
use webxr_api::SwapChainId as WebXRSwapChainId;
/// Helper function that creates a WebGL channel (WebGLSender, WebGLReceiver) to be used in WebGLCommands.
pub use crate::webgl_channel::webgl_channel;
@ -35,6 +37,24 @@ pub struct WebGLCommandBacktrace {
pub js_backtrace: Option<String>,
}
/// WebGL Threading API entry point that lives in the constellation.
pub struct WebGLThreads(pub WebGLSender<WebGLMsg>);
impl WebGLThreads {
/// 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")
}
}
/// WebGL Message API
#[derive(Debug, Deserialize, Serialize)]
pub enum WebGLMsg {
@ -53,21 +73,16 @@ pub enum WebGLMsg {
WebGLCommand(WebGLContextId, WebGLCommand, WebGLCommandBacktrace),
/// Runs a WebVRCommand in a specific WebGLContext.
WebVRCommand(WebGLContextId, WebVRCommand),
/// Locks a specific WebGLContext. Lock messages are used for a correct synchronization
/// with WebRender external image API.
/// WR locks a external texture when it wants to use the shared texture contents.
/// The WR client should not change the shared texture content until the Unlock call.
/// Currently OpenGL Sync Objects are used to implement the synchronization mechanism.
Lock(WebGLContextId, WebGLSender<(u32, Size2D<i32>, usize)>),
/// Unlocks a specific WebGLContext. Unlock messages are used for a correct synchronization
/// with WebRender external image API.
/// The WR unlocks a context when it finished reading the shared texture contents.
/// Unlock messages are always sent after a Lock message.
Unlock(WebGLContextId),
/// Commands used for the DOMToTexture feature.
DOMToTextureCommand(DOMToTextureCommand),
/// Creates a new opaque framebuffer for WebXR.
CreateWebXRSwapChain(
WebGLContextId,
Size2D<i32>,
WebGLSender<Option<WebXRSwapChainId>>,
),
/// Performs a buffer swap.
SwapBuffers(Vec<WebGLContextId>, WebGLSender<()>),
SwapBuffers(Vec<SwapChainId>, WebGLSender<()>),
/// Frees all resources and closes the thread.
Exit,
}
@ -85,26 +100,14 @@ pub struct WebGLCreateContextResult {
pub sender: WebGLMsgSender,
/// Information about the internal GL Context.
pub limits: GLLimits,
/// How the WebGLContext is shared with WebRender.
pub share_mode: WebGLContextShareMode,
/// The GLSL version supported by the context.
pub glsl_version: WebGLSLVersion,
/// The GL API used by the context.
pub api_type: GlType,
/// The format for creating new offscreen framebuffers for this context.
pub framebuffer_format: GLFormats,
/// The WebRender image key.
pub image_key: ImageKey,
}
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
pub enum WebGLContextShareMode {
/// Fast: a shared texture_id is used in WebRender.
SharedTexture,
/// Slow: glReadPixels is used to send pixels to WebRender each frame.
Readback,
}
/// Defines the WebGL version
#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum WebGLVersion {
@ -176,6 +179,28 @@ impl WebGLMsgSender {
self.sender.send(WebGLMsg::RemoveContext(self.ctx_id))
}
#[inline]
pub fn send_create_webxr_swap_chain(
&self,
size: Size2D<i32>,
sender: WebGLSender<Option<WebXRSwapChainId>>,
) -> WebGLSendResult {
self.sender
.send(WebGLMsg::CreateWebXRSwapChain(self.ctx_id, size, sender))
}
#[inline]
pub fn send_swap_buffers(&self, id: Option<WebGLOpaqueFramebufferId>) -> WebGLSendResult {
let swap_id = id
.map(|id| SwapChainId::Framebuffer(self.ctx_id, id))
.unwrap_or_else(|| SwapChainId::Context(self.ctx_id));
let (sender, receiver) = webgl_channel()?;
self.sender
.send(WebGLMsg::SwapBuffers(vec![swap_id], sender))?;
receiver.recv()?;
Ok(())
}
pub fn send_dom_to_texture(&self, command: DOMToTextureCommand) -> WebGLSendResult {
self.sender.send(WebGLMsg::DOMToTextureCommand(command))
}
@ -239,7 +264,7 @@ pub enum WebGLCommand {
CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32),
CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32),
CreateBuffer(WebGLSender<Option<WebGLBufferId>>),
CreateFramebuffer(WebGLSender<Option<WebGLFramebufferId>>),
CreateFramebuffer(WebGLSender<Option<WebGLTransparentFramebufferId>>),
CreateRenderbuffer(WebGLSender<Option<WebGLRenderbufferId>>),
CreateTexture(WebGLSender<Option<WebGLTextureId>>),
CreateProgram(WebGLSender<Option<WebGLProgramId>>),
@ -517,7 +542,7 @@ macro_rules! define_resource_id {
}
define_resource_id!(WebGLBufferId, u32);
define_resource_id!(WebGLFramebufferId, u32);
define_resource_id!(WebGLTransparentFramebufferId, u32);
define_resource_id!(WebGLRenderbufferId, u32);
define_resource_id!(WebGLTextureId, u32);
define_resource_id!(WebGLProgramId, u32);
@ -530,7 +555,22 @@ define_resource_id!(WebGLVertexArrayId, u32);
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct WebGLContextId(pub usize);
pub struct WebGLContextId(pub u64);
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum SwapChainId {
Context(WebGLContextId),
Framebuffer(WebGLContextId, WebGLOpaqueFramebufferId),
}
impl SwapChainId {
pub fn context_id(&self) -> WebGLContextId {
match *self {
SwapChainId::Context(id) => id,
SwapChainId::Framebuffer(id, _) => id,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum WebGLError {
@ -542,6 +582,18 @@ pub enum WebGLError {
ContextLost,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum WebGLOpaqueFramebufferId {
// At the moment the only source of opaque framebuffers is webxr
WebXR(#[ignore_malloc_size_of = "ids don't malloc"] WebXRSwapChainId),
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum WebGLFramebufferId {
Transparent(WebGLTransparentFramebufferId),
Opaque(WebGLOpaqueFramebufferId),
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum WebGLFramebufferBindingRequest {
Explicit(WebGLFramebufferId),
@ -915,9 +967,3 @@ pub struct GLLimits {
pub max_vertex_uniform_vectors: u32,
pub max_client_wait_timeout_webgl: std::time::Duration,
}
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct GLFormats {
pub texture_format: u32,
pub texture_type: u32,
}

View file

@ -360,7 +360,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
}
pub fn deinit(self) {
self.window.prepare_for_composite();
self.window.make_gl_context_current();
self.webrender.deinit();
}
@ -1263,7 +1263,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
) -> Result<Option<Image>, UnableToComposite> {
let size = self.embedder_coordinates.framebuffer.to_u32();
self.window.prepare_for_composite();
self.window.make_gl_context_current();
self.webrender.update();
let wait_for_stable_image = match target {

View file

@ -144,9 +144,8 @@ pub enum AnimationState {
pub trait WindowMethods {
/// Presents the window to the screen (perhaps by page flipping).
fn present(&self);
/// Requests that the window system prepare a composite. Typically this will involve making
/// some type of platform-specific graphics context current.
fn prepare_for_composite(&self);
/// Make the OpenGL context current.
fn make_gl_context_current(&self);
/// Return the GL function pointer trait.
#[cfg(feature = "gl")]
fn gl(&self) -> Rc<dyn gl::Gl>;

View file

@ -13,7 +13,6 @@ path = "lib.rs"
[features]
canvas2d-azure = ["canvas/canvas2d-azure"]
canvas2d-raqote = ["canvas/canvas2d-raqote"]
no_wgl = ["canvas/no_wgl"]
[dependencies]
background_hang_monitor = { path = "../background_hang_monitor"}

View file

@ -104,9 +104,8 @@ use background_hang_monitor::HangMonitorRegister;
use backtrace::Backtrace;
use bluetooth_traits::BluetoothRequest;
use canvas::canvas_paint_thread::CanvasPaintThread;
use canvas::webgl_thread::WebGLThreads;
use canvas_traits::canvas::CanvasId;
use canvas_traits::canvas::CanvasMsg;
use canvas_traits::canvas::{CanvasId, CanvasMsg};
use canvas_traits::webgl::WebGLThreads;
use compositing::compositor_thread::CompositorProxy;
use compositing::compositor_thread::Msg as ToCompositorMsg;
use compositing::SendableFrameTree;

View file

@ -47,11 +47,10 @@ use canvas_traits::canvas::{
use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
use canvas_traits::webgl::WebGLVertexArrayId;
use canvas_traits::webgl::{ActiveAttribInfo, ActiveUniformInfo, GlType, TexDataType, TexFormat};
use canvas_traits::webgl::{GLFormats, GLLimits, WebGLQueryId, WebGLSamplerId};
use canvas_traits::webgl::{
WebGLBufferId, WebGLChan, WebGLContextId, WebGLContextShareMode, WebGLError,
};
use canvas_traits::webgl::{GLLimits, WebGLQueryId, WebGLSamplerId};
use canvas_traits::webgl::{WebGLBufferId, WebGLChan, WebGLContextId, WebGLError};
use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsgSender, WebGLPipeline, WebGLProgramId};
use canvas_traits::webgl::{WebGLOpaqueFramebufferId, WebGLTransparentFramebufferId};
use canvas_traits::webgl::{WebGLReceiver, WebGLRenderbufferId, WebGLSLVersion, WebGLSender};
use canvas_traits::webgl::{WebGLShaderId, WebGLSyncId, WebGLTextureId, WebGLVersion};
use content_security_policy::CspList;
@ -147,6 +146,7 @@ use time::{Duration, Timespec};
use uuid::Uuid;
use webrender_api::{DocumentId, ImageKey, RenderApiSender};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
use webxr_api::SwapChainId as WebXRSwapChainId;
/// A trait to allow tracing (only) DOM objects.
pub unsafe trait JSTraceable {
@ -451,7 +451,7 @@ unsafe_no_jsmanaged_fields!(StorageType);
unsafe_no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle);
unsafe_no_jsmanaged_fields!(LineCapStyle, LineJoinStyle, CompositionOrBlending);
unsafe_no_jsmanaged_fields!(RepetitionStyle);
unsafe_no_jsmanaged_fields!(WebGLError, GLFormats, GLLimits, GlType);
unsafe_no_jsmanaged_fields!(WebGLError, GLLimits, GlType);
unsafe_no_jsmanaged_fields!(TimeProfilerChan);
unsafe_no_jsmanaged_fields!(MemProfilerChan);
unsafe_no_jsmanaged_fields!(PseudoElement);
@ -486,8 +486,9 @@ unsafe_no_jsmanaged_fields!(DocumentId);
unsafe_no_jsmanaged_fields!(ImageKey);
unsafe_no_jsmanaged_fields!(WebGLBufferId);
unsafe_no_jsmanaged_fields!(WebGLChan);
unsafe_no_jsmanaged_fields!(WebGLContextShareMode);
unsafe_no_jsmanaged_fields!(WebGLFramebufferId);
unsafe_no_jsmanaged_fields!(WebGLOpaqueFramebufferId);
unsafe_no_jsmanaged_fields!(WebGLTransparentFramebufferId);
unsafe_no_jsmanaged_fields!(WebGLMsgSender);
unsafe_no_jsmanaged_fields!(WebGLPipeline);
unsafe_no_jsmanaged_fields!(WebGLProgramId);
@ -500,6 +501,7 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId);
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
unsafe_no_jsmanaged_fields!(WebGLVersion);
unsafe_no_jsmanaged_fields!(WebGLSLVersion);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand);
unsafe_no_jsmanaged_fields!(

View file

@ -98,6 +98,7 @@ use crate::dom::treewalker::TreeWalker;
use crate::dom::uievent::UIEvent;
use crate::dom::virtualmethods::vtable_for;
use crate::dom::webglcontextevent::WebGLContextEvent;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::wheelevent::WheelEvent;
use crate::dom::window::{ReflowReason, Window};
use crate::dom::windowproxy::WindowProxy;
@ -109,7 +110,7 @@ use crate::stylesheet_set::StylesheetSetRef;
use crate::task::TaskBox;
use crate::task_source::{TaskSource, TaskSourceName};
use crate::timers::OneshotTimerCallback;
use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg};
use canvas_traits::webgl::{self, SwapChainId, WebGLContextId, WebGLMsg};
use content_security_policy::{self as csp, CspList};
use cookie::Cookie;
use devtools_traits::ScriptToDevtoolsControlMsg;
@ -399,7 +400,7 @@ pub struct Document {
/// hosting the media controls UI.
media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
/// List of all WebGL context IDs that need flushing.
dirty_webgl_contexts: DomRefCell<HashSet<WebGLContextId>>,
dirty_webgl_contexts: DomRefCell<HashMap<WebGLContextId, Dom<WebGLRenderingContext>>>,
/// https://html.spec.whatwg.org/multipage/#concept-document-csp-list
#[ignore_malloc_size_of = "Defined in rust-content-security-policy"]
csp_list: DomRefCell<Option<CspList>>,
@ -2486,15 +2487,26 @@ impl Document {
}
}
pub fn add_dirty_canvas(&self, context_id: WebGLContextId) {
self.dirty_webgl_contexts.borrow_mut().insert(context_id);
pub fn add_dirty_canvas(&self, context: &WebGLRenderingContext) {
self.dirty_webgl_contexts
.borrow_mut()
.entry(context.context_id())
.or_insert_with(|| Dom::from_ref(context));
}
pub fn flush_dirty_canvases(&self) {
let dirty_context_ids: Vec<_> = self.dirty_webgl_contexts.borrow_mut().drain().collect();
let dirty_context_ids: Vec<_> = self
.dirty_webgl_contexts
.borrow_mut()
.drain()
.filter(|(_, context)| context.onscreen())
.map(|(id, _)| SwapChainId::Context(id))
.collect();
if dirty_context_ids.is_empty() {
return;
}
let (sender, receiver) = webgl::webgl_channel().unwrap();
self.window
.webgl_chan()
@ -2797,7 +2809,7 @@ impl Document {
shadow_roots: DomRefCell::new(HashSet::new()),
shadow_roots_styles_changed: Cell::new(false),
media_controls: DomRefCell::new(HashMap::new()),
dirty_webgl_contexts: DomRefCell::new(HashSet::new()),
dirty_webgl_contexts: DomRefCell::new(HashMap::new()),
csp_list: DomRefCell::new(None),
}
}

View file

@ -1426,6 +1426,6 @@ impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingConte
#[allow(unsafe_code)]
unsafe fn canvas_data_source(&self) -> HTMLCanvasDataSource {
let this = &*self.unsafe_get();
HTMLCanvasDataSource::WebGL((*this.base.to_layout().unsafe_get()).layout_handle())
(*this.base.to_layout().unsafe_get()).layout_handle()
}
}

View file

@ -8,15 +8,20 @@ use crate::dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::webglobject::WebGLObject;
use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::xrsession::XRSession;
use canvas_traits::webgl::{webgl_channel, WebGLError, WebGLResult};
use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId};
use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest};
use canvas_traits::webgl::{WebGLFramebufferId, WebGLOpaqueFramebufferId};
use dom_struct::dom_struct;
use euclid::Size2D;
use std::cell::Cell;
use webxr_api::SwapChainId as WebXRSwapChainId;
use webxr_api::Viewport;
pub enum CompleteForRendering {
Complete,
@ -92,6 +97,9 @@ pub struct WebGLFramebuffer {
stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
is_initialized: Cell<bool>,
// Framebuffers for XR keep a reference to the XR session.
// https://github.com/immersive-web/webxr/issues/856
xr_session: MutNullableDom<XRSession>,
}
impl WebGLFramebuffer {
@ -108,16 +116,37 @@ impl WebGLFramebuffer {
stencil: DomRefCell::new(None),
depthstencil: DomRefCell::new(None),
is_initialized: Cell::new(false),
xr_session: Default::default(),
}
}
pub fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateFramebuffer(sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLFramebuffer::new(context, id))
let id = receiver.recv().unwrap()?;
let framebuffer = WebGLFramebuffer::new(context, WebGLFramebufferId::Transparent(id));
Some(framebuffer)
}
// TODO: depth, stencil and alpha
// https://github.com/servo/servo/issues/24498
pub fn maybe_new_webxr(
session: &XRSession,
context: &WebGLRenderingContext,
size: Size2D<i32, Viewport>,
) -> Option<(WebXRSwapChainId, DomRoot<Self>)> {
let (sender, receiver) = webgl_channel().unwrap();
let _ = context
.webgl_sender()
.send_create_webxr_swap_chain(size.to_untyped(), sender);
let swap_chain_id = receiver.recv().unwrap()?;
let framebuffer_id =
WebGLFramebufferId::Opaque(WebGLOpaqueFramebufferId::WebXR(swap_chain_id));
let framebuffer = WebGLFramebuffer::new(context, framebuffer_id);
framebuffer.size.set(Some((size.width, size.height)));
framebuffer.status.set(constants::FRAMEBUFFER_COMPLETE);
framebuffer.xr_session.set(Some(session));
Some((swap_chain_id, framebuffer))
}
pub fn new(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> DomRoot<Self> {
@ -134,11 +163,25 @@ impl WebGLFramebuffer {
self.id
}
fn is_in_xr_session(&self) -> bool {
self.xr_session.get().is_some()
}
pub fn validate_transparent(&self) -> WebGLResult<()> {
if self.is_in_xr_session() {
Err(WebGLError::InvalidOperation)
} else {
Ok(())
}
}
pub fn bind(&self, target: u32) {
// Update the framebuffer status on binding. It may have
// changed if its attachments were resized or deleted while
// we've been unbound.
self.update_status();
if !self.is_in_xr_session() {
// Update the framebuffer status on binding. It may have
// changed if its attachments were resized or deleted while
// we've been unbound.
self.update_status();
}
self.target.set(Some(target));
self.upcast::<WebGLObject>()
@ -267,7 +310,17 @@ impl WebGLFramebuffer {
}
pub fn check_status(&self) -> u32 {
return self.status.get();
// For opaque framebuffers, check to see if the XR session is currently processing an rAF
// https://immersive-web.github.io/webxr/#opaque-framebuffer
if let Some(xr_session) = self.xr_session.get() {
if xr_session.is_outside_raf() {
constants::FRAMEBUFFER_UNSUPPORTED
} else {
constants::FRAMEBUFFER_COMPLETE
}
} else {
self.status.get()
}
}
pub fn check_status_for_rendering(&self) -> CompleteForRendering {
@ -276,6 +329,12 @@ impl WebGLFramebuffer {
return CompleteForRendering::Incomplete;
}
// XR framebuffers are complete inside an rAF
// https://github.com/immersive-web/webxr/issues/854
if self.xr_session.get().is_some() {
return CompleteForRendering::Complete;
}
if self.color.borrow().is_none() {
return CompleteForRendering::MissingColorAttachment;
}
@ -309,6 +368,10 @@ impl WebGLFramebuffer {
}
pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
// Opaque framebuffers cannot have their attachments changed
// https://immersive-web.github.io/webxr/#opaque-framebuffer
self.validate_transparent()?;
let binding = self
.attachment_binding(attachment)
.ok_or(WebGLError::InvalidEnum)?;
@ -337,7 +400,7 @@ impl WebGLFramebuffer {
));
if rb.is_none() {
self.detach_binding(binding, attachment);
self.detach_binding(binding, attachment)?;
}
self.update_status();
@ -349,14 +412,19 @@ impl WebGLFramebuffer {
&self,
binding: &DomRefCell<Option<WebGLFramebufferAttachment>>,
attachment: u32,
) {
) -> WebGLResult<()> {
// Opaque framebuffers cannot have their attachments changed
// https://immersive-web.github.io/webxr/#opaque-framebuffer
self.validate_transparent()?;
if let Some(att) = &*binding.borrow() {
att.detach();
}
*binding.borrow_mut() = None;
if INTERESTING_ATTACHMENT_POINTS.contains(&attachment) {
self.reattach_depth_stencil();
self.reattach_depth_stencil()?;
}
Ok(())
}
fn attachment_binding(
@ -372,7 +440,11 @@ impl WebGLFramebuffer {
}
}
fn reattach_depth_stencil(&self) {
fn reattach_depth_stencil(&self) -> WebGLResult<()> {
// Opaque framebuffers cannot have their attachments changed
// https://immersive-web.github.io/webxr/#opaque-framebuffer
self.validate_transparent()?;
let reattach = |attachment: &WebGLFramebufferAttachment, attachment_point| {
let context = self.upcast::<WebGLObject>().context();
match *attachment {
@ -411,6 +483,7 @@ impl WebGLFramebuffer {
if let Some(ref depth_stencil) = *self.depthstencil.borrow() {
reattach(depth_stencil, constants::DEPTH_STENCIL_ATTACHMENT);
}
Ok(())
}
pub fn attachment(&self, attachment: u32) -> Option<WebGLFramebufferAttachmentRoot> {
@ -428,6 +501,10 @@ impl WebGLFramebuffer {
texture: Option<&WebGLTexture>,
level: i32,
) -> WebGLResult<()> {
// Opaque framebuffers cannot have their attachments changed
// https://immersive-web.github.io/webxr/#opaque-framebuffer
self.validate_transparent()?;
let binding = self
.attachment_binding(attachment)
.ok_or(WebGLError::InvalidEnum)?;
@ -498,7 +575,7 @@ impl WebGLFramebuffer {
));
if texture.is_none() {
self.detach_binding(binding, attachment);
self.detach_binding(binding, attachment)?;
}
self.update_status();
@ -563,7 +640,11 @@ impl WebGLFramebuffer {
}
}
pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) -> WebGLResult<()> {
// Opaque framebuffers cannot have their attachments changed
// https://immersive-web.github.io/webxr/#opaque-framebuffer
self.validate_transparent()?;
let mut depth_or_stencil_updated = false;
self.with_matching_renderbuffers(rb, |att, name| {
depth_or_stencil_updated |= INTERESTING_ATTACHMENT_POINTS.contains(&name);
@ -575,11 +656,16 @@ impl WebGLFramebuffer {
});
if depth_or_stencil_updated {
self.reattach_depth_stencil();
self.reattach_depth_stencil()?;
}
Ok(())
}
pub fn detach_texture(&self, texture: &WebGLTexture) {
pub fn detach_texture(&self, texture: &WebGLTexture) -> WebGLResult<()> {
// Opaque framebuffers cannot have their attachments changed
// https://immersive-web.github.io/webxr/#opaque-framebuffer
self.validate_transparent()?;
let mut depth_or_stencil_updated = false;
self.with_matching_textures(texture, |att, name| {
depth_or_stencil_updated |= INTERESTING_ATTACHMENT_POINTS.contains(&name);
@ -591,8 +677,9 @@ impl WebGLFramebuffer {
});
if depth_or_stencil_updated {
self.reattach_depth_stencil();
self.reattach_depth_stencil()?;
}
Ok(())
}
pub fn invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer) {
@ -615,7 +702,7 @@ impl WebGLFramebuffer {
impl Drop for WebGLFramebuffer {
fn drop(&mut self) {
self.delete(true);
let _ = self.delete(true);
}
}

View file

@ -392,10 +392,12 @@ impl WebGLProgram {
sender,
));
let location = receiver.recv().unwrap();
let context_id = self.upcast::<WebGLObject>().context().context_id();
Ok(Some(WebGLUniformLocation::new(
self.global().as_window(),
location,
context_id,
self.id,
self.link_generation.get(),
size,

View file

@ -106,7 +106,7 @@ impl WebGLRenderbuffer {
let currently_bound_framebuffer =
self.upcast::<WebGLObject>().context().bound_framebuffer();
if let Some(fb) = currently_bound_framebuffer {
fb.detach_renderbuffer(self);
let _ = fb.detach_renderbuffer(self);
}
let context = self.upcast::<WebGLObject>().context();

View file

@ -54,11 +54,11 @@ use crate::script_runtime::JSContext as SafeJSContext;
use backtrace::Backtrace;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLFormats, GLLimits,
GlType, Parameter, TexDataType, TexFormat, TexParameter, WebGLChan, WebGLCommand,
WebGLCommandBacktrace, WebGLContextId, WebGLContextShareMode, WebGLError,
WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId, WebGLResult,
WebGLSLVersion, WebGLSendResult, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment,
webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLLimits, GlType,
Parameter, TexDataType, TexFormat, TexParameter, WebGLChan, WebGLCommand,
WebGLCommandBacktrace, WebGLContextId, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg,
WebGLMsgSender, WebGLOpaqueFramebufferId, WebGLProgramId, WebGLResult, WebGLSLVersion,
WebGLSendResult, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment,
};
use dom_struct::dom_struct;
use embedder_traits::EventLoopWaker;
@ -81,6 +81,7 @@ use std::cell::Cell;
use std::cmp;
use std::ptr::{self, NonNull};
use std::rc::Rc;
use webxr_api::SwapChainId as WebXRSwapChainId;
// From the GLES 2.0.25 spec, page 85:
//
@ -130,10 +131,9 @@ pub struct WebGLRenderingContext {
webgl_sender: WebGLMessageSender,
#[ignore_malloc_size_of = "Defined in webrender"]
webrender_image: webrender_api::ImageKey,
share_mode: WebGLContextShareMode,
webgl_version: WebGLVersion,
glsl_version: WebGLSLVersion,
#[ignore_malloc_size_of = "Defined in offscreen_gl_context"]
#[ignore_malloc_size_of = "Defined in surfman"]
limits: GLLimits,
canvas: Dom<HTMLCanvasElement>,
#[ignore_malloc_size_of = "Defined in canvas_traits"]
@ -160,7 +160,6 @@ pub struct WebGLRenderingContext {
current_vao: MutNullableDom<WebGLVertexArrayObjectOES>,
textures: Textures,
api_type: GlType,
framebuffer_format: GLFormats,
}
impl WebGLRenderingContext {
@ -195,7 +194,6 @@ impl WebGLRenderingContext {
window.get_event_loop_waker(),
),
webrender_image: ctx_data.image_key,
share_mode: ctx_data.share_mode,
webgl_version,
glsl_version: ctx_data.glsl_version,
limits: ctx_data.limits,
@ -220,7 +218,6 @@ impl WebGLRenderingContext {
current_vao: Default::default(),
textures: Textures::new(max_combined_texture_image_units),
api_type: ctx_data.api_type,
framebuffer_format: ctx_data.framebuffer_format,
}
})
}
@ -292,7 +289,7 @@ impl WebGLRenderingContext {
self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
// Bound texture must not change when the canvas is resized.
// Right now offscreen_gl_context generates a new FBO and the bound texture is changed
// Right now surfman generates a new FBO and the bound texture is changed
// in order to create a new render to texture attachment.
// Send a command to re-bind the TEXTURE_2D, if any.
if let Some(texture) = self
@ -308,7 +305,7 @@ impl WebGLRenderingContext {
}
// Bound framebuffer must not change when the canvas is resized.
// Right now offscreen_gl_context generates a new FBO on resize.
// Right now surfman generates a new FBO on resize.
// Send a command to re-bind the framebuffer, if any.
if let Some(fbo) = self.bound_framebuffer.get() {
let id = WebGLFramebufferBindingRequest::Explicit(fbo.id());
@ -324,6 +321,10 @@ impl WebGLRenderingContext {
self.webgl_sender.context_id()
}
pub fn onscreen(&self) -> bool {
self.canvas.upcast::<Node>().is_connected()
}
#[inline]
pub fn send_command(&self, command: WebGLCommand) {
self.webgl_sender
@ -337,6 +338,10 @@ impl WebGLRenderingContext {
.send(command, capture_webgl_backtrace(self));
}
pub fn swap_buffers(&self, id: Option<WebGLOpaqueFramebufferId>) {
let _ = self.webgl_sender.send_swap_buffers(id);
}
#[inline]
pub fn send_vr_command(&self, command: WebVRCommand) {
self.webgl_sender.send_vr(command).unwrap();
@ -462,7 +467,7 @@ impl WebGLRenderingContext {
.dirty(NodeDamage::OtherNodeDamage);
let document = document_from_node(&*self.canvas);
document.add_dirty_canvas(self.context_id());
document.add_dirty_canvas(self);
}
fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
@ -810,8 +815,9 @@ impl WebGLRenderingContext {
receiver.recv().unwrap()
}
pub fn layout_handle(&self) -> webrender_api::ImageKey {
self.webrender_image
pub(crate) fn layout_handle(&self) -> HTMLCanvasDataSource {
let image_key = self.webrender_image;
HTMLCanvasDataSource::WebGL(image_key)
}
// https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
@ -1086,17 +1092,9 @@ impl WebGLRenderingContext {
self.bound_framebuffer.get()
}
pub fn bound_renderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
self.bound_renderbuffer.get()
}
pub fn extension_manager(&self) -> &WebGLExtensions {
&self.extension_manager
}
pub fn formats(&self) -> &GLFormats {
&self.framebuffer_format
}
}
#[cfg(not(feature = "webgl_backtrace"))]
@ -2225,6 +2223,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
if let Some(framebuffer) = framebuffer {
// https://immersive-web.github.io/webxr/#opaque-framebuffer
// Can opaque framebuffers be deleted?
// https://github.com/immersive-web/webxr/issues/855
handle_potential_webgl_error!(self, framebuffer.validate_transparent(), return);
handle_potential_webgl_error!(self, self.validate_ownership(framebuffer), return);
handle_object_deletion!(
self,
@ -2384,7 +2386,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
pname: u32,
) -> JSVal {
// Check if currently bound framebuffer is non-zero as per spec.
if self.bound_framebuffer.get().is_none() {
if let Some(fb) = self.bound_framebuffer.get() {
// Opaque framebuffers cannot have their attachments inspected
// https://immersive-web.github.io/webxr/#opaque-framebuffer
handle_potential_webgl_error!(self, fb.validate_transparent(), return NullValue());
} else {
self.webgl_error(InvalidOperation);
return NullValue();
}
@ -2487,6 +2493,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
fn GetRenderbufferParameter(&self, _cx: SafeJSContext, target: u32, pname: u32) -> JSVal {
// We do not check to see if the renderbuffer came from an opaque framebuffer
// https://github.com/immersive-web/webxr/issues/862
let target_matches = target == constants::RENDERBUFFER;
let pname_matches = match pname {
@ -3477,6 +3485,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if program.is_deleted() ||
!program.is_linked() ||
self.context_id() != location.context_id() ||
program.id() != location.program_id() ||
program.link_generation() != location.link_generation()
{
@ -4153,7 +4162,7 @@ pub trait LayoutCanvasWebGLRenderingContextHelpers {
impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGLRenderingContext> {
#[allow(unsafe_code)]
unsafe fn canvas_data_source(&self) -> HTMLCanvasDataSource {
HTMLCanvasDataSource::WebGL((*self.unsafe_get()).layout_handle())
(*self.unsafe_get()).layout_handle()
}
}
@ -4347,7 +4356,7 @@ impl TexPixels {
}
#[derive(JSTraceable)]
pub(crate) struct WebGLCommandSender {
pub struct WebGLCommandSender {
sender: WebGLChan,
waker: Option<Box<dyn EventLoopWaker>>,
}
@ -4410,6 +4419,18 @@ impl WebGLMessageSender {
self.wake_after_send(|| self.sender.send_vr(command))
}
pub fn send_swap_buffers(&self, id: Option<WebGLOpaqueFramebufferId>) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_swap_buffers(id))
}
pub fn send_create_webxr_swap_chain(
&self,
size: Size2D<i32>,
sender: WebGLSender<Option<WebXRSwapChainId>>,
) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_create_webxr_swap_chain(size, sender))
}
pub fn send_resize(
&self,
size: Size2D<u32>,

View file

@ -210,7 +210,7 @@ impl WebGLTexture {
let currently_bound_framebuffer =
self.upcast::<WebGLObject>().context().bound_framebuffer();
if let Some(fb) = currently_bound_framebuffer {
fb.detach_texture(self);
let _ = fb.detach_texture(self);
}
let cmd = WebGLCommand::DeleteTexture(self.id);

View file

@ -7,6 +7,7 @@ use crate::dom::bindings::codegen::Bindings::WebGLUniformLocationBinding;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::window::Window;
use canvas_traits::webgl::WebGLContextId;
use canvas_traits::webgl::WebGLProgramId;
use dom_struct::dom_struct;
@ -14,6 +15,7 @@ use dom_struct::dom_struct;
pub struct WebGLUniformLocation {
reflector_: Reflector,
id: i32,
context_id: WebGLContextId,
program_id: WebGLProgramId,
link_generation: u64,
size: Option<i32>,
@ -23,6 +25,7 @@ pub struct WebGLUniformLocation {
impl WebGLUniformLocation {
fn new_inherited(
id: i32,
context_id: WebGLContextId,
program_id: WebGLProgramId,
link_generation: u64,
size: Option<i32>,
@ -31,6 +34,7 @@ impl WebGLUniformLocation {
Self {
reflector_: Reflector::new(),
id,
context_id,
program_id,
link_generation,
size,
@ -41,6 +45,7 @@ impl WebGLUniformLocation {
pub fn new(
window: &Window,
id: i32,
context_id: WebGLContextId,
program_id: WebGLProgramId,
link_generation: u64,
size: Option<i32>,
@ -49,6 +54,7 @@ impl WebGLUniformLocation {
reflect_dom_object(
Box::new(Self::new_inherited(
id,
context_id,
program_id,
link_generation,
size,
@ -67,6 +73,10 @@ impl WebGLUniformLocation {
self.program_id
}
pub fn context_id(&self) -> WebGLContextId {
self.context_id
}
pub fn link_generation(&self) -> u64 {
self.link_generation
}

View file

@ -6,7 +6,6 @@ use crate::compartments::InCompartment;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
@ -30,7 +29,6 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::node::Node;
use crate::dom::node::NodeDamage;
use crate::dom::promise::Promise;
use crate::dom::webglframebuffer::WebGLFramebufferAttachmentRoot;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrinputsourcearray::XRInputSourceArray;
use crate::dom::xrinputsourceevent::XRInputSourceEvent;
@ -41,7 +39,6 @@ use crate::dom::xrspace::XRSpace;
use crate::dom::xrwebgllayer::XRWebGLLayer;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
use euclid::default::Size2D;
use euclid::RigidTransform3D;
use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER;
@ -77,6 +74,9 @@ pub struct XRSession {
end_promises: DomRefCell<Vec<Rc<Promise>>>,
/// https://immersive-web.github.io/webxr/#ended
ended: Cell<bool>,
/// Opaque framebuffers need to know the session is "outside of a requestAnimationFrame"
/// https://immersive-web.github.io/webxr/#opaque-framebuffer
outside_raf: Cell<bool>,
}
impl XRSession {
@ -102,6 +102,7 @@ impl XRSession {
input_sources: Dom::from_ref(input_sources),
end_promises: DomRefCell::new(vec![]),
ended: Cell::new(false),
outside_raf: Cell::new(true),
}
}
@ -169,6 +170,10 @@ impl XRSession {
self.session.borrow_mut().request_animation_frame(sender);
}
pub fn is_outside_raf(&self) -> bool {
self.outside_raf.get()
}
fn attach_event_handler(&self) {
let this = Trusted::new(self);
let global = self.global();
@ -276,6 +281,8 @@ impl XRSession {
/// https://immersive-web.github.io/webxr/#xr-animation-frame
fn raf_callback(&self, (time, mut frame): (f64, Frame)) {
debug!("WebXR RAF callback");
// Step 1
if let Some(pending) = self.pending_render_state.take() {
// https://immersive-web.github.io/webxr/#apply-the-pending-render-state
@ -285,19 +292,8 @@ impl XRSession {
// Step 6-7: XXXManishearth handle inlineVerticalFieldOfView
// XXXManishearth handle inline sessions and composition disabled flag
if let Some(layer) = pending.GetBaseLayer() {
let attachment = layer.framebuffer().attachment(constants::COLOR_ATTACHMENT0);
if let Some(WebGLFramebufferAttachmentRoot::Texture(texture)) = attachment {
let context = layer.Context().context_id().0;
let texture_id = texture.id().get();
if let Some((width, height)) = layer.framebuffer().size() {
let size = Size2D::new(width, height);
self.session
.borrow_mut()
.set_texture(context, texture_id, size);
}
}
}
let swap_chain_id = pending.GetBaseLayer().map(|layer| layer.swap_chain_id());
self.session.borrow_mut().set_swap_chain(swap_chain_id);
}
for event in frame.events.drain(..) {
@ -321,13 +317,16 @@ impl XRSession {
frame.set_animation_frame(true);
// Step 8
self.outside_raf.set(false);
for (_, callback) in callbacks.drain(..) {
if let Some(callback) = callback {
let _ = callback.Call__(Finite::wrap(time), &frame, ExceptionHandling::Report);
}
}
self.outside_raf.set(true);
frame.set_active(false);
base_layer.swap_buffers();
self.session.borrow_mut().render_animation_frame();
self.request_new_xr_frame();

View file

@ -2,27 +2,26 @@
* 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::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethods};
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerInit;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector, DomObject};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::webgl_validations::types::TexImageTarget;
use crate::dom::webglframebuffer::WebGLFramebuffer;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::window::Window;
use crate::dom::xrsession::XRSession;
use crate::dom::xrview::XRView;
use crate::dom::xrviewport::XRViewport;
use canvas_traits::webgl::WebGLFramebufferId;
use dom_struct::dom_struct;
use js::rust::CustomAutoRooter;
use std::convert::TryInto;
use webxr_api::SwapChainId as WebXRSwapChainId;
use webxr_api::Views;
#[dom_struct]
@ -32,6 +31,8 @@ pub struct XRWebGLLayer {
depth: bool,
stencil: bool,
alpha: bool,
#[ignore_malloc_size_of = "ids don't malloc"]
swap_chain_id: WebXRSwapChainId,
context: Dom<WebGLRenderingContext>,
session: Dom<XRSession>,
framebuffer: Dom<WebGLFramebuffer>,
@ -39,6 +40,7 @@ pub struct XRWebGLLayer {
impl XRWebGLLayer {
pub fn new_inherited(
swap_chain_id: WebXRSwapChainId,
session: &XRSession,
context: &WebGLRenderingContext,
init: &XRWebGLLayerInit,
@ -50,6 +52,7 @@ impl XRWebGLLayer {
depth: init.depth,
stencil: init.stencil,
alpha: init.alpha,
swap_chain_id,
context: Dom::from_ref(context),
session: Dom::from_ref(session),
framebuffer: Dom::from_ref(framebuffer),
@ -58,6 +61,7 @@ impl XRWebGLLayer {
pub fn new(
global: &GlobalScope,
swap_chain_id: WebXRSwapChainId,
session: &XRSession,
context: &WebGLRenderingContext,
init: &XRWebGLLayerInit,
@ -65,6 +69,7 @@ impl XRWebGLLayer {
) -> DomRoot<XRWebGLLayer> {
reflect_dom_object(
Box::new(XRWebGLLayer::new_inherited(
swap_chain_id,
session,
context,
init,
@ -89,108 +94,16 @@ impl XRWebGLLayer {
// XXXManishearth step 3: throw error if context is lost
// XXXManishearth step 4: check XR compat flag for immersive sessions
let cx = global.get_cx();
let old_fbo = context.bound_framebuffer();
let old_rbo = context.bound_renderbuffer();
let old_texture = context
.textures()
.active_texture_for_image_target(TexImageTarget::Texture2D);
// Step 9.2. "Initialize layers framebuffer to a new opaque framebuffer created with
// context and layerInits depth, stencil, and alpha values."
let framebuffer = context.CreateFramebuffer().ok_or(Error::Operation)?;
// Step 9.2. "Initialize layers framebuffer to a new opaque framebuffer created with context."
let size = session.with_session(|session| session.recommended_framebuffer_resolution());
let (swap_chain_id, framebuffer) =
WebGLFramebuffer::maybe_new_webxr(session, context, size).ok_or(Error::Operation)?;
// Step 9.3. "Allocate and initialize resources compatible with sessions XR device,
// including GPU accessible memory buffers, as required to support the compositing of layer."
// Create a new texture with size given by the session's recommended resolution
let texture = context.CreateTexture().ok_or(Error::Operation)?;
let render_buffer = context.CreateRenderbuffer().ok_or(Error::Operation)?;
let resolution = session.with_session(|s| s.recommended_framebuffer_resolution());
let mut pixels = CustomAutoRooter::new(None);
let mut clear_bits = constants::COLOR_BUFFER_BIT;
let formats = context.formats();
context.BindTexture(constants::TEXTURE_2D, Some(&texture));
let sc = context.TexImage2D(
constants::TEXTURE_2D,
0,
formats.texture_format,
resolution.width,
resolution.height,
0,
formats.texture_format,
formats.texture_type,
pixels.root(*cx),
);
// Bind the new texture to the framebuffer
context.BindFramebuffer(constants::FRAMEBUFFER, Some(&framebuffer));
context.FramebufferTexture2D(
constants::FRAMEBUFFER,
constants::COLOR_ATTACHMENT0,
constants::TEXTURE_2D,
Some(&texture),
0,
);
// Create backing store and bind a renderbuffer if requested
if init.depth || init.stencil {
let (internal_format, attachment) = if init.depth && init.stencil {
clear_bits |= constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT;
(
constants::DEPTH_STENCIL,
constants::DEPTH_STENCIL_ATTACHMENT,
)
} else if init.depth {
clear_bits |= constants::DEPTH_BUFFER_BIT;
(constants::DEPTH_COMPONENT16, constants::DEPTH_ATTACHMENT)
} else {
clear_bits |= constants::STENCIL_BUFFER_BIT;
(constants::STENCIL_INDEX8, constants::STENCIL_ATTACHMENT)
};
context.BindRenderbuffer(constants::RENDERBUFFER, Some(&render_buffer));
context.RenderbufferStorage(
constants::RENDERBUFFER,
internal_format,
resolution.width,
resolution.height,
);
context.FramebufferRenderbuffer(
constants::FRAMEBUFFER,
attachment,
constants::RENDERBUFFER,
Some(&render_buffer),
);
}
context.initialize_framebuffer(clear_bits);
// Restore the WebGL state while complaining about global mutable state
let fb_status = context.CheckFramebufferStatus(constants::FRAMEBUFFER);
let gl_status = context.GetError();
context.BindTexture(constants::TEXTURE_2D, old_texture.as_ref().map(|t| &**t));
context.BindFramebuffer(constants::FRAMEBUFFER, old_fbo.as_ref().map(|f| &**f));
context.BindRenderbuffer(constants::RENDERBUFFER, old_rbo.as_ref().map(|f| &**f));
// Step 9.4: "If layers resources were unable to be created for any reason,
// throw an OperationError and abort these steps."
if let Err(err) = sc {
error!("TexImage2D error {:?} while creating XR context", err);
return Err(Error::Operation);
}
if fb_status != constants::FRAMEBUFFER_COMPLETE {
error!(
"Framebuffer error {:x} while creating XR context",
fb_status
);
return Err(Error::Operation);
}
if gl_status != constants::NO_ERROR {
error!("GL error {:x} while creating XR context", gl_status);
return Err(Error::Operation);
}
// Ensure that we finish setting up this layer before continuing.
context.Finish();
@ -198,6 +111,7 @@ impl XRWebGLLayer {
// Step 10. "Return layer."
Ok(XRWebGLLayer::new(
&global.global(),
swap_chain_id,
session,
context,
init,
@ -205,12 +119,18 @@ impl XRWebGLLayer {
))
}
pub fn swap_chain_id(&self) -> WebXRSwapChainId {
self.swap_chain_id
}
pub fn session(&self) -> &XRSession {
&self.session
}
pub fn framebuffer(&self) -> &WebGLFramebuffer {
&self.framebuffer
pub fn swap_buffers(&self) {
if let WebGLFramebufferId::Opaque(id) = self.framebuffer.id() {
self.context.swap_buffers(Some(id));
}
}
}

View file

@ -24,7 +24,7 @@ layout-2013 = ["layout_thread_2013"]
layout-2020 = ["layout_thread_2020"]
max_log_level = ["log/release_max_level_info"]
native-bluetooth = ["bluetooth/native-bluetooth"]
no_wgl = ["canvas/no_wgl"]
no-wgl = ["canvas/no-wgl"]
uwp = ["servo_config/uwp", "script/uwp"]
webrender_debugger = ["webrender/debugger"]
no_static_freetype = ["webrender/no_static_freetype"]
@ -63,7 +63,6 @@ media = {path = "../media"}
msg = {path = "../msg"}
net = {path = "../net"}
net_traits = {path = "../net_traits"}
offscreen_gl_context = "0.25"
profile = {path = "../profile"}
profile_traits = {path = "../profile_traits"}
script = {path = "../script"}
@ -83,6 +82,7 @@ webdriver_server = {path = "../webdriver_server", optional = true}
webvr = {path = "../webvr"}
webvr_traits = {path = "../webvr_traits"}
webxr-api = {git = "https://github.com/servo/webxr"}
surfman = { git = "https://github.com/pcwalton/surfman", features = ["sm-osmesa"] }
[target.'cfg(all(not(target_os = "windows"), not(target_os = "ios"), not(target_os="android"), not(target_arch="arm"), not(target_arch="aarch64")))'.dependencies]
gaol = "0.2.1"

View file

@ -64,8 +64,8 @@ fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use canvas::gl_context::{CloneableDispatcher, GLContextFactory};
use canvas::webgl_thread::{ThreadMode, WebGLMainThread, WebGLThreads};
use canvas::WebGLComm;
use canvas_traits::webgl::WebGLThreads;
use compositing::compositor_thread::{
CompositorProxy, CompositorReceiver, InitialCompositorState, Msg,
};
@ -100,7 +100,6 @@ use media::{GLPlayerThreads, WindowGLContext};
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId};
use net::resource_thread::new_resource_threads;
use net_traits::IpcSend;
use offscreen_gl_context::GLContextDispatcher;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
@ -114,8 +113,17 @@ use std::borrow::Cow;
use std::cmp::max;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
#[cfg(not(target_os = "windows"))]
use surfman::platform::default::device::Device as HWDevice;
#[cfg(not(target_os = "windows"))]
use surfman::platform::generic::osmesa::device::Device as SWDevice;
#[cfg(not(target_os = "windows"))]
use surfman::platform::generic::universal::context::Context;
use surfman::platform::generic::universal::device::Device;
use webrender::{RendererKind, ShaderPrecacheFlags};
use webrender_traits::{WebrenderExternalImageHandlers, WebrenderImageHandlerType};
use webrender_traits::WebrenderImageHandlerType;
use webrender_traits::{WebrenderExternalImageHandlers, WebrenderExternalImageRegistry};
use webvr::{VRServiceManager, WebVRCompositorHandler, WebVRThread};
use webvr_traits::WebVRMsg;
@ -260,7 +268,6 @@ pub struct Servo<Window: WindowMethods + 'static + ?Sized> {
embedder_receiver: EmbedderReceiver,
embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>,
profiler_enabled: bool,
webgl_thread_data: Option<Rc<WebGLMainThread>>,
}
#[derive(Clone)]
@ -328,7 +335,7 @@ where
}
// Make sure the gl context is made current.
window.prepare_for_composite();
window.make_gl_context_current();
// Reserving a namespace to create TopLevelBrowserContextId.
PipelineNamespace::install(PipelineNamespaceId(0));
@ -451,58 +458,18 @@ where
(None, None, None)
};
// GLContext factory used to create WebGL Contexts
let gl_factory = if opts.should_use_osmesa() {
GLContextFactory::current_osmesa_handle()
} else {
let dispatcher =
Box::new(MainThreadDispatcher::new(compositor_proxy.clone())) as Box<_>;
let gl_type = match window.gl().get_type() {
gl::GlType::Gl => sparkle::gl::GlType::Gl,
gl::GlType::Gles => sparkle::gl::GlType::Gles,
};
GLContextFactory::current_native_handle(dispatcher, gl_type)
};
let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new();
let mut external_image_handlers = Box::new(external_image_handlers);
let run_webgl_on_main_thread =
cfg!(windows) || std::env::var("SERVO_WEBGL_MAIN_THREAD").is_ok();
// Initialize WebGL Thread entry point.
let webgl_result = gl_factory.map(|factory| {
let (webgl_threads, thread_data, webxr_handler, image_handler, output_handler) =
WebGLThreads::new(
factory,
window.gl(),
webrender_api_sender.clone(),
webvr_compositor.map(|c| c as Box<_>),
external_images.clone(),
if run_webgl_on_main_thread {
ThreadMode::MainThread(embedder.create_event_loop_waker())
} else {
ThreadMode::OffThread
},
);
// Set webrender external image handler for WebGL textures
external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
// Set webxr external image handler for WebGL textures
webxr_main_thread.set_webgl(webxr_handler);
// Set DOM to texture handler, if enabled.
if let Some(output_handler) = output_handler {
webrender.set_output_image_handler(output_handler);
}
(webgl_threads, thread_data)
});
let (webgl_threads, webgl_thread_data) = match webgl_result {
Some((a, b)) => (Some(a), b),
None => (None, None),
};
let webgl_threads = create_webgl_threads(
&*window,
&mut webrender,
webrender_api_sender.clone(),
webvr_compositor,
&mut webxr_main_thread,
&mut external_image_handlers,
external_images.clone(),
);
let glplayer_threads = match window.get_gl_context() {
GlContext::Unknown => None,
@ -523,16 +490,7 @@ where
webrender.set_external_image_handler(external_image_handlers);
// When webgl execution occurs on the main thread, and the script thread
// lives in the same process, then the script thread needs the ability to
// wake up the main thread's event loop when webgl commands need processing.
// When there are multiple processes, this is handled automatically by
// the IPC receiving handler instead.
let event_loop_waker = if run_webgl_on_main_thread && !opts.multiprocess {
Some(embedder.create_event_loop_waker())
} else {
None
};
let event_loop_waker = None;
// Create the constellation, which maintains the engine
// pipelines, including the script and layout threads, as well
@ -550,7 +508,7 @@ where
webrender_api_sender,
webxr_main_thread.registry(),
player_context,
webgl_threads,
Some(webgl_threads),
webvr_chan,
webvr_constellation_sender,
glplayer_threads,
@ -596,7 +554,6 @@ where
embedder_receiver: embedder_receiver,
embedder_events: Vec::new(),
profiler_enabled: false,
webgl_thread_data,
}
}
@ -788,10 +745,6 @@ where
}
pub fn handle_events(&mut self, events: Vec<WindowEvent>) {
if let Some(ref mut webgl_thread) = self.webgl_thread_data {
webgl_thread.process();
}
if self.compositor.receive_messages() {
self.receive_messages();
}
@ -1052,28 +1005,68 @@ fn create_sandbox() {
panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
}
/// Implements GLContextDispatcher to dispatch functions from GLContext threads to the main thread's event loop.
/// It's used in Windows to allow WGL GLContext sharing.
pub struct MainThreadDispatcher {
compositor_proxy: CompositorProxy,
}
// Initializes the WebGL thread.
fn create_webgl_threads<W>(
window: &W,
webrender: &mut webrender::Renderer,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<Box<WebVRCompositorHandler>>,
webxr_main_thread: &mut webxr_api::MainThreadRegistry,
external_image_handlers: &mut WebrenderExternalImageHandlers,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> WebGLThreads
where
W: WindowMethods + 'static + ?Sized,
{
// Create a `surfman` device and context.
window.make_gl_context_current();
impl MainThreadDispatcher {
fn new(proxy: CompositorProxy) -> Self {
Self {
compositor_proxy: proxy,
#[cfg(not(target_os = "windows"))]
let (device, context) = unsafe {
if opts::get().headless {
let (device, context) = SWDevice::from_current_context()
.expect("Failed to create software graphics context!");
(Device::Software(device), Context::Software(context))
} else {
let (device, context) = HWDevice::from_current_context()
.expect("Failed to create hardware graphics context!");
(Device::Hardware(device), Context::Hardware(context))
}
};
#[cfg(target_os = "windows")]
let (device, context) =
unsafe { Device::from_current_context().expect("Failed to create graphics context!") };
let gl_type = match window.gl().get_type() {
gleam::gl::GlType::Gl => sparkle::gl::GlType::Gl,
gleam::gl::GlType::Gles => sparkle::gl::GlType::Gles,
};
let WebGLComm {
webgl_threads,
webxr_swap_chains,
image_handler,
output_handler,
} = WebGLComm::new(
device,
context,
window.gl(),
webrender_api_sender,
webvr_compositor.map(|compositor| compositor as Box<_>),
external_images,
gl_type,
);
// Set webrender external image handler for WebGL textures
external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
// Set webxr external image handler for WebGL textures
webxr_main_thread.set_swap_chains(webxr_swap_chains);
// Set DOM to texture handler, if enabled.
if let Some(output_handler) = output_handler {
webrender.set_output_image_handler(output_handler);
}
}
impl GLContextDispatcher for MainThreadDispatcher {
fn dispatch(&self, f: Box<dyn Fn() + Send>) {
self.compositor_proxy.send(Msg::Dispatch(f));
}
}
impl CloneableDispatcher for MainThreadDispatcher {
fn clone(&self) -> Box<dyn GLContextDispatcher> {
Box::new(MainThreadDispatcher {
compositor_proxy: self.compositor_proxy.clone(),
}) as Box<_>
}
webgl_threads
}