mirror of
https://github.com/servo/servo
synced 2026-05-09 00:22:16 +02:00
As started in https://github.com/servo/servo/issues/42168 let's report memory pressure for webgl objects. CanvasContexts report memory twice: once because of underlying texture object and once by themself, but that's okay as we also need to account for swapchain textures. Computing exact size would be to much work and code so we report rough estimations. Testing: None Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
286 lines
9.7 KiB
Rust
286 lines
9.7 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 canvas_traits::webgl::{
|
|
GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult,
|
|
WebGLVersion, webgl_channel,
|
|
};
|
|
use dom_struct::dom_struct;
|
|
|
|
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::script_runtime::CanGc;
|
|
|
|
#[dom_struct(associated_memory)]
|
|
pub(crate) struct WebGLRenderbuffer {
|
|
webgl_object: WebGLObject,
|
|
#[no_trace]
|
|
id: WebGLRenderbufferId,
|
|
ever_bound: Cell<bool>,
|
|
is_deleted: Cell<bool>,
|
|
size: Cell<Option<(i32, i32)>>,
|
|
internal_format: Cell<Option<u32>>,
|
|
is_initialized: Cell<bool>,
|
|
attached_framebuffer: MutNullableDom<WebGLFramebuffer>,
|
|
}
|
|
|
|
impl WebGLRenderbuffer {
|
|
fn new_inherited(context: &WebGLRenderingContext, id: WebGLRenderbufferId) -> Self {
|
|
Self {
|
|
webgl_object: WebGLObject::new_inherited(context),
|
|
id,
|
|
ever_bound: Cell::new(false),
|
|
is_deleted: Cell::new(false),
|
|
internal_format: Cell::new(None),
|
|
size: Cell::new(None),
|
|
is_initialized: Cell::new(false),
|
|
attached_framebuffer: Default::default(),
|
|
}
|
|
}
|
|
|
|
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::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.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) {
|
|
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"
|
|
*/
|
|
let webgl_object = self.upcast();
|
|
if let Some(context) = webgl_object.context() {
|
|
if let Some(fb) = context.get_draw_framebuffer_slot().get() {
|
|
let _ = fb.detach_renderbuffer(self);
|
|
}
|
|
if let Some(fb) = context.get_read_framebuffer_slot().get() {
|
|
let _ = fb.detach_renderbuffer(self);
|
|
}
|
|
}
|
|
|
|
webgl_object.send_with_fallibility(
|
|
WebGLCommand::DeleteRenderbuffer(self.id),
|
|
operation_fallibility,
|
|
);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn is_deleted(&self) -> bool {
|
|
self.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);
|
|
}
|
|
}
|
|
|
|
impl Drop for WebGLRenderbuffer {
|
|
fn drop(&mut self) {
|
|
self.delete(Operation::Fallible);
|
|
}
|
|
}
|