| /* 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] |
| 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::<WebGLObject>() |
| .context() |
| .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); |
| |
| let context = self.upcast::<WebGLObject>().context(); |
| |
| /* |
| 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(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); |
| } |
| |
| let cmd = WebGLCommand::DeleteRenderbuffer(self.id); |
| match operation_fallibility { |
| Operation::Fallible => context.send_command_ignored(cmd), |
| Operation::Infallible => context.send_command(cmd), |
| } |
| } |
| } |
| |
| 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 is_gles = api_type == GlType::Gles; |
| let webgl_version = self.upcast().context().webgl_version(); |
| |
| // 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 !self |
| .upcast() |
| .context() |
| .extension_manager() |
| .is_half_float_buffer_renderable() |
| { |
| return Err(WebGLError::InvalidEnum); |
| } |
| internal_format |
| }, |
| WEBGLColorBufferFloatConstants::RGBA32F_EXT => { |
| if !self |
| .upcast() |
| .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::<WebGLObject>().context().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::<WebGLObject>().context().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); |
| } |
| } |