mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
commit
8ec28978cd
164 changed files with 3105 additions and 552 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue