mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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>
This commit is contained in:
parent
084006abb6
commit
a6f19c0092
12 changed files with 317 additions and 210 deletions
77
components/script/canvas_context.rs
Normal file
77
components/script/canvas_context.rs
Normal file
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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/
|
||||
//
|
||||
|
|
|
@ -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>>>> {
|
||||
|
|
|
@ -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(),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
impl CanvasContext for GPUCanvasContext {
|
||||
type ID = WebGPUContextId;
|
||||
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))] // Crown is wrong here #35570
|
||||
fn context_id(&self) -> WebGPUContextId {
|
||||
self.context_id
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// <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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue