aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorSamson <16504129+sagudev@users.noreply.github.com>2025-02-21 21:26:27 +0100
committerGitHub <noreply@github.com>2025-02-21 20:26:27 +0000
commita6f19c0092de2e86f4d79c8328e66589764b6da6 (patch)
treea6afbf8fdb371b20ce6fc9a811b220329c5ebdd7 /components
parent084006abb627da8e1f0920887d68c3fba01adf72 (diff)
downloadservo-a6f19c0092de2e86f4d79c8328e66589764b6da6.tar.gz
servo-a6f19c0092de2e86f4d79c8328e66589764b6da6.zip
script: Add `CanvasContext` trait (#35448)
* trait `CanvasContext` Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * fixup most stuff Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * explain and limit crown `allow(crown::unrooted_must_root)` Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Diffstat (limited to 'components')
-rw-r--r--components/script/canvas_context.rs77
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs69
-rw-r--r--components/script/dom/document.rs3
-rw-r--r--components/script/dom/htmlcanvaselement.rs48
-rw-r--r--components/script/dom/webgl2renderingcontext.rs38
-rw-r--r--components/script/dom/webglprogram.rs1
-rw-r--r--components/script/dom/webglrenderingcontext.rs228
-rw-r--r--components/script/dom/webgpu/gpucanvascontext.rs59
-rw-r--r--components/script/dom/webxr/xrlayer.rs1
-rw-r--r--components/script/dom/webxr/xrsession.rs1
-rw-r--r--components/script/dom/webxr/xrwebgllayer.rs1
-rw-r--r--components/script/lib.rs1
12 files changed, 317 insertions, 210 deletions
diff --git a/components/script/canvas_context.rs b/components/script/canvas_context.rs
new file mode 100644
index 00000000000..74855781805
--- /dev/null
+++ b/components/script/canvas_context.rs
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Common interfaces for Canvas Contexts
+
+use canvas_traits::canvas::CanvasId;
+use euclid::default::Size2D;
+use ipc_channel::ipc::IpcSharedMemory;
+use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
+
+use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::node::{Node, NodeDamage};
+
+pub(crate) trait LayoutCanvasRenderingContextHelpers {
+ fn canvas_data_source(self) -> HTMLCanvasDataSource;
+}
+
+pub(crate) trait LayoutHTMLCanvasElementHelpers {
+ fn data(self) -> HTMLCanvasData;
+ fn get_canvas_id_for_layout(self) -> CanvasId;
+}
+
+pub(crate) trait CanvasContext {
+ type ID;
+
+ fn context_id(&self) -> Self::ID;
+
+ fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas;
+
+ fn resize(&self);
+
+ fn get_image_data_as_shared_memory(&self) -> Option<IpcSharedMemory>;
+
+ fn get_image_data(&self) -> Option<Vec<u8>> {
+ self.get_image_data_as_shared_memory().map(|sm| sm.to_vec())
+ }
+
+ fn origin_is_clean(&self) -> bool {
+ true
+ }
+
+ fn size(&self) -> Size2D<u64> {
+ self.canvas().size()
+ }
+
+ fn mark_as_dirty(&self) {
+ if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = &self.canvas() {
+ canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ }
+ }
+
+ fn update_rendering(&self) {}
+
+ fn onscreen(&self) -> bool {
+ match self.canvas() {
+ HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
+ canvas.upcast::<Node>().is_connected()
+ },
+ // FIXME(34628): Offscreen canvases should be considered offscreen if a placeholder is set.
+ // <https://www.w3.org/TR/webgpu/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
+ HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false,
+ }
+ }
+}
+
+impl HTMLCanvasElementOrOffscreenCanvas {
+ pub(crate) fn size(&self) -> Size2D<u64> {
+ match self {
+ HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
+ canvas.get_size().cast()
+ },
+ HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
+ }
+ }
+}
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index 307cead72ba..3ba0addfb17 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -11,12 +11,15 @@ use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
use profile_traits::ipc;
use servo_url::ServoUrl;
+use crate::canvas_context::CanvasContext;
use crate::canvas_state::CanvasState;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin,
CanvasRenderingContext2DMethods, CanvasTextAlign, CanvasTextBaseline,
};
-use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
+use crate::dom::bindings::codegen::UnionTypes::{
+ HTMLCanvasElementOrOffscreenCanvas, StringOrCanvasGradientOrCanvasPattern,
+};
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{reflect_dom_object, DomGlobal, Reflector};
@@ -92,10 +95,6 @@ impl CanvasRenderingContext2D {
self.canvas_state.set_bitmap_dimensions(size);
}
- pub(crate) fn mark_as_dirty(&self) {
- self.canvas_state.mark_as_dirty(self.canvas.as_deref())
- }
-
pub(crate) fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
std::mem::take(&mut self.canvas_state.get_missing_image_urls().borrow_mut())
}
@@ -108,10 +107,6 @@ impl CanvasRenderingContext2D {
self.canvas_state.send_canvas_2d_msg(msg)
}
- pub(crate) fn origin_is_clean(&self) -> bool {
- self.canvas_state.origin_is_clean()
- }
-
pub(crate) fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> {
let rect = Rect::new(
Point2D::new(rect.origin.x as u64, rect.origin.y as u64),
@@ -125,14 +120,6 @@ impl CanvasRenderingContext2D {
)
}
- pub(crate) fn fetch_data(&self) -> IpcSharedMemory {
- let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
- let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender), self.get_canvas_id());
- self.canvas_state.get_ipc_renderer().send(msg).unwrap();
-
- receiver.recv().unwrap()
- }
-
pub(crate) fn send_data(&self, sender: IpcSender<CanvasImageData>) {
let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender), self.get_canvas_id());
let _ = self.canvas_state.get_ipc_renderer().send(msg);
@@ -157,6 +144,54 @@ impl LayoutCanvasRenderingContext2DHelpers for LayoutDom<'_, CanvasRenderingCont
}
}
+impl CanvasContext for CanvasRenderingContext2D {
+ type ID = CanvasId;
+
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Crown is wrong here #35570
+ fn context_id(&self) -> Self::ID {
+ self.canvas_state.get_canvas_id()
+ }
+
+ fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
+ if let Some(ref canvas) = self.canvas {
+ HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas.as_rooted())
+ } else {
+ unimplemented!()
+ }
+ }
+
+ fn resize(&self) {
+ self.set_bitmap_dimensions(self.size().cast())
+ }
+
+ fn get_image_data_as_shared_memory(&self) -> Option<IpcSharedMemory> {
+ let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
+ let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender), self.get_canvas_id());
+ self.canvas_state.get_ipc_renderer().send(msg).unwrap();
+
+ Some(receiver.recv().unwrap())
+ }
+
+ fn get_image_data(&self) -> Option<Vec<u8>> {
+ Some(self.get_rect(Rect::from_size(self.size().cast())))
+ }
+
+ fn origin_is_clean(&self) -> bool {
+ self.canvas_state.origin_is_clean()
+ }
+
+ fn mark_as_dirty(&self) {
+ self.canvas_state.mark_as_dirty(self.canvas.as_deref())
+ }
+
+ fn size(&self) -> Size2D<u64> {
+ self.canvas
+ .as_ref()
+ .map(|c| c.get_size().cast())
+ .unwrap_or(Size2D::zero())
+ }
+}
+
// We add a guard to each of methods by the spec:
// http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/
//
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index ea4ac90422c..ac8c86cfb71 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -80,6 +80,7 @@ use super::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMet
use super::clipboardevent::ClipboardEventType;
use crate::animation_timeline::AnimationTimeline;
use crate::animations::Animations;
+use crate::canvas_context::CanvasContext as _;
use crate::document_loader::{DocumentLoader, LoadType};
use crate::dom::attr::Attr;
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
@@ -3326,7 +3327,7 @@ impl Document {
.iter()
.filter_map(|(_, context)| context.root())
.filter(|context| context.onscreen())
- .for_each(|context| context.update_rendering_of_webgpu_canvas());
+ .for_each(|context| context.update_rendering());
}
pub(crate) fn id_map(&self) -> Ref<HashMapTracedValues<Atom, Vec<Dom<Element>>>> {
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index 112fc26f8ae..4e36395707a 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -9,7 +9,7 @@ use std::rc::Rc;
use canvas_traits::canvas::{CanvasId, CanvasMsg, FromScriptMsg};
use canvas_traits::webgl::{GLContextAttributes, WebGLVersion};
use dom_struct::dom_struct;
-use euclid::default::{Rect, Size2D};
+use euclid::default::Size2D;
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
use image::codecs::jpeg::JpegEncoder;
use image::codecs::png::PngEncoder;
@@ -29,6 +29,8 @@ use servo_media::streams::registry::MediaStreamId;
use servo_media::streams::MediaStreamType;
use style::attr::AttrValue;
+use crate::canvas_context::CanvasContext as _;
+pub(crate) use crate::canvas_context::*;
use crate::dom::attr::Attr;
use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{
@@ -158,19 +160,16 @@ impl HTMLCanvasElement {
)
}
- fn recreate_contexts(&self) {
- let size = self.get_size();
+ fn recreate_contexts_after_resize(&self) {
if let Some(ref context) = *self.context.borrow() {
match *context {
- CanvasContext::Context2d(ref context) => {
- context.set_canvas_bitmap_dimensions(size.to_u64())
- },
- CanvasContext::WebGL(ref context) => context.recreate(size),
- CanvasContext::WebGL2(ref context) => context.recreate(size),
+ CanvasContext::Context2d(ref context) => context.resize(),
+ CanvasContext::WebGL(ref context) => context.resize(),
+ CanvasContext::WebGL2(ref context) => context.resize(),
#[cfg(feature = "webgpu")]
CanvasContext::WebGPU(ref context) => context.resize(),
CanvasContext::Placeholder(ref context) => {
- context.set_canvas_bitmap_dimensions(size.to_u64())
+ context.set_canvas_bitmap_dimensions(self.get_size().to_u64())
},
}
}
@@ -208,15 +207,6 @@ impl HTMLCanvasElement {
}
}
-pub(crate) trait LayoutCanvasRenderingContextHelpers {
- fn canvas_data_source(self) -> HTMLCanvasDataSource;
-}
-
-pub(crate) trait LayoutHTMLCanvasElementHelpers {
- fn data(self) -> HTMLCanvasData;
- fn get_canvas_id_for_layout(self) -> CanvasId;
-}
-
impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
#[allow(unsafe_code)]
fn data(self) -> HTMLCanvasData {
@@ -401,17 +391,17 @@ impl HTMLCanvasElement {
}
let data = match self.context.borrow().as_ref() {
- Some(CanvasContext::Context2d(context)) => Some(context.fetch_data()),
- Some(&CanvasContext::WebGL(_)) => {
+ Some(CanvasContext::Context2d(context)) => context.get_image_data_as_shared_memory(),
+ Some(CanvasContext::WebGL(_context)) => {
// TODO: add a method in WebGLRenderingContext to get the pixels.
return None;
},
- Some(&CanvasContext::WebGL2(_)) => {
+ Some(CanvasContext::WebGL2(_context)) => {
// TODO: add a method in WebGL2RenderingContext to get the pixels.
return None;
},
#[cfg(feature = "webgpu")]
- Some(CanvasContext::WebGPU(context)) => Some(context.get_ipc_image()),
+ Some(CanvasContext::WebGPU(context)) => context.get_image_data_as_shared_memory(),
Some(CanvasContext::Placeholder(context)) => {
let (sender, receiver) =
ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
@@ -431,15 +421,11 @@ impl HTMLCanvasElement {
fn get_content(&self) -> Option<Vec<u8>> {
match *self.context.borrow() {
- Some(CanvasContext::Context2d(ref context)) => {
- Some(context.get_rect(Rect::from_size(self.get_size())))
- },
- Some(CanvasContext::WebGL(ref context)) => context.get_image_data(self.get_size()),
- Some(CanvasContext::WebGL2(ref context)) => {
- context.base_context().get_image_data(self.get_size())
- },
+ Some(CanvasContext::Context2d(ref context)) => context.get_image_data(),
+ Some(CanvasContext::WebGL(ref context)) => context.get_image_data(),
+ Some(CanvasContext::WebGL2(ref context)) => context.get_image_data(),
#[cfg(feature = "webgpu")]
- Some(CanvasContext::WebGPU(ref context)) => Some(context.get_image_data()),
+ Some(CanvasContext::WebGPU(ref context)) => context.get_image_data(),
Some(CanvasContext::Placeholder(_)) | None => {
// Each pixel is fully-transparent black.
Some(vec![0; (self.Width() * self.Height() * 4) as usize])
@@ -737,7 +723,7 @@ impl VirtualMethods for HTMLCanvasElement {
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &local_name!("width") | &local_name!("height") => self.recreate_contexts(),
+ &local_name!("width") | &local_name!("height") => self.recreate_contexts_after_resize(),
_ => (),
};
}
diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs
index b69d0f6961a..c7cc25526d5 100644
--- a/components/script/dom/webgl2renderingcontext.rs
+++ b/components/script/dom/webgl2renderingcontext.rs
@@ -11,8 +11,8 @@ use std::rc::Rc;
use bitflags::bitflags;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
- webgl_channel, GLContextAttributes, InternalFormatParameter, WebGLCommand, WebGLResult,
- WebGLVersion,
+ webgl_channel, GLContextAttributes, InternalFormatParameter, WebGLCommand, WebGLContextId,
+ WebGLResult, WebGLVersion,
};
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D};
@@ -25,6 +25,7 @@ use script_layout_interface::HTMLCanvasDataSource;
use servo_config::pref;
use url::Host;
+use crate::canvas_context::CanvasContext;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{
WebGL2RenderingContextConstants as constants, WebGL2RenderingContextMethods,
};
@@ -205,10 +206,6 @@ impl WebGL2RenderingContext {
static WEBGL2_ORIGINS: &[&str] = &["www.servoexperiments.com"];
impl WebGL2RenderingContext {
- pub(crate) fn recreate(&self, size: Size2D<u32>) {
- self.base.recreate(size)
- }
-
pub(crate) fn current_vao(&self) -> DomRoot<WebGLVertexArrayObject> {
self.base.current_vao_webgl2()
}
@@ -903,6 +900,35 @@ impl WebGL2RenderingContext {
}
}
+impl CanvasContext for WebGL2RenderingContext {
+ type ID = WebGLContextId;
+
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Crown is wrong here #35570
+ fn context_id(&self) -> Self::ID {
+ self.base.context_id()
+ }
+
+ fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
+ self.base.canvas().clone()
+ }
+
+ fn resize(&self) {
+ self.base.resize();
+ }
+
+ fn get_image_data_as_shared_memory(&self) -> Option<IpcSharedMemory> {
+ self.base.get_image_data_as_shared_memory()
+ }
+
+ fn get_image_data(&self) -> Option<Vec<u8>> {
+ self.base.get_image_data()
+ }
+
+ fn mark_as_dirty(&self) {
+ self.base.mark_as_dirty()
+ }
+}
+
impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingContext {
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs
index 674be65fa7f..ad0edb4c64b 100644
--- a/components/script/dom/webglprogram.rs
+++ b/components/script/dom/webglprogram.rs
@@ -12,6 +12,7 @@ use canvas_traits::webgl::{
use dom_struct::dom_struct;
use fnv::FnvHashSet;
+use crate::canvas_context::CanvasContext;
use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index 8e3fa40839d..98a60a19169 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -36,6 +36,7 @@ use serde::{Deserialize, Serialize};
use servo_config::pref;
use webrender_api::ImageKey;
+use crate::canvas_context::CanvasContext;
use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
@@ -351,68 +352,6 @@ impl WebGLRenderingContext {
self.current_vertex_attribs.borrow_mut()
}
- pub(crate) fn recreate(&self, size: Size2D<u32>) {
- let (sender, receiver) = webgl_channel().unwrap();
- self.webgl_sender.send_resize(size, sender).unwrap();
- // FIXME(#21718) The backend is allowed to choose a size smaller than
- // what was requested
- self.size.set(size);
-
- if let Err(msg) = receiver.recv().unwrap() {
- error!("Error resizing WebGLContext: {}", msg);
- return;
- };
-
- // ClearColor needs to be restored because after a resize the GLContext is recreated
- // and the framebuffer is cleared using the default black transparent color.
- let color = self.current_clear_color.get();
- self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
-
- // WebGL Spec: Scissor rect must not change if the canvas is resized.
- // See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html
- // NativeContext handling library changes the scissor after a resize, so we need to reset the
- // default scissor when the canvas was created or the last scissor that the user set.
- let rect = self.current_scissor.get();
- self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
-
- // Bound texture must not change when the canvas is resized.
- // Right now surfman generates a new FBO and the bound texture is changed
- // in order to create a new render to texture attachment.
- // Send a command to re-bind the TEXTURE_2D, if any.
- if let Some(texture) = self
- .textures
- .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
- .unwrap()
- .get()
- {
- self.send_command(WebGLCommand::BindTexture(
- constants::TEXTURE_2D,
- Some(texture.id()),
- ));
- }
-
- // Bound framebuffer must not change when the canvas is resized.
- // Right now surfman generates a new FBO on resize.
- // Send a command to re-bind the framebuffer, if any.
- if let Some(fbo) = self.bound_draw_framebuffer.get() {
- let id = WebGLFramebufferBindingRequest::Explicit(fbo.id());
- self.send_command(WebGLCommand::BindFramebuffer(constants::FRAMEBUFFER, id));
- }
- }
-
- pub(crate) fn context_id(&self) -> WebGLContextId {
- self.webgl_sender.context_id()
- }
-
- pub(crate) fn onscreen(&self) -> bool {
- match self.canvas {
- HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
- canvas.upcast::<Node>().is_connected()
- },
- HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false,
- }
- }
-
#[inline]
pub(crate) fn send_command(&self, command: WebGLCommand) {
self.webgl_sender
@@ -538,27 +477,6 @@ impl WebGLRenderingContext {
}
}
- pub(crate) fn mark_as_dirty(&self) {
- // If we have a bound framebuffer, then don't mark the canvas as dirty.
- if self.bound_draw_framebuffer.get().is_some() {
- return;
- }
-
- // Dirtying the canvas is unnecessary if we're actively displaying immersive
- // XR content right now.
- if self.global().as_window().in_immersive_xr_session() {
- return;
- }
-
- match self.canvas {
- HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
- canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
- canvas.owner_document().add_dirty_webgl_canvas(self);
- },
- HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => {},
- }
- }
-
fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
if indx >= self.limits.max_vertex_attribs {
return self.webgl_error(InvalidValue);
@@ -1133,33 +1051,6 @@ impl WebGLRenderingContext {
self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor });
}
- // Used by HTMLCanvasElement.toDataURL
- //
- // This emits errors quite liberally, but the spec says that this operation
- // can fail and that it is UB what happens in that case.
- //
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
- pub(crate) fn get_image_data(&self, mut size: Size2D<u32>) -> Option<Vec<u8>> {
- handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
-
- let (fb_width, fb_height) = handle_potential_webgl_error!(
- self,
- self.get_current_framebuffer_size().ok_or(InvalidOperation),
- return None
- );
- size.width = cmp::min(size.width, fb_width as u32);
- size.height = cmp::min(size.height, fb_height as u32);
-
- let (sender, receiver) = ipc::bytes_channel().unwrap();
- self.send_command(WebGLCommand::ReadPixels(
- Rect::from_size(size),
- constants::RGBA,
- constants::UNSIGNED_BYTE,
- sender,
- ));
- Some(receiver.recv().unwrap())
- }
-
pub(crate) fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
self.bound_buffer_array.get()
}
@@ -1970,6 +1861,123 @@ impl WebGLRenderingContext {
}
}
+impl CanvasContext for WebGLRenderingContext {
+ type ID = WebGLContextId;
+
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Crown is wrong here #35570
+ fn context_id(&self) -> Self::ID {
+ self.webgl_sender.context_id()
+ }
+
+ fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
+ self.canvas.clone()
+ }
+
+ fn resize(&self) {
+ let size = self.size().cast();
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.webgl_sender.send_resize(size, sender).unwrap();
+ // FIXME(#21718) The backend is allowed to choose a size smaller than
+ // what was requested
+ self.size.set(size);
+
+ if let Err(msg) = receiver.recv().unwrap() {
+ error!("Error resizing WebGLContext: {}", msg);
+ return;
+ };
+
+ // ClearColor needs to be restored because after a resize the GLContext is recreated
+ // and the framebuffer is cleared using the default black transparent color.
+ let color = self.current_clear_color.get();
+ self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
+
+ // WebGL Spec: Scissor rect must not change if the canvas is resized.
+ // See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html
+ // NativeContext handling library changes the scissor after a resize, so we need to reset the
+ // default scissor when the canvas was created or the last scissor that the user set.
+ let rect = self.current_scissor.get();
+ self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
+
+ // Bound texture must not change when the canvas is resized.
+ // Right now surfman generates a new FBO and the bound texture is changed
+ // in order to create a new render to texture attachment.
+ // Send a command to re-bind the TEXTURE_2D, if any.
+ if let Some(texture) = self
+ .textures
+ .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
+ .unwrap()
+ .get()
+ {
+ self.send_command(WebGLCommand::BindTexture(
+ constants::TEXTURE_2D,
+ Some(texture.id()),
+ ));
+ }
+
+ // Bound framebuffer must not change when the canvas is resized.
+ // Right now surfman generates a new FBO on resize.
+ // Send a command to re-bind the framebuffer, if any.
+ if let Some(fbo) = self.bound_draw_framebuffer.get() {
+ let id = WebGLFramebufferBindingRequest::Explicit(fbo.id());
+ self.send_command(WebGLCommand::BindFramebuffer(constants::FRAMEBUFFER, id));
+ }
+ }
+
+ fn get_image_data_as_shared_memory(&self) -> Option<IpcSharedMemory> {
+ // TODO: add a method in WebGLRenderingContext to get the pixels.
+ None
+ }
+
+ // Used by HTMLCanvasElement.toDataURL
+ //
+ // This emits errors quite liberally, but the spec says that this operation
+ // can fail and that it is UB what happens in that case.
+ //
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
+ fn get_image_data(&self) -> Option<Vec<u8>> {
+ handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
+ let mut size = self.size().cast();
+
+ let (fb_width, fb_height) = handle_potential_webgl_error!(
+ self,
+ self.get_current_framebuffer_size().ok_or(InvalidOperation),
+ return None
+ );
+ size.width = cmp::min(size.width, fb_width as u32);
+ size.height = cmp::min(size.height, fb_height as u32);
+
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
+ self.send_command(WebGLCommand::ReadPixels(
+ Rect::from_size(size),
+ constants::RGBA,
+ constants::UNSIGNED_BYTE,
+ sender,
+ ));
+ Some(receiver.recv().unwrap())
+ }
+
+ fn mark_as_dirty(&self) {
+ // If we have a bound framebuffer, then don't mark the canvas as dirty.
+ if self.bound_draw_framebuffer.get().is_some() {
+ return;
+ }
+
+ // Dirtying the canvas is unnecessary if we're actively displaying immersive
+ // XR content right now.
+ if self.global().as_window().in_immersive_xr_session() {
+ return;
+ }
+
+ match self.canvas {
+ HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
+ canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ canvas.owner_document().add_dirty_webgl_canvas(self);
+ },
+ HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => {},
+ }
+ }
+}
+
#[cfg(not(feature = "webgl_backtrace"))]
#[inline]
pub(crate) fn capture_webgl_backtrace<T: DomObject>(_: &T) -> WebGLCommandBacktrace {
diff --git a/components/script/dom/webgpu/gpucanvascontext.rs b/components/script/dom/webgpu/gpucanvascontext.rs
index e4ef65bea31..3b9abd27dce 100644
--- a/components/script/dom/webgpu/gpucanvascontext.rs
+++ b/components/script/dom/webgpu/gpucanvascontext.rs
@@ -7,7 +7,6 @@ use std::cell::RefCell;
use arrayvec::ArrayVec;
use dom_struct::dom_struct;
-use euclid::default::Size2D;
use ipc_channel::ipc::{self, IpcSharedMemory};
use script_layout_interface::HTMLCanvasDataSource;
use webgpu::swapchain::WebGPUContextId;
@@ -20,6 +19,7 @@ use webrender_api::ImageKey;
use super::gpuconvert::convert_texture_descriptor;
use super::gputexture::GPUTexture;
+use crate::canvas_context::CanvasContext;
use crate::conversions::Convert;
use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUTexture_Binding::GPUTextureMethods;
@@ -30,7 +30,6 @@ use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
};
use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas;
use crate::dom::bindings::error::{Error, Fallible};
-use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomGlobal, Reflector};
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::USVString;
@@ -38,20 +37,9 @@ use crate::dom::bindings::weakref::WeakRef;
use crate::dom::document::WebGPUContextsMap;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlcanvaselement::{HTMLCanvasElement, LayoutCanvasRenderingContextHelpers};
-use crate::dom::node::{Node, NodeDamage, NodeTraits};
+use crate::dom::node::NodeTraits;
use crate::script_runtime::CanGc;
-impl HTMLCanvasElementOrOffscreenCanvas {
- fn size(&self) -> Size2D<u64> {
- match self {
- HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
- canvas.get_size().cast()
- },
- HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
- }
- }
-}
-
/// <https://gpuweb.github.io/gpuweb/#supported-context-formats>
fn supported_context_format(format: GPUTextureFormat) -> bool {
// TODO: GPUTextureFormat::Rgba16float
@@ -260,43 +248,24 @@ impl GPUCanvasContext {
);
}
}
-
- fn size(&self) -> Size2D<u64> {
- self.canvas.size()
- }
}
-// public methods for canvas handling
-// these methods should probably be behind trait for all canvases
-impl GPUCanvasContext {
- pub(crate) fn context_id(&self) -> WebGPUContextId {
- self.context_id
- }
+impl CanvasContext for GPUCanvasContext {
+ type ID = WebGPUContextId;
- pub(crate) fn mark_as_dirty(&self) {
- if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = &self.canvas {
- canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
- }
- }
-
- pub(crate) fn onscreen(&self) -> bool {
- match self.canvas {
- HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
- canvas.upcast::<Node>().is_connected()
- },
- // FIXME(#34628): Handle this properly
- HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false,
- }
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Crown is wrong here #35570
+ fn context_id(&self) -> WebGPUContextId {
+ self.context_id
}
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
- pub(crate) fn update_rendering_of_webgpu_canvas(&self) {
+ fn update_rendering(&self) {
// Step 1
self.expire_current_texture();
}
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-update-the-canvas-size>
- pub(crate) fn resize(&self) {
+ fn resize(&self) {
// Step 1
self.replace_drawing_buffer();
// Step 2
@@ -309,9 +278,9 @@ impl GPUCanvasContext {
}
/// <https://gpuweb.github.io/gpuweb/#ref-for-abstract-opdef-get-a-copy-of-the-image-contents-of-a-context%E2%91%A5>
- pub(crate) fn get_ipc_image(&self) -> IpcSharedMemory {
+ fn get_image_data_as_shared_memory(&self) -> Option<IpcSharedMemory> {
// 1. Return a copy of the image contents of context.
- if self.drawing_buffer.borrow().cleared {
+ Some(if self.drawing_buffer.borrow().cleared {
IpcSharedMemory::from_byte(0, self.size().area() as usize * 4)
} else {
let (sender, receiver) = ipc::channel().unwrap();
@@ -323,11 +292,11 @@ impl GPUCanvasContext {
})
.unwrap();
receiver.recv().unwrap()
- }
+ })
}
- pub(crate) fn get_image_data(&self) -> Vec<u8> {
- self.get_ipc_image().to_vec()
+ fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
+ self.canvas.clone()
}
}
diff --git a/components/script/dom/webxr/xrlayer.rs b/components/script/dom/webxr/xrlayer.rs
index 4db2cfe3ac2..1920e508819 100644
--- a/components/script/dom/webxr/xrlayer.rs
+++ b/components/script/dom/webxr/xrlayer.rs
@@ -6,6 +6,7 @@ use canvas_traits::webgl::WebGLContextId;
use dom_struct::dom_struct;
use webxr_api::LayerId;
+use crate::canvas_context::CanvasContext as _;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::Dom;
use crate::dom::eventtarget::EventTarget;
diff --git a/components/script/dom/webxr/xrsession.rs b/components/script/dom/webxr/xrsession.rs
index 553789fa6b4..8f888c0efcc 100644
--- a/components/script/dom/webxr/xrsession.rs
+++ b/components/script/dom/webxr/xrsession.rs
@@ -25,6 +25,7 @@ use webxr_api::{
};
use crate::conversions::Convert;
+use crate::canvas_context::CanvasContext;
use crate::dom::bindings::trace::HashMapTracedValues;
use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::callback::ExceptionHandling;
diff --git a/components/script/dom/webxr/xrwebgllayer.rs b/components/script/dom/webxr/xrwebgllayer.rs
index df0ff932902..07beeb89afc 100644
--- a/components/script/dom/webxr/xrwebgllayer.rs
+++ b/components/script/dom/webxr/xrwebgllayer.rs
@@ -10,6 +10,7 @@ use euclid::{Rect, Size2D};
use js::rust::HandleObject;
use webxr_api::{ContextId as WebXRContextId, LayerId, LayerInit, Viewport};
+use crate::canvas_context::CanvasContext as _;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::{
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 1d9b2bc3886..fdbe43d1ba7 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -35,6 +35,7 @@ mod devtools;
pub(crate) mod document_loader;
#[macro_use]
mod dom;
+mod canvas_context;
mod canvas_state;
pub(crate) mod fetch;
mod init;