Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronize WebGL contexts with page rendering #21841

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 1 addition & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -23,3 +23,4 @@ opt-level = 3
#
# [patch."https://github.com/servo/<repository>"]
# <crate> = { path = "/path/to/local/checkout" }
offscreen_gl_context = { path = "../rust-offscreen-rendering-context" }
58 changes: 53 additions & 5 deletions components/canvas/gl_context.rs
Expand Up @@ -3,14 +3,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use super::webgl_thread::{GLState, WebGLImpl};
use canvas_traits::webgl::{WebGLCommand, WebGLCommandBacktrace, WebGLVersion};
use canvas_traits::webgl::{WebGLCommand, WebGLCommandBacktrace, WebGLVersion, GLContextAttributes, GLLimits};
use compositing::compositor_thread::{self, CompositorProxy};
use euclid::Size2D;
use gleam::gl;
use offscreen_gl_context::{
ColorAttachmentType, GLContext, GLContextAttributes, GLContextDispatcher,
ColorAttachmentType, GLContext, GLContextAttributes as RawGLContextAttributes, GLContextDispatcher,
};
use offscreen_gl_context::{GLLimits, GLVersion};
use offscreen_gl_context::{GLLimits as RawGLLimits, GLVersion};
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle, NativeGLContextMethods};
use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle};
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -52,6 +52,7 @@ impl GLContextFactory {
size: Size2D<u32>,
attributes: GLContextAttributes,
) -> Result<GLContextWrapper, &'static str> {
let attributes = map_attrs(attributes);
Ok(match *self {
GLContextFactory::Native(ref handle, ref dispatcher) => {
let dispatcher = dispatcher.as_ref().map(|d| Box::new(d.clone()) as Box<_>);
Expand Down Expand Up @@ -88,6 +89,7 @@ impl GLContextFactory {
size: Size2D<u32>,
attributes: GLContextAttributes,
) -> Result<GLContextWrapper, &'static str> {
let attributes = map_attrs(attributes);
Ok(match *self {
GLContextFactory::Native(..) => {
GLContextWrapper::Native(GLContext::new_shared_with_dispatcher(
Expand Down Expand Up @@ -176,6 +178,15 @@ impl GLContextWrapper {
}
}

/// Swap the backing texture for the draw buffer, returning the id of the texture
/// now used for reading.
pub fn swap_draw_buffer(&mut self) -> u32 {
match *self {
GLContextWrapper::Native(ref mut ctx) => ctx.swap_draw_buffer().unwrap(),
GLContextWrapper::OSMesa(ref mut ctx) => ctx.swap_draw_buffer().unwrap(),
}
}

pub fn get_info(&self) -> (Size2D<i32>, u32, GLLimits) {
match *self {
GLContextWrapper::Native(ref ctx) => {
Expand All @@ -189,7 +200,7 @@ impl GLContextWrapper {

let limits = ctx.borrow_limits().clone();

(real_size, texture_id, limits)
(real_size, texture_id, map_limits(limits))
},
GLContextWrapper::OSMesa(ref ctx) => {
let (real_size, texture_id) = {
Expand All @@ -202,7 +213,7 @@ impl GLContextWrapper {

let limits = ctx.borrow_limits().clone();

(real_size, texture_id, limits)
(real_size, texture_id, map_limits(limits))
},
}
}
Expand Down Expand Up @@ -243,3 +254,40 @@ impl GLContextDispatcher for MainThreadDispatcher {
.send(compositor_thread::Msg::Dispatch(f));
}
}

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

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,
}
}
139 changes: 82 additions & 57 deletions components/canvas/webgl_thread.rs
Expand Up @@ -2,14 +2,15 @@
* 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::gl_context::{GLContextFactory, GLContextWrapper};
use super::gl_context::{GLContextFactory, GLContextWrapper, map_attrs_to_script_attrs};
use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
use canvas_traits::webgl::*;
use euclid::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use half::f16;
use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
use ipc_channel::ipc;
use offscreen_gl_context::{GLContext, NativeGLContextMethods};
use pixels::{self, PixelFormat};
use std::borrow::Cow;
use std::thread;
Expand Down Expand Up @@ -119,7 +120,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
WebGLMsg::CreateContext(version, size, attributes, result_sender) => {
let result = self.create_webgl_context(version, size, attributes);
result_sender
.send(result.map(|(id, limits, share_mode)| {
.send(result.map(|(id, limits, share_mode, receiver)| {
let data = Self::make_current_if_needed(
id,
&self.contexts,
Expand All @@ -141,6 +142,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
limits,
share_mode,
glsl_version,
ready_to_present: receiver,
}
}))
.unwrap();
Expand All @@ -154,6 +156,9 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => {
self.handle_webgl_command(ctx_id, command, backtrace);
},
WebGLMsg::PresentWebGLFrame(ctx_id) => {
self.handle_present_webgl_frame(ctx_id);
}
WebGLMsg::WebVRCommand(ctx_id, command) => {
self.handle_webvr_command(ctx_id, command);
},
Expand All @@ -177,6 +182,62 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
false
}

fn handle_present_webgl_frame(
&mut self,
context_id: WebGLContextId,
) {
let info = self.cached_context_info.get_mut(&context_id).unwrap();
let new_texture = match Self::make_current_if_needed_mut(
context_id,
&mut self.contexts,
&mut self.bound_context_id,
) {
Some(data) => {
match info.share_mode {
WebGLContextShareMode::Readback => {
// Obtain the pixels for the current frame and notify webrender.
let pixels = Self::raw_pixels(&data.ctx, info.size);
if let Some(image_key) = info.image_key {
Self::update_wr_readback_image(
&self.webrender_api,
info.size,
info.alpha,
image_key,
pixels,
);
} else {
info.image_key = Some(Self::create_wr_readback_image(
&self.webrender_api,
info.size,
info.alpha,
pixels,
));
}
}
WebGLContextShareMode::SharedTexture if info.image_key.is_none() => {
info.image_key = Some(Self::create_wr_external_image(
&self.webrender_api,
info.size,
info.alpha,
context_id
));
}
WebGLContextShareMode::SharedTexture => {}
}

// Swap the backing textures now that this frame is complete.
data.ctx.swap_draw_buffer()
}
None => return,
};

// Store the texture that represents the complete frame.
info.texture_id = new_texture;
if let Some(ref sender) = info.present_notifier {
let _ = sender.send(());
}
}

/// Handles a WebGLCommand for a specific WebGLContext
fn handle_webgl_command(
&mut self,
Expand Down Expand Up @@ -248,7 +309,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
version: WebGLVersion,
size: Size2D<u32>,
attributes: GLContextAttributes,
) -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode), String> {
) -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode, Option<ipc::IpcReceiver<()>>), String> {
// Creating a new GLContext may make the current bound context_id dirty.
// Clear it to ensure that make_current() is called in subsequent commands.
self.bound_context_id = None;
Expand Down Expand Up @@ -279,6 +340,13 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
state: Default::default(),
},
);
let (notifier_sender, notifier_receiver) = match share_mode {
WebGLContextShareMode::SharedTexture => (None, None),
WebGLContextShareMode::Readback => {
let (sender, receiver) = ipc::channel().unwrap();
(Some(sender), Some(receiver))
}
};
self.cached_context_info.insert(
id,
WebGLContextInfo {
Expand All @@ -288,10 +356,11 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
image_key: None,
share_mode,
gl_sync: None,
present_notifier: notifier_sender,
},
);

Ok((id, limits, share_mode))
Ok((id, limits, share_mode, notifier_receiver))
}

/// Resizes a WebGLContext
Expand Down Expand Up @@ -358,62 +427,16 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
self.bound_context_id = None;
}

/// Handles the creation/update of webrender_api::ImageKeys for a specific WebGLContext.
/// This method is invoked from a UpdateWebRenderImage message sent by the layout thread.
/// If SharedTexture is used the UpdateWebRenderImage message is sent only after a WebGLContext creation.
/// If Readback is used UpdateWebRenderImage message is sent always on each layout iteration in order to
/// submit the updated raw pixels.
/// Handles the retrieval of webrender_api::ImageKeys for a specific WebGLContext.
/// This method is invoked from a UpdateWebRenderImage message sent by the layout thread,
/// only once after a WebGL frame is presented to the compositor for the first time.
fn handle_update_wr_image(
&mut self,
context_id: WebGLContextId,
sender: WebGLSender<webrender_api::ImageKey>,
) {
let info = self.cached_context_info.get_mut(&context_id).unwrap();
let webrender_api = &self.webrender_api;

let image_key = match info.share_mode {
WebGLContextShareMode::SharedTexture => {
let size = info.size;
let alpha = info.alpha;
// Reuse existing ImageKey or generate a new one.
// When using a shared texture ImageKeys are only generated after a WebGLContext creation.
*info.image_key.get_or_insert_with(|| {
Self::create_wr_external_image(webrender_api, size, alpha, context_id)
})
},
WebGLContextShareMode::Readback => {
let pixels = Self::raw_pixels(&self.contexts[&context_id].ctx, info.size);
match info.image_key.clone() {
Some(image_key) => {
// ImageKey was already created, but WR Images must
// be updated every frame in readback mode to send the new raw pixels.
Self::update_wr_readback_image(
webrender_api,
info.size,
info.alpha,
image_key,
pixels,
);

image_key
},
None => {
// Generate a new ImageKey for Readback mode.
let image_key = Self::create_wr_readback_image(
webrender_api,
info.size,
info.alpha,
pixels,
);
info.image_key = Some(image_key);
image_key
},
}
},
};

// Send the ImageKey to the Layout thread.
sender.send(image_key).unwrap();
let info = self.cached_context_info.get(&context_id).unwrap();
sender.send(info.image_key.expect("no image key for webgl context?"));
}

fn handle_dom_to_texture(&mut self, command: DOMToTextureCommand) {
Expand Down Expand Up @@ -685,6 +708,8 @@ struct WebGLContextInfo {
share_mode: WebGLContextShareMode,
/// GLSync Object used for a correct synchronization with Webrender external image callbacks.
gl_sync: Option<gl::GLsync>,
/// Signal that the webgl thread is ready to present a new frame to the compositor.
present_notifier: Option<ipc::IpcSender<()>>,
}

/// This trait is used as a bridge between the `WebGLThreads` implementation and
Expand Down Expand Up @@ -755,7 +780,7 @@ impl WebGLImpl {
) {
match command {
WebGLCommand::GetContextAttributes(ref sender) => {
sender.send(*ctx.borrow_attributes()).unwrap()
sender.send(map_attrs_to_script_attrs(*ctx.borrow_attributes())).unwrap()
},
WebGLCommand::ActiveTexture(target) => ctx.gl().active_texture(target),
WebGLCommand::AttachShader(program_id, shader_id) => {
Expand Down
1 change: 0 additions & 1 deletion components/canvas_traits/Cargo.toml
Expand Up @@ -21,7 +21,6 @@ gleam = "0.6.7"
lazy_static = "1"
malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = { path = "../malloc_size_of_derive" }
offscreen_gl_context = {version = "0.21", features = ["serde"]}
pixels = {path = "../pixels"}
serde = "1.0"
serde_bytes = "0.10"
Expand Down