Files
servo/components/script/dom/webgl/webglrenderbuffer.rs
Tim van der Lippe c8029ea092 script: Rename CanGc::note() to CanGc::deprecated_note() (#44081)
Per the suggestion in

https://servo.zulipchat.com/#narrow/channel/263398-general/topic/PSA.3A.20avoid.20new.20usages.20of.20CanGc.20whenever.20possible/near/583995807
to mark this method as deprecated and make clear it shouldn't be used
anymore, as alternatives exist.

Part of #40600

Testing: It compiles

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2026-04-10 06:07:52 +00:00

311 lines
11 KiB
Rust

/* 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/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use std::cell::Cell;
use dom_struct::dom_struct;
use script_bindings::weakref::WeakRef;
use servo_canvas_traits::webgl::{
GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult,
WebGLVersion, webgl_channel,
};
use crate::dom::bindings::codegen::Bindings::EXTColorBufferHalfFloatBinding::EXTColorBufferHalfFloatConstants;
use crate::dom::bindings::codegen::Bindings::WEBGLColorBufferFloatBinding::WEBGLColorBufferFloatConstants;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::webgl::webglframebuffer::WebGLFramebuffer;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
use crate::script_runtime::CanGc;
#[derive(JSTraceable, MallocSizeOf)]
struct DroppableWebGLRenderbuffer {
context: WeakRef<WebGLRenderingContext>,
#[no_trace]
id: WebGLRenderbufferId,
is_deleted: Cell<bool>,
}
impl DroppableWebGLRenderbuffer {
fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
if let Some(root) = self.context.root() {
let result = root.sender().send(command, capture_webgl_backtrace());
if matches!(fallibility, Operation::Infallible) {
result.expect("Operation failed");
}
}
}
fn delete(&self, operation_fallibility: Operation) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
/*
If a renderbuffer object is deleted while its image is attached to one or more
attachment points in a currently bound framebuffer object, then it is as if
FramebufferRenderbuffer had been called, with a renderbuffer of zero, for each
attachment point to which this image was attached in that framebuffer object.
In other words,the renderbuffer image is first detached from all attachment points
in that frame-buffer object.
- GLES 3.0, 4.4.2.3, "Attaching Renderbuffer Images to a Framebuffer"
*/
if let Some(context) = self.context.root() {
if let Some(fb) = context.get_draw_framebuffer_slot().get() {
let _ = fb.detach_renderbuffer_by_id(&self.id);
}
if let Some(fb) = context.get_read_framebuffer_slot().get() {
let _ = fb.detach_renderbuffer_by_id(&self.id);
}
}
self.send_with_fallibility(
WebGLCommand::DeleteRenderbuffer(self.id),
operation_fallibility,
);
}
}
}
impl Drop for DroppableWebGLRenderbuffer {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}
#[dom_struct(associated_memory)]
pub(crate) struct WebGLRenderbuffer {
webgl_object: WebGLObject,
ever_bound: Cell<bool>,
size: Cell<Option<(i32, i32)>>,
internal_format: Cell<Option<u32>>,
is_initialized: Cell<bool>,
attached_framebuffer: MutNullableDom<WebGLFramebuffer>,
droppable: DroppableWebGLRenderbuffer,
}
impl WebGLRenderbuffer {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLRenderbufferId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
ever_bound: Cell::new(false),
internal_format: Cell::new(None),
size: Cell::new(None),
is_initialized: Cell::new(false),
attached_framebuffer: Default::default(),
droppable: DroppableWebGLRenderbuffer {
context: WeakRef::new(context),
id,
is_deleted: Cell::new(false),
},
}
}
pub(crate) fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateRenderbuffer(sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLRenderbuffer::new(context, id, CanGc::deprecated_note()))
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: WebGLRenderbufferId,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLRenderbuffer::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
}
impl WebGLRenderbuffer {
pub(crate) fn id(&self) -> WebGLRenderbufferId {
self.droppable.id
}
pub(crate) fn size(&self) -> Option<(i32, i32)> {
self.size.get()
}
pub(crate) fn internal_format(&self) -> u32 {
self.internal_format.get().unwrap_or(constants::RGBA4)
}
pub(crate) fn mark_initialized(&self) {
self.is_initialized.set(true);
}
pub(crate) fn is_initialized(&self) -> bool {
self.is_initialized.get()
}
pub(crate) fn bind(&self, target: u32) {
self.ever_bound.set(true);
self.upcast()
.send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id())));
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
self.droppable.delete(operation_fallibility);
}
pub(crate) fn is_deleted(&self) -> bool {
self.droppable.is_deleted.get()
}
pub(crate) fn ever_bound(&self) -> bool {
self.ever_bound.get()
}
pub(crate) fn storage(
&self,
api_type: GlType,
sample_count: i32,
internal_format: u32,
width: i32,
height: i32,
) -> WebGLResult<()> {
let Some(context) = self.upcast().context() else {
return Err(WebGLError::ContextLost);
};
let webgl_version = context.webgl_version();
let is_gles = api_type == GlType::Gles;
// Validate the internal_format, and save it for completeness
// validation.
let actual_format = match internal_format {
constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
internal_format
},
constants::R8 |
constants::R8UI |
constants::R8I |
constants::R16UI |
constants::R16I |
constants::R32UI |
constants::R32I |
constants::RG8 |
constants::RG8UI |
constants::RG8I |
constants::RG16UI |
constants::RG16I |
constants::RG32UI |
constants::RG32I |
constants::RGB8 |
constants::RGBA8 |
constants::SRGB8_ALPHA8 |
constants::RGB10_A2 |
constants::RGBA8UI |
constants::RGBA8I |
constants::RGB10_A2UI |
constants::RGBA16UI |
constants::RGBA16I |
constants::RGBA32I |
constants::RGBA32UI |
constants::DEPTH_COMPONENT24 |
constants::DEPTH_COMPONENT32F |
constants::DEPTH24_STENCIL8 |
constants::DEPTH32F_STENCIL8 => match webgl_version {
WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
_ => internal_format,
},
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.8
constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
constants::RGB5_A1 => {
// 16-bit RGBA formats are not supported on desktop GL.
if is_gles {
constants::RGB5_A1
} else {
constants::RGBA8
}
},
constants::RGB565 => {
// RGB565 is not supported on desktop GL.
if is_gles {
constants::RGB565
} else {
constants::RGB8
}
},
EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
if !context
.extension_manager()
.is_half_float_buffer_renderable()
{
return Err(WebGLError::InvalidEnum);
}
internal_format
},
WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
if !context.extension_manager().is_float_buffer_renderable() {
return Err(WebGLError::InvalidEnum);
}
internal_format
},
_ => return Err(WebGLError::InvalidEnum),
};
if webgl_version != WebGLVersion::WebGL1 {
let (sender, receiver) = webgl_channel().unwrap();
self.upcast()
.send_command(WebGLCommand::GetInternalFormatIntVec(
constants::RENDERBUFFER,
internal_format,
InternalFormatIntVec::Samples,
sender,
));
let samples = receiver.recv().unwrap();
if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
return Err(WebGLError::InvalidOperation);
}
}
self.internal_format.set(Some(internal_format));
self.is_initialized.set(false);
if let Some(fb) = self.attached_framebuffer.get() {
fb.update_status();
}
let command = match sample_count {
0 => WebGLCommand::RenderbufferStorage(
constants::RENDERBUFFER,
actual_format,
width,
height,
),
_ => WebGLCommand::RenderbufferStorageMultisample(
constants::RENDERBUFFER,
sample_count,
actual_format,
width,
height,
),
};
self.upcast().send_command(command);
self.size.set(Some((width, height)));
Ok(())
}
pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
self.attached_framebuffer.set(Some(fb));
}
pub(crate) fn detach_from_framebuffer(&self) {
self.attached_framebuffer.set(None);
}
}