Auto merge of #23777 - jdm:webgl-main-thread, r=asajeffrey

Support running WebGL in its own thread or on the main thread.

This is the final missing piece to support WebGL in ANGLE on Windows. ANGLE doesn't support multiple GL contexts on separate threads using the same underlying Direct3d device, so we need to process all GL operations for WebGL on the same thread as the compositor. These changes try to retain enough flexibility to support both approaches so we can get WebGL working on Windows ASAP.

---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #23697
- [x] There are tests for these changes

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23777)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-07-26 09:16:34 -04:00 committed by GitHub
commit 8ec28978cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
164 changed files with 3105 additions and 552 deletions

1
Cargo.lock generated
View file

@ -395,6 +395,7 @@ dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"canvas_traits 0.0.1",
"cssparser 0.25.8 (registry+https://github.com/rust-lang/crates.io-index)",
"embedder_traits 0.0.1",
"euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -21,6 +21,7 @@ azure = {git = "https://github.com/servo/rust-azure", optional = true}
byteorder = "1"
canvas_traits = {path = "../canvas_traits"}
cssparser = "0.25"
embedder_traits = {path = "../embedder_traits"}
euclid = "0.20"
fnv = "1.0"
gleam = "0.6.7"

View file

@ -3,15 +3,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::gl_context::GLContextFactory;
use crate::webgl_thread::WebGLThread;
use crate::webgl_thread::{TexturesMap, 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, WebVRCommand, WebVRRenderHandler};
use canvas_traits::webgl::{WebGLSender, WebVRRenderHandler};
use embedder_traits::EventLoopWaker;
use euclid::default::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use servo_config::pref;
use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry};
@ -19,37 +23,70 @@ use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry
/// WebGL Threading API entry point that lives in the constellation.
pub struct WebGLThreads(WebGLSender<WebGLMsg>);
pub enum ThreadMode {
MainThread(Box<dyn EventLoopWaker>),
OffThread(Rc<dyn gl::Gl>),
}
impl WebGLThreads {
/// Creates a new WebGLThreads object
pub fn new(
gl_factory: GLContextFactory,
webrender_gl: Rc<dyn gl::Gl>,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
mode: ThreadMode,
) -> (
WebGLThreads,
Option<WebGLMainThread>,
Box<dyn WebrenderExternalImageApi>,
Option<Box<dyn webrender::OutputImageHandler>>,
) {
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
// This implementation creates a single `WebGLThread` for all the pipelines.
let channel = WebGLThread::start(
let init = WebGLThreadInit {
gl_factory,
webrender_api_sender,
webvr_compositor.map(|c| WebVRRenderWrapper(c)),
webvr_compositor,
external_images,
);
sender: sender.clone(),
receiver,
};
let output_handler = if pref!(dom.webgl.dom_to_texture.enabled) {
Some(Box::new(OutputHandler::new(
webrender_gl.clone(),
channel.clone(),
)))
Some(Box::new(match mode {
ThreadMode::MainThread(..) => OutputHandler::new_main_thread(),
ThreadMode::OffThread(ref webrender_gl) => {
OutputHandler::new_off_thread(webrender_gl.clone(), sender.clone())
},
}))
} else {
None
};
let external = WebGLExternalImages::new(webrender_gl, channel.clone());
let (external, webgl_thread) = match mode {
ThreadMode::MainThread(event_loop_waker) => {
let textures = Rc::new(RefCell::new(HashMap::new()));
let thread_data =
WebGLThread::run_on_current_thread(init, event_loop_waker, textures.clone());
(
WebGLThreads(channel),
WebGLExternalImages::new_main_thread(textures),
Some(thread_data),
)
},
ThreadMode::OffThread(webrender_gl) => {
WebGLThread::run_on_own_thread(init);
(
WebGLExternalImages::new_off_thread(webrender_gl, sender.clone()),
None,
)
},
};
(
WebGLThreads(sender),
webgl_thread,
Box::new(external),
output_handler.map(|b| b as Box<_>),
)
@ -70,7 +107,8 @@ impl WebGLThreads {
}
/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
struct WebGLExternalImages {
enum WebGLExternalImages {
OffThread {
webrender_gl: Rc<dyn gl::Gl>,
webgl_channel: WebGLSender<WebGLMsg>,
// Used to avoid creating a new channel on each received WebRender request.
@ -78,61 +116,79 @@ struct WebGLExternalImages {
WebGLSender<(u32, Size2D<i32>, usize)>,
WebGLReceiver<(u32, Size2D<i32>, usize)>,
),
},
MainThread {
textures: TexturesMap,
},
}
impl WebGLExternalImages {
fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
Self {
fn new_off_thread(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
WebGLExternalImages::OffThread {
webrender_gl,
webgl_channel: channel,
lock_channel: webgl_channel().unwrap(),
}
}
fn new_main_thread(textures: TexturesMap) -> Self {
WebGLExternalImages::MainThread { textures }
}
}
impl WebrenderExternalImageApi for WebGLExternalImages {
fn lock(&mut self, id: u64) -> (u32, Size2D<i32>) {
match *self {
WebGLExternalImages::OffThread {
ref webgl_channel,
ref webrender_gl,
ref lock_channel,
} => {
// 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
webgl_channel
.send(WebGLMsg::Lock(
WebGLContextId(id as usize),
self.lock_channel.0.clone(),
lock_channel.0.clone(),
))
.unwrap();
let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
let (image_id, size, gl_sync) = lock_channel.1.recv().unwrap();
// 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.
self.webrender_gl
.wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
webrender_gl.wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
(image_id, size)
},
WebGLExternalImages::MainThread { ref textures } => {
let textures = textures.borrow();
let entry = textures
.get(&WebGLContextId(id as usize))
.expect("no texture entry???");
(entry.0, entry.1)
},
}
}
fn unlock(&mut self, id: u64) {
self.webgl_channel
match *self {
WebGLExternalImages::OffThread {
ref webgl_channel, ..
} => {
webgl_channel
.send(WebGLMsg::Unlock(WebGLContextId(id as usize)))
.unwrap();
},
WebGLExternalImages::MainThread { .. } => {},
}
}
/// Wrapper to send WebVR commands used in `WebGLThread`.
struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>);
impl WebVRRenderHandler for WebVRRenderWrapper {
fn handle(
&mut self,
gl: &dyn gl::Gl,
command: WebVRCommand,
texture: Option<(u32, Size2D<i32>)>,
) {
self.0.handle(gl, command, texture);
}
}
/// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait.
type OutputHandlerData = Option<(u32, Size2D<i32>)>;
struct OutputHandler {
enum OutputHandler {
OffThread {
webrender_gl: Rc<dyn gl::Gl>,
webgl_channel: WebGLSender<WebGLMsg>,
// Used to avoid creating a new channel on each received WebRender request.
@ -141,17 +197,23 @@ struct OutputHandler {
WebGLReceiver<OutputHandlerData>,
),
sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
},
MainThread,
}
impl OutputHandler {
fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
Self {
fn new_off_thread(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
OutputHandler::OffThread {
webrender_gl,
webgl_channel: channel,
lock_channel: webgl_channel().unwrap(),
sync_objects: Default::default(),
}
}
fn new_main_thread() -> Self {
OutputHandler::MainThread
}
}
/// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization.
@ -160,29 +222,49 @@ impl webrender::OutputImageHandler for OutputHandler {
&mut self,
id: webrender_api::PipelineId,
) -> Option<(u32, webrender_api::units::FramebufferIntSize)> {
match *self {
OutputHandler::OffThread {
ref webrender_gl,
ref lock_channel,
ref webgl_channel,
..
} => {
// Insert a fence in the WR command queue
let gl_sync = self
.webrender_gl
.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
let gl_sync = webrender_gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
// 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
let command =
DOMToTextureCommand::Lock(id, gl_sync as usize, lock_channel.0.clone());
webgl_channel
.send(WebGLMsg::DOMToTextureCommand(command))
.unwrap();
self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
(
tex_id,
webrender_api::units::FramebufferIntSize::new(size.width, size.height),
)
})
},
OutputHandler::MainThread => unimplemented!(),
}
}
fn unlock(&mut self, id: webrender_api::PipelineId) {
if let Some(gl_sync) = self.sync_objects.remove(&id) {
match *self {
OutputHandler::OffThread {
ref webrender_gl,
ref mut sync_objects,
..
} => {
if let Some(gl_sync) = sync_objects.remove(&id) {
// Flush the Sync object into the GPU's command queue to guarantee that it it's signaled.
self.webrender_gl.flush();
webrender_gl.flush();
// Mark the sync object for deletion.
self.webrender_gl.delete_sync(gl_sync);
webrender_gl.delete_sync(gl_sync);
}
},
OutputHandler::MainThread => {},
}
}
}

View file

@ -3,4 +3,4 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
mod inprocess;
pub use self::inprocess::WebGLThreads;
pub use self::inprocess::{ThreadMode, WebGLThreads};

View file

@ -5,21 +5,27 @@
use super::gl_context::{map_attrs_to_script_attrs, GLContextFactory, GLContextWrapper};
use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
use canvas_traits::webgl::*;
use embedder_traits::EventLoopWaker;
use euclid::default::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use half::f16;
use ipc_channel::ipc::IpcSender;
use ipc_channel::ipc::{self, IpcSender, OpaqueIpcMessage};
use ipc_channel::router::ROUTER;
use offscreen_gl_context::{DrawBuffer, GLContext, NativeGLContextMethods};
use pixels::{self, PixelFormat};
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::thread;
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
/// WebGL Threading API entry point that lives in the constellation.
/// It allows to get a WebGLThread handle for each script pipeline.
pub use crate::webgl_mode::WebGLThreads;
pub use crate::webgl_mode::{ThreadMode, WebGLThreads};
struct GLContextData {
ctx: GLContextWrapper,
@ -50,7 +56,7 @@ impl Default for GLState {
/// A WebGLThread manages the life cycle and message multiplexing of
/// a set of WebGLContexts living in the same thread.
pub struct WebGLThread<VR: WebVRRenderHandler + 'static> {
pub(crate) struct WebGLThread {
/// Factory used to create a new GLContext shared with the WR/Main thread.
gl_factory: GLContextFactory,
/// Channel used to generate/update or delete `webrender_api::ImageKey`s.
@ -62,20 +68,74 @@ pub struct WebGLThread<VR: WebVRRenderHandler + 'static> {
/// Current bound context.
bound_context_id: Option<WebGLContextId>,
/// Handler user to send WebVR commands.
webvr_compositor: Option<VR>,
webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
/// Texture ids and sizes used in DOM to texture outputs.
dom_outputs: FnvHashMap<webrender_api::PipelineId, DOMToTextureData>,
/// List of registered webrender external images.
/// We use it to get an unique ID for new WebGLContexts.
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
/// The receiver that will be used for processing WebGL messages.
receiver: WebGLReceiver<WebGLMsg>,
/// The receiver that should be used to send WebGL messages for processing.
sender: WebGLSender<WebGLMsg>,
}
impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
pub fn new(
gl_factory: GLContextFactory,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<VR>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
/// A map of GL contexts to backing textures and their sizes.
/// Only used for accessing this information when the WebGL processing is run
/// on the main thread and the compositor needs access to this information
/// synchronously.
pub(crate) type TexturesMap = Rc<RefCell<HashMap<WebGLContextId, (u32, Size2D<i32>)>>>;
#[derive(PartialEq)]
enum EventLoop {
Blocking,
Nonblocking,
}
/// The data required to initialize an instance of the WebGLThread type.
pub(crate) struct WebGLThreadInit {
pub gl_factory: GLContextFactory,
pub webrender_api_sender: webrender_api::RenderApiSender,
pub webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
pub external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
pub sender: WebGLSender<WebGLMsg>,
pub receiver: WebGLReceiver<WebGLMsg>,
}
/// The extra data required to run an instance of WebGLThread when it is
/// not running in its own thread.
pub struct WebGLMainThread {
thread_data: WebGLThread,
shut_down: bool,
textures: TexturesMap,
}
impl WebGLMainThread {
/// Synchronously process all outstanding WebGL messages.
pub fn process(&mut self) {
if self.shut_down {
return;
}
// Any context could be current when we start.
self.thread_data.bound_context_id = None;
self.shut_down = !self
.thread_data
.process(EventLoop::Nonblocking, Some(self.textures.clone()))
}
}
impl WebGLThread {
/// Create a new instance of WebGLThread.
pub(crate) fn new(
WebGLThreadInit {
gl_factory,
webrender_api_sender,
webvr_compositor,
external_images,
sender,
receiver,
}: WebGLThreadInit,
) -> Self {
WebGLThread {
gl_factory,
@ -86,49 +146,80 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
webvr_compositor,
dom_outputs: Default::default(),
external_images,
sender,
receiver,
}
}
/// Creates a new `WebGLThread` and returns a Sender to
/// communicate with it.
pub fn start(
gl_factory: GLContextFactory,
webrender_api_sender: webrender_api::RenderApiSender,
webvr_compositor: Option<VR>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
) -> WebGLSender<WebGLMsg> {
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
let result = sender.clone();
thread::Builder::new()
.name("WebGLThread".to_owned())
.spawn(move || {
let mut renderer = WebGLThread::new(
gl_factory,
webrender_api_sender,
webvr_compositor,
external_images,
/// Perform all initialization required to run an instance of WebGLThread
/// concurrently on the current thread. Returns a `WebGLMainThread` instance
/// that can be used to process any outstanding WebGL messages at any given
/// point in time.
pub(crate) fn run_on_current_thread(
mut init: WebGLThreadInit,
event_loop_waker: Box<dyn EventLoopWaker>,
textures: TexturesMap,
) -> WebGLMainThread {
if let WebGLReceiver::Ipc(ref mut receiver) = init.receiver {
// Interpose a new channel in between the existing WebGL channel endpoints.
// This will bounce all WebGL messages through the router thread adding a small
// delay, but this will also ensure that the main thread will wake up and
// process the WebGL message when it arrives.
let (from_router_sender, from_router_receiver) = ipc::channel::<WebGLMsg>().unwrap();
let old_receiver = mem::replace(receiver, from_router_receiver);
ROUTER.add_route(
old_receiver.to_opaque(),
Box::new(move |msg: OpaqueIpcMessage| {
let _ = from_router_sender.send(msg.to().unwrap());
event_loop_waker.wake();
}),
);
let webgl_chan = WebGLChan(sender);
loop {
let msg = receiver.recv().unwrap();
let exit = renderer.handle_msg(msg, &webgl_chan);
if exit {
return;
}
WebGLMainThread {
thread_data: WebGLThread::new(init),
textures,
shut_down: false,
}
}
/// Perform all initialization required to run an instance of WebGLThread
/// in parallel on its own dedicated thread.
pub(crate) fn run_on_own_thread(init: WebGLThreadInit) {
thread::Builder::new()
.name("WebGL thread".to_owned())
.spawn(move || {
let mut data = WebGLThread::new(init);
data.process(EventLoop::Blocking, None);
})
.expect("Thread spawning failed");
}
result
fn process(&mut self, loop_type: EventLoop, textures: Option<TexturesMap>) -> bool {
let webgl_chan = WebGLChan(self.sender.clone());
while let Ok(msg) = match loop_type {
EventLoop::Blocking => self.receiver.recv(),
EventLoop::Nonblocking => self.receiver.try_recv(),
} {
let exit = self.handle_msg(msg, &webgl_chan, textures.as_ref());
if exit {
return false;
}
}
true
}
/// Handles a generic WebGLMsg message
#[inline]
fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool {
fn handle_msg(
&mut self,
msg: WebGLMsg,
webgl_chan: &WebGLChan,
textures: Option<&TexturesMap>,
) -> bool {
trace!("processing {:?}", msg);
match msg {
WebGLMsg::CreateContext(version, size, attributes, result_sender) => {
let result = self.create_webgl_context(version, size, attributes);
let result = self.create_webgl_context(version, size, attributes, textures);
result_sender
.send(result.map(|(id, limits, share_mode)| {
let data = Self::make_current_if_needed(
@ -173,10 +264,10 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
.unwrap();
},
WebGLMsg::ResizeContext(ctx_id, size, sender) => {
self.resize_webgl_context(ctx_id, size, sender);
self.resize_webgl_context(ctx_id, size, sender, textures);
},
WebGLMsg::RemoveContext(ctx_id) => {
self.remove_webgl_context(ctx_id);
self.remove_webgl_context(ctx_id, textures);
},
WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => {
self.handle_webgl_command(ctx_id, command, backtrace);
@ -296,6 +387,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
version: WebGLVersion,
size: Size2D<u32>,
attributes: GLContextAttributes,
textures: Option<&TexturesMap>,
) -> Result<(WebGLContextId, GLLimits, WebGLContextShareMode), 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.
@ -332,6 +424,11 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
state: Default::default(),
},
);
if let Some(ref textures) = textures {
textures.borrow_mut().insert(id, (texture_id, size));
}
self.cached_context_info.insert(
id,
WebGLContextInfo {
@ -354,6 +451,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
context_id: WebGLContextId,
size: Size2D<u32>,
sender: WebGLSender<Result<(), String>>,
textures: Option<&TexturesMap>,
) {
let data = Self::make_current_if_needed_mut(
context_id,
@ -378,6 +476,13 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
// Update webgl texture size. Texture id may change too.
info.texture_id = texture_id;
info.size = real_size;
if let Some(ref textures) = textures {
textures
.borrow_mut()
.insert(context_id, (texture_id, real_size));
}
// Update WR image if needed. Resize image updates are only required for SharedTexture mode.
// Readback mode already updates the image every frame to send the raw pixels.
// See `handle_update_wr_image`.
@ -403,7 +508,7 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
}
/// Removes a WebGLContext and releases attached resources.
fn remove_webgl_context(&mut self, context_id: WebGLContextId) {
fn remove_webgl_context(&mut self, context_id: WebGLContextId, textures: Option<&TexturesMap>) {
// Release webrender image keys.
if let Some(info) = self.cached_context_info.remove(&context_id) {
let mut txn = webrender_api::Transaction::new();
@ -422,6 +527,10 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
// Release GL context.
self.contexts.remove(&context_id);
if let Some(ref textures) = textures {
textures.borrow_mut().remove(&context_id);
}
// Removing a GLContext may make the current bound context_id dirty.
self.bound_context_id = None;
}
@ -729,12 +838,12 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
}
}
impl<VR: WebVRRenderHandler + 'static> Drop for WebGLThread<VR> {
impl Drop for WebGLThread {
fn drop(&mut self) {
// Call remove_context functions in order to correctly delete WebRender image keys.
let context_ids: Vec<WebGLContextId> = self.contexts.keys().map(|id| *id).collect();
for id in context_ids {
self.remove_webgl_context(id);
self.remove_webgl_context(id, None);
}
}
}

View file

@ -71,6 +71,13 @@ where
WebGLReceiver::Mpsc(ref receiver) => receiver.recv().map_err(|_| ()),
}
}
pub fn try_recv(&self) -> Result<T, ()> {
match *self {
WebGLReceiver::Ipc(ref receiver) => receiver.try_recv().map_err(|_| ()),
WebGLReceiver::Mpsc(ref receiver) => receiver.try_recv().map_err(|_| ()),
}
}
}
pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()>

View file

@ -47,6 +47,10 @@ impl<T> WebGLReceiver<T> {
pub fn recv(&self) -> Result<T, mpsc::RecvError> {
self.0.recv()
}
#[inline]
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
self.0.try_recv()
}
}
pub fn webgl_channel<T>() -> Result<(WebGLSender<T>, WebGLReceiver<T>), ()> {

View file

@ -112,7 +112,7 @@ use compositing::compositor_thread::Msg as ToCompositorMsg;
use compositing::SendableFrameTree;
use crossbeam_channel::{unbounded, Receiver, Sender};
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg};
use embedder_traits::{Cursor, EmbedderMsg, EmbedderProxy};
use embedder_traits::{Cursor, EmbedderMsg, EmbedderProxy, EventLoopWaker};
use euclid::{default::Size2D as UntypedSize2D, Scale, Size2D};
use gfx::font_cache_thread::FontCacheThread;
use gfx_traits::Epoch;
@ -416,6 +416,9 @@ pub struct Constellation<Message, LTF, STF> {
/// Application window's GL Context for Media player
player_context: WindowGLContext,
/// Mechanism to force the compositor to process events.
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
}
/// State needed to construct a constellation.
@ -469,6 +472,9 @@ pub struct InitialConstellationState {
/// Application window's GL Context for Media player
pub player_context: WindowGLContext,
/// Mechanism to force the compositor to process events.
pub event_loop_waker: Option<Box<dyn EventLoopWaker>>,
}
/// Data needed for webdriver
@ -767,6 +773,7 @@ where
enable_canvas_antialiasing,
glplayer_threads: state.glplayer_threads,
player_context: state.player_context,
event_loop_waker: state.event_loop_waker,
};
constellation.run();
@ -1009,6 +1016,7 @@ where
webvr_chan: self.webvr_chan.clone(),
webxr_registry: self.webxr_registry.clone(),
player_context: self.player_context.clone(),
event_loop_waker: self.event_loop_waker.as_ref().map(|w| (*w).clone_box()),
});
let pipeline = match result {

View file

@ -11,6 +11,7 @@ use compositing::CompositionPipeline;
use compositing::CompositorProxy;
use crossbeam_channel::Sender;
use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
use embedder_traits::EventLoopWaker;
use euclid::{Scale, Size2D};
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
@ -195,6 +196,9 @@ pub struct InitialPipelineState {
/// Application window's GL Context for Media player
pub player_context: WindowGLContext,
/// Mechanism to force the compositor to process events.
pub event_loop_waker: Option<Box<dyn EventLoopWaker>>,
}
pub struct NewPipeline {
@ -327,7 +331,11 @@ impl Pipeline {
let register = state
.background_monitor_register
.expect("Couldn't start content, no background monitor has been initiated");
unprivileged_pipeline_content.start_all::<Message, LTF, STF>(false, register);
unprivileged_pipeline_content.start_all::<Message, LTF, STF>(
false,
register,
state.event_loop_waker,
);
None
};
@ -524,6 +532,7 @@ impl UnprivilegedPipelineContent {
self,
wait_for_completion: bool,
background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
) where
LTF: LayoutThreadFactory<Message = Message>,
STF: ScriptThreadFactory<Message = Message>,
@ -566,6 +575,7 @@ impl UnprivilegedPipelineContent {
webrender_api_sender: self.webrender_api_sender.clone(),
layout_is_busy: layout_thread_busy_flag.clone(),
player_context: self.player_context.clone(),
event_loop_waker,
},
self.load_data.clone(),
self.opts.profile_script_events,

View file

@ -54,6 +54,7 @@ use canvas_traits::webgl::{WebGLShaderId, WebGLTextureId, WebGLVersion, WebGLVer
use crossbeam_channel::{Receiver, Sender};
use cssparser::RGBA;
use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
use embedder_traits::EventLoopWaker;
use encoding_rs::{Decoder, Encoding};
use euclid::default::{Point2D, Rect, Rotation3D, Transform2D, Transform3D};
use euclid::Length as EuclidLength;
@ -146,7 +147,7 @@ pub unsafe trait JSTraceable {
unsafe fn trace(&self, trc: *mut JSTracer);
}
unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>);
unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>);
unsafe_no_jsmanaged_fields!(CSSError);

View file

@ -32,11 +32,11 @@ use crate::dom::vreyeparameters::VREyeParameters;
use crate::dom::vrframedata::VRFrameData;
use crate::dom::vrpose::VRPose;
use crate::dom::vrstageparameters::VRStageParameters;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webglrenderingcontext::{WebGLMessageSender, WebGLRenderingContext};
use crate::script_runtime::CommonScriptMsg;
use crate::script_runtime::ScriptThreadEventCategory::WebVREvent;
use crate::task_source::{TaskSource, TaskSourceName};
use canvas_traits::webgl::{webgl_channel, WebGLMsgSender, WebGLReceiver, WebVRCommand};
use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand};
use crossbeam_channel::{unbounded, Sender};
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
@ -102,7 +102,7 @@ struct VRRAFUpdate {
depth_near: f64,
depth_far: f64,
/// WebGL API sender
api_sender: Option<WebGLMsgSender>,
api_sender: Option<WebGLMessageSender>,
/// Number uniquely identifying the WebGL context
/// so that we may setup/tear down VR compositors as things change
context_id: usize,
@ -583,7 +583,7 @@ impl VRDisplay {
.fire(self.global().upcast::<EventTarget>());
}
fn api_sender(&self) -> Option<WebGLMsgSender> {
fn api_sender(&self) -> Option<WebGLMessageSender> {
self.layer_ctx.get().map(|c| c.webgl_sender())
}

View file

@ -54,12 +54,13 @@ use backtrace::Backtrace;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLLimits, GlType,
Parameter, TexDataType, TexFormat, TexParameter, WebGLCommand, WebGLCommandBacktrace,
WebGLContextShareMode, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender,
WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSender, WebGLVersion, WebVRCommand,
YAxisTreatment,
Parameter, TexDataType, TexFormat, TexParameter, WebGLChan, WebGLCommand,
WebGLCommandBacktrace, WebGLContextId, WebGLContextShareMode, WebGLError,
WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId, WebGLResult,
WebGLSLVersion, WebGLSendResult, WebGLSender, WebGLVersion, WebVRCommand, YAxisTreatment,
};
use dom_struct::dom_struct;
use embedder_traits::EventLoopWaker;
use euclid::default::{Point2D, Rect, Size2D};
use ipc_channel::ipc::{self, IpcSharedMemory};
use js::jsapi::{JSContext, JSObject, Type};
@ -79,6 +80,7 @@ use std::cell::Cell;
use std::cmp;
use std::ptr::{self, NonNull};
use std::rc::Rc;
use webrender_api::ImageKey;
// From the GLES 2.0.25 spec, page 85:
//
@ -135,7 +137,7 @@ bitflags! {
pub struct WebGLRenderingContext {
reflector_: Reflector,
#[ignore_malloc_size_of = "Channels are hard"]
webgl_sender: WebGLMsgSender,
webgl_sender: WebGLMessageSender,
#[ignore_malloc_size_of = "Defined in webrender"]
webrender_image: Cell<Option<webrender_api::ImageKey>>,
share_mode: WebGLContextShareMode,
@ -197,7 +199,10 @@ impl WebGLRenderingContext {
let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
Self {
reflector_: Reflector::new(),
webgl_sender: ctx_data.sender,
webgl_sender: WebGLMessageSender::new(
ctx_data.sender,
window.get_event_loop_waker(),
),
webrender_image: Cell::new(None),
share_mode: ctx_data.share_mode,
webgl_version,
@ -319,7 +324,7 @@ impl WebGLRenderingContext {
}
}
pub fn webgl_sender(&self) -> WebGLMsgSender {
pub(crate) fn webgl_sender(&self) -> WebGLMessageSender {
self.webgl_sender.clone()
}
@ -4288,3 +4293,92 @@ impl TexPixels {
}
}
}
#[derive(JSTraceable)]
pub(crate) struct WebGLCommandSender {
sender: WebGLChan,
waker: Option<Box<dyn EventLoopWaker>>,
}
impl WebGLCommandSender {
pub fn new(sender: WebGLChan, waker: Option<Box<dyn EventLoopWaker>>) -> WebGLCommandSender {
WebGLCommandSender { sender, waker }
}
pub fn send(&self, msg: WebGLMsg) -> WebGLSendResult {
let result = self.sender.send(msg);
if let Some(ref waker) = self.waker {
waker.wake();
}
result
}
}
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct WebGLMessageSender {
sender: WebGLMsgSender,
#[ignore_malloc_size_of = "traits are cumbersome"]
waker: Option<Box<dyn EventLoopWaker>>,
}
impl Clone for WebGLMessageSender {
fn clone(&self) -> WebGLMessageSender {
WebGLMessageSender {
sender: self.sender.clone(),
waker: self.waker.as_ref().map(|w| (*w).clone_box()),
}
}
}
impl WebGLMessageSender {
fn wake_after_send<F: FnOnce() -> WebGLSendResult>(&self, f: F) -> WebGLSendResult {
let result = f();
if let Some(ref waker) = self.waker {
waker.wake();
}
result
}
pub fn new(
sender: WebGLMsgSender,
waker: Option<Box<dyn EventLoopWaker>>,
) -> WebGLMessageSender {
WebGLMessageSender { sender, waker }
}
pub fn context_id(&self) -> WebGLContextId {
self.sender.context_id()
}
pub fn send(&self, msg: WebGLCommand, backtrace: WebGLCommandBacktrace) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send(msg, backtrace))
}
pub fn send_vr(&self, command: WebVRCommand) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_vr(command))
}
pub fn send_resize(
&self,
size: Size2D<u32>,
sender: WebGLSender<Result<(), String>>,
) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_resize(size, sender))
}
pub fn send_remove(&self) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_remove())
}
pub fn send_update_wr_image(&self, sender: WebGLSender<ImageKey>) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_update_wr_image(sender))
}
pub fn send_dom_to_texture(&self, command: DOMToTextureCommand) -> WebGLSendResult {
self.wake_after_send(|| self.sender.send_dom_to_texture(command))
}
pub fn webxr_external_image_api(&self) -> impl webxr_api::WebGLExternalImageApi {
self.sender.webxr_external_image_api()
}
}

View file

@ -50,6 +50,7 @@ use crate::dom::promise::Promise;
use crate::dom::screen::Screen;
use crate::dom::storage::Storage;
use crate::dom::testrunner::TestRunner;
use crate::dom::webglrenderingcontext::WebGLCommandSender;
use crate::dom::windowproxy::WindowProxy;
use crate::dom::worklet::Worklet;
use crate::dom::workletglobalscope::WorkletGlobalScopeType;
@ -73,7 +74,7 @@ use crossbeam_channel::{unbounded, Sender, TryRecvError};
use cssparser::{Parser, ParserInput, SourceLocation};
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
use dom_struct::dom_struct;
use embedder_traits::EmbedderMsg;
use embedder_traits::{EmbedderMsg, EventLoopWaker};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
use ipc_channel::ipc::{channel, IpcSender};
@ -326,6 +327,10 @@ pub struct Window {
/// Window's GL context from application
#[ignore_malloc_size_of = "defined in script_thread"]
player_context: WindowGLContext,
/// A mechanism to force the compositor to process events.
#[ignore_malloc_size_of = "traits are cumbersome"]
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
}
impl Window {
@ -432,8 +437,10 @@ impl Window {
self.current_viewport.clone().get()
}
pub fn webgl_chan(&self) -> Option<WebGLChan> {
self.webgl_chan.clone()
pub(crate) fn webgl_chan(&self) -> Option<WebGLCommandSender> {
self.webgl_chan
.as_ref()
.map(|chan| WebGLCommandSender::new(chan.clone(), self.get_event_loop_waker()))
}
pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> {
@ -498,6 +505,10 @@ impl Window {
pub fn get_player_context(&self) -> WindowGLContext {
self.player_context.clone()
}
pub fn get_event_loop_waker(&self) -> Option<Box<dyn EventLoopWaker>> {
self.event_loop_waker.as_ref().map(|w| (*w).clone_box())
}
}
// https://html.spec.whatwg.org/multipage/#atob
@ -2087,6 +2098,7 @@ impl Window {
replace_surrogates: bool,
user_agent: Cow<'static, str>,
player_context: WindowGLContext,
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
) -> DomRoot<Self> {
let layout_rpc: Box<dyn LayoutRPC + Send> = {
let (rpc_send, rpc_recv) = unbounded();
@ -2169,6 +2181,7 @@ impl Window {
userscripts_path,
replace_surrogates,
player_context,
event_loop_waker,
});
unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) }

View file

@ -93,7 +93,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender};
use devtools_traits::CSSError;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::EmbedderMsg;
use embedder_traits::{EmbedderMsg, EventLoopWaker};
use euclid::default::{Point2D, Rect};
use euclid::Vector2D;
use headers::ReferrerPolicy as ReferrerPolicyHeader;
@ -684,6 +684,9 @@ pub struct ScriptThread {
/// Application window's GL Context for Media player
player_context: WindowGLContext,
/// A mechanism to force the compositor's event loop to process events.
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
}
/// In the event of thread panic, all data on the stack runs its destructor. However, there
@ -1314,6 +1317,7 @@ impl ScriptThread {
replace_surrogates,
user_agent,
player_context: state.player_context,
event_loop_waker: state.event_loop_waker,
}
}
@ -3142,6 +3146,7 @@ impl ScriptThread {
self.replace_surrogates,
self.user_agent.clone(),
self.player_context.clone(),
self.event_loop_waker.as_ref().map(|w| (*w).clone_box()),
);
// Initialize the browsing context for the window.

View file

@ -24,7 +24,7 @@ use bluetooth_traits::BluetoothRequest;
use canvas_traits::webgl::WebGLPipeline;
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::Cursor;
use embedder_traits::{Cursor, EventLoopWaker};
use euclid::{
default::{Point2D, Rect},
Length, Scale, Size2D, Vector2D,
@ -666,6 +666,8 @@ pub struct InitialScriptState {
pub layout_is_busy: Arc<AtomicBool>,
/// Application window's GL Context for Media player
pub player_context: WindowGLContext,
/// Mechanism to force the compositor to process events.
pub event_loop_waker: Option<Box<dyn EventLoopWaker>>,
}
/// This trait allows creating a `ScriptThread` without depending on the `script`

View file

@ -60,9 +60,9 @@ layout_thread_2020 = {path = "../layout_thread_2020", optional = true}
log = "0.4"
media = {path = "../media"}
msg = {path = "../msg"}
offscreen_gl_context = "0.23"
net = {path = "../net"}
net_traits = {path = "../net_traits"}
offscreen_gl_context = "0.23"
profile = {path = "../profile"}
profile_traits = {path = "../profile_traits"}
script = {path = "../script"}

View file

@ -65,7 +65,7 @@ fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use canvas::gl_context::{CloneableDispatcher, GLContextFactory};
use canvas::webgl_thread::WebGLThreads;
use canvas::webgl_thread::{ThreadMode, WebGLMainThread, WebGLThreads};
use compositing::compositor_thread::{
CompositorProxy, CompositorReceiver, InitialCompositorState, Msg,
};
@ -117,6 +117,7 @@ use std::rc::Rc;
use webrender::{RendererKind, ShaderPrecacheFlags};
use webrender_traits::{WebrenderExternalImageHandlers, WebrenderImageHandlerType};
use webvr::{VRServiceManager, WebVRCompositorHandler, WebVRThread};
use webvr_traits::WebVRMsg;
pub use gleam::gl;
pub use keyboard_types;
@ -226,6 +227,7 @@ pub struct Servo<Window: WindowMethods + 'static + ?Sized> {
embedder_receiver: EmbedderReceiver,
embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>,
profiler_enabled: bool,
webgl_thread_data: Option<WebGLMainThread>,
}
#[derive(Clone)]
@ -384,11 +386,93 @@ where
None
};
let (webvr_chan, webvr_constellation_sender, webvr_compositor) =
if let Some(services) = webvr_services {
// WebVR initialization
let (mut handler, sender) = WebVRCompositorHandler::new();
let (webvr_thread, constellation_sender) = WebVRThread::spawn(sender, services);
handler.set_webvr_thread_sender(webvr_thread.clone());
(
Some(webvr_thread),
Some(constellation_sender),
Some(handler),
)
} else {
(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<_>;
GLContextFactory::current_native_handle(dispatcher, window.gl().get_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, image_handler, output_handler) = WebGLThreads::new(
factory,
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(window.gl())
},
);
// Set webrender external image handler for WebGL textures
external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
// 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 glplayer_threads = match window.get_gl_context() {
GlContext::Unknown => None,
_ => {
let (glplayer_threads, image_handler) = GLPlayerThreads::new(external_images);
external_image_handlers
.set_handler(image_handler, WebrenderImageHandlerType::Media);
Some(glplayer_threads)
},
};
let player_context = WindowGLContext {
gl_context: window.get_gl_context(),
native_display: window.get_native_display(),
gl_api: window.get_gl_api(),
glplayer_chan: None,
glplayer_chan: glplayer_threads.as_ref().map(GLPlayerThreads::pipeline),
};
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
};
// Create the constellation, which maintains the engine
@ -403,13 +487,15 @@ where
mem_profiler_chan.clone(),
debugger_chan,
devtools_chan,
&mut webrender,
webrender_document,
webrender_api_sender,
window.gl(),
webvr_services,
webxr_main_thread.registry(),
player_context,
webgl_threads,
webvr_chan,
webvr_constellation_sender,
glplayer_threads,
event_loop_waker,
);
// Send the constellation's swmanager sender to service worker manager thread
@ -450,6 +536,7 @@ where
embedder_receiver: embedder_receiver,
embedder_events: Vec::new(),
profiler_enabled: false,
webgl_thread_data,
}
}
@ -641,6 +728,10 @@ 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();
}
@ -715,13 +806,15 @@ fn create_constellation(
mem_profiler_chan: mem::ProfilerChan,
debugger_chan: Option<debugger::Sender>,
devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
webrender: &mut webrender::Renderer,
webrender_document: webrender_api::DocumentId,
webrender_api_sender: webrender_api::RenderApiSender,
window_gl: Rc<dyn gl::Gl>,
webvr_services: Option<VRServiceManager>,
webxr_registry: webxr_api::Registry,
player_context: WindowGLContext,
webgl_threads: Option<WebGLThreads>,
webvr_chan: Option<IpcSender<WebVRMsg>>,
webvr_constellation_sender: Option<Sender<Sender<ConstellationMsg>>>,
glplayer_threads: Option<GLPlayerThreads>,
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
) -> (Sender<ConstellationMsg>, SWManagerSenders) {
// Global configuration options, parsed from the command line.
let opts = opts::get();
@ -745,69 +838,6 @@ fn create_constellation(
let resource_sender = public_resource_threads.sender();
let (webvr_chan, webvr_constellation_sender, webvr_compositor) =
if let Some(services) = webvr_services {
// WebVR initialization
let (mut handler, sender) = WebVRCompositorHandler::new();
let (webvr_thread, constellation_sender) = WebVRThread::spawn(sender, services);
handler.set_webvr_thread_sender(webvr_thread.clone());
(
Some(webvr_thread),
Some(constellation_sender),
Some(handler),
)
} else {
(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<_>;
GLContextFactory::current_native_handle(dispatcher, window_gl.get_type())
};
let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new();
let mut external_image_handlers = Box::new(external_image_handlers);
// Initialize WebGL Thread entry point.
let webgl_threads = gl_factory.map(|factory| {
let (webgl_threads, image_handler, output_handler) = WebGLThreads::new(
factory,
window_gl,
webrender_api_sender.clone(),
webvr_compositor.map(|c| c as Box<_>),
external_images.clone(),
);
// Set webrender external image handler for WebGL textures
external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
// Set DOM to texture handler, if enabled.
if let Some(output_handler) = output_handler {
webrender.set_output_image_handler(output_handler);
}
webgl_threads
});
let glplayer_threads = match player_context.gl_context {
GlContext::Unknown => None,
_ => {
let (glplayer_threads, image_handler) = GLPlayerThreads::new(external_images);
external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::Media);
Some(glplayer_threads)
},
};
webrender.set_external_image_handler(external_image_handlers);
let player_context = WindowGLContext {
glplayer_chan: glplayer_threads.as_ref().map(|threads| threads.pipeline()),
..player_context
};
let initial_state = InitialConstellationState {
compositor_proxy,
embedder_proxy,
@ -826,6 +856,7 @@ fn create_constellation(
webxr_registry,
glplayer_threads,
player_context,
event_loop_waker,
};
let (constellation_chan, from_swmanager_sender) = Constellation::<
script_layout_interface::message::Msg,
@ -930,7 +961,8 @@ pub fn run_content_process(token: String) {
layout_thread::LayoutThread,
script::script_thread::ScriptThread>(
true,
background_hang_monitor_register
background_hang_monitor_register,
None,
);
}

View file

@ -52,6 +52,8 @@ linux-rel-css:
- ./mach build --release --with-debug-assertions -p servo
- ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default
- env SERVO_WEBGL_MAIN_THREAD=1 ./mach test-wpt --release --processes 24 --log-raw test-wpt-webgl.log --log-errorsummary webgl-errorsummary.log --always-succeed tests/wpt/webgl/tests/conformance
- ./mach filter-intermittents webgl-errorsummary.log --log-intermittents webgl-intermittents.log --log-filteredsummary filtered-webgl-errorsummary.log --tracker-api default --reporter-api default
- bash ./etc/ci/lockfile_changed.sh
- ./etc/ci/clean_build_artifacts.sh

View file

@ -7,27 +7,36 @@
use glutin;
use servo::embedder_traits::EventLoopWaker;
use std::sync::Arc;
use std::sync::{Arc, Condvar, Mutex};
use std::rc::Rc;
use std::cell::RefCell;
use std::thread;
use std::time;
pub struct EventsLoop(Option<glutin::EventsLoop>);
#[allow(dead_code)]
enum EventLoop {
/// A real Glutin windowing event loop.
Glutin(Option<glutin::EventsLoop>),
/// A fake event loop which contains a signalling flag used to ensure
/// that pending events get processed in a timely fashion, and a condition
/// variable to allow waiting on that flag changing state.
Headless(Arc<(Mutex<bool>, Condvar)>),
}
pub struct EventsLoop(EventLoop);
impl EventsLoop {
// Ideally, we could use the winit event loop in both modes,
// but on Linux, the event loop requires a X11 server.
#[cfg(not(target_os = "linux"))]
pub fn new(_headless: bool) -> Rc<RefCell<EventsLoop>> {
Rc::new(RefCell::new(EventsLoop(Some(glutin::EventsLoop::new()))))
Rc::new(RefCell::new(EventsLoop(EventLoop::Glutin(Some(glutin::EventsLoop::new())))))
}
#[cfg(target_os = "linux")]
pub fn new(headless: bool) -> Rc<RefCell<EventsLoop>> {
let events_loop = if headless {
None
EventLoop::Headless(Arc::new((Mutex::new(false), Condvar::new())))
} else {
Some(glutin::EventsLoop::new())
EventLoop::Glutin(Some(glutin::EventsLoop::new()))
};
Rc::new(RefCell::new(EventsLoop(events_loop)))
}
@ -35,39 +44,79 @@ impl EventsLoop {
impl EventsLoop {
pub fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
if let Some(ref events_loop) = self.0 {
match self.0 {
EventLoop::Glutin(ref events_loop) => {
let events_loop = events_loop
.as_ref()
.expect("Can't create waker for unavailable event loop.");
Box::new(HeadedEventLoopWaker::new(&events_loop))
} else {
Box::new(HeadlessEventLoopWaker)
},
EventLoop::Headless(ref data) =>
Box::new(HeadlessEventLoopWaker(data.clone())),
}
}
pub fn as_winit(&self) -> &glutin::EventsLoop {
&self.0.as_ref().expect("Can't access winit event loop while using the fake headless event loop")
match self.0 {
EventLoop::Glutin(Some(ref event_loop)) => event_loop,
EventLoop::Glutin(None) | EventLoop::Headless(..) =>
panic!("Can't access winit event loop while using the fake headless event loop"),
}
}
pub fn take(&mut self) -> Option<glutin::EventsLoop> {
self.0.take()
match self.0 {
EventLoop::Glutin(ref mut event_loop) => event_loop.take(),
EventLoop::Headless(..) => None,
}
}
pub fn poll_events<F>(&mut self, callback: F) where F: FnMut(glutin::Event) {
if let Some(ref mut events_loop) = self.0 {
events_loop.poll_events(callback);
} else {
self.sleep();
match self.0 {
EventLoop::Glutin(Some(ref mut events_loop)) => events_loop.poll_events(callback),
EventLoop::Glutin(None) => (),
EventLoop::Headless(ref data) => {
// This is subtle - the use of the event loop in App::run_loop
// optionally calls run_forever, then always calls poll_events.
// If our signalling flag is true before we call run_forever,
// we don't want to reset it before poll_events is called or
// we'll end up sleeping even though there are events waiting
// to be handled. We compromise by only resetting the flag here
// in poll_events, so that both poll_events and run_forever can
// check it first and avoid sleeping unnecessarily.
self.sleep(&data.0, &data.1);
*data.0.lock().unwrap() = false;
}
}
}
pub fn run_forever<F>(&mut self, mut callback: F) where F: FnMut(glutin::Event) -> glutin::ControlFlow {
if let Some(ref mut events_loop) = self.0 {
match self.0 {
EventLoop::Glutin(ref mut events_loop) => {
let events_loop = events_loop
.as_mut()
.expect("Can't run an unavailable event loop.");
events_loop.run_forever(callback);
} else {
loop {
self.sleep();
}
EventLoop::Headless(ref data) => {
let &(ref flag, ref condvar) = &**data;
while { !*flag.lock().unwrap() } {
self.sleep(flag, condvar);
if callback(glutin::Event::Awakened) == glutin::ControlFlow::Break {
break;
}
}
}
}
fn sleep(&self) {
thread::sleep(time::Duration::from_millis(5));
}
fn sleep(&self, lock: &Mutex<bool>, condvar: &Condvar) {
// To avoid sleeping when we should be processing events, do two things:
// * before sleeping, check whether our signalling flag has been set
// * wait on a condition variable with a maximum timeout, to allow
// being woken up by any signals that occur while sleeping.
let guard = lock.lock().unwrap();
if *guard {
return;
}
let _ = condvar.wait_timeout(
guard, time::Duration::from_millis(5)
).unwrap();
}
}
@ -94,8 +143,19 @@ impl EventLoopWaker for HeadedEventLoopWaker {
}
}
struct HeadlessEventLoopWaker;
struct HeadlessEventLoopWaker(Arc<(Mutex<bool>, Condvar)>);
impl EventLoopWaker for HeadlessEventLoopWaker {
fn wake(&self) {}
fn clone_box(&self) -> Box<dyn EventLoopWaker> { Box::new(HeadlessEventLoopWaker) }
fn wake(&self) {
// Set the signalling flag and notify the condition variable.
// This ensures that any sleep operation is interrupted,
// and any non-sleeping operation will have a change to check
// the flag before going to sleep.
let (ref flag, ref condvar) = *self.0;
let mut flag = flag.lock().unwrap();
*flag = true;
condvar.notify_all();
}
fn clone_box(&self) -> Box<dyn EventLoopWaker> {
Box::new(HeadlessEventLoopWaker(self.0.clone()))
}
}

View file

@ -17,6 +17,8 @@ use servo::webrender_api::units::{DeviceIntRect, DeviceIntSize};
use servo_media::player::context as MediaPlayerCtxt;
use std::cell::Cell;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use std::cell::RefCell;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use std::ffi::CString;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use std::mem;
@ -28,8 +30,8 @@ use std::rc::Rc;
struct HeadlessContext {
width: u32,
height: u32,
_context: osmesa_sys::OSMesaContext,
_buffer: Vec<u32>,
context: osmesa_sys::OSMesaContext,
buffer: RefCell<Vec<u32>>,
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
@ -51,30 +53,17 @@ impl HeadlessContext {
attribs.push(3);
attribs.push(0);
let share = share.map_or(ptr::null_mut(), |share| share._context as *mut _);
let share = share.map_or(ptr::null_mut(), |share| share.context as *mut _);
let context = unsafe { osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), share) };
assert!(!context.is_null());
let mut buffer = vec![0; (width * height) as usize];
unsafe {
let ret = osmesa_sys::OSMesaMakeCurrent(
context,
buffer.as_mut_ptr() as *mut _,
gl::UNSIGNED_BYTE,
width as i32,
height as i32,
);
assert_ne!(ret, 0);
};
HeadlessContext {
width: width,
height: height,
_context: context,
_buffer: buffer,
context: context,
buffer: RefCell::new(vec![0; (width * height) as usize]),
}
}
@ -196,7 +185,23 @@ impl WindowMethods for Window {
self.animation_state.set(state);
}
fn prepare_for_composite(&self) { }
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn prepare_for_composite(&self) {
unsafe {
let mut buffer = self.context.buffer.borrow_mut();
let ret = osmesa_sys::OSMesaMakeCurrent(
self.context.context,
buffer.as_mut_ptr() as *mut _,
gl::UNSIGNED_BYTE,
self.context.width as i32,
self.context.height as i32,
);
assert_ne!(ret, 0);
};
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn prepare_for_composite(&self) {}
fn get_gl_context(&self) -> MediaPlayerCtxt::GlContext {
MediaPlayerCtxt::GlContext::Unknown

View file

@ -251,7 +251,9 @@ impl ServoGlue {
debug!("perform_updates");
let events = mem::replace(&mut self.events, Vec::new());
self.servo.handle_events(events);
self.handle_servo_events()
let r = self.handle_servo_events();
debug!("done perform_updates");
r
}
/// In batch mode, Servo won't call perform_updates automatically.
@ -628,7 +630,7 @@ impl WindowMethods for ServoWindowCallbacks {
}
fn set_animation_state(&self, state: AnimationState) {
debug!("WindowMethods::set_animation_state");
debug!("WindowMethods::set_animation_state: {:?}", state);
self.host_callbacks
.on_animating_changed(state == AnimationState::Animating);
}

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>Test that contexts are freed and garbage collected reasonably</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src=/resources/testharness.js></script>

View file

@ -29,6 +29,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL Context Release Test</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src=/resources/testharness.js></script>

View file

@ -29,6 +29,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>Driver Bug - temp experssions should not crash</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src=/resources/testharness.js></script>

View file

@ -29,6 +29,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL Conformance Tests - Non Reserved Words</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<link rel="stylesheet" href="../../../resources/glsl-feature-tests.css"/>

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: abs_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: all_001_to_004.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: any_001_to_004.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: array_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: atan_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: atan_009_to_012.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: biConstants_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: biConstants_009_to_016.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: biuDepthRange_001_to_002.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_009_to_016.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_017_to_024.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_025_to_032.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_033_to_040.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_041_to_048.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_049_to_056.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_057_to_064.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_065_to_072.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_073_to_080.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_081_to_088.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_089_to_096.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_097_to_104.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_105_to_112.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_113_to_120.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_121_to_128.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_129_to_136.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_137_to_144.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_145_to_152.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_153_to_160.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_161_to_168.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_169_to_176.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: build_177_to_178.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: built_in_varying_array_out_of_bounds_001_to_001.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: ceil_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: clamp_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: control_flow_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: control_flow_009_to_010.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: cos_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: cross_001_to_002.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: default_001_to_001.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: degrees_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: discard_001_to_002.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: distance_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: dot_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: equal_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: equal_009_to_012.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: exp_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: exp_009_to_012.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: exp2_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: exp2_009_to_012.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: faceforward_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: floor_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: fract_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_009_to_016.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_017_to_024.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_025_to_032.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_033_to_040.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_041_to_048.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_049_to_056.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_057_to_064.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_065_to_072.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_073_to_080.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_081_to_088.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_089_to_096.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_097_to_104.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_105_to_112.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_113_to_120.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: functions_121_to_126.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: gl_FragCoord_001_to_003.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: gl_FrontFacing_001_to_001.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: greaterThan_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: greaterThanEqual_001_to_008.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

View file

@ -28,6 +28,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>WebGL GLSL conformance test: inversesqrt_001_to_006.html</title>
<link rel="stylesheet" href="../../../../resources/js-test-style.css" />
<link rel="stylesheet" href="../../../../resources/ogles-tests.css" />

Some files were not shown because too many files have changed in this diff Show more