mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01: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
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue