script/dom/webgl/
webglrenderbuffer.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
6use std::cell::Cell;
7
8use canvas_traits::webgl::{
9    GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult,
10    WebGLVersion, webgl_channel,
11};
12use dom_struct::dom_struct;
13
14use crate::dom::bindings::codegen::Bindings::EXTColorBufferHalfFloatBinding::EXTColorBufferHalfFloatConstants;
15use crate::dom::bindings::codegen::Bindings::WEBGLColorBufferFloatBinding::WEBGLColorBufferFloatConstants;
16use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
17use crate::dom::bindings::inheritance::Castable;
18use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
19use crate::dom::bindings::root::{DomRoot, MutNullableDom};
20use crate::dom::webgl::webglframebuffer::WebGLFramebuffer;
21use crate::dom::webgl::webglobject::WebGLObject;
22use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
23use crate::script_runtime::CanGc;
24
25#[dom_struct]
26pub(crate) struct WebGLRenderbuffer {
27    webgl_object: WebGLObject,
28    #[no_trace]
29    id: WebGLRenderbufferId,
30    ever_bound: Cell<bool>,
31    is_deleted: Cell<bool>,
32    size: Cell<Option<(i32, i32)>>,
33    internal_format: Cell<Option<u32>>,
34    is_initialized: Cell<bool>,
35    attached_framebuffer: MutNullableDom<WebGLFramebuffer>,
36}
37
38impl WebGLRenderbuffer {
39    fn new_inherited(context: &WebGLRenderingContext, id: WebGLRenderbufferId) -> Self {
40        Self {
41            webgl_object: WebGLObject::new_inherited(context),
42            id,
43            ever_bound: Cell::new(false),
44            is_deleted: Cell::new(false),
45            internal_format: Cell::new(None),
46            size: Cell::new(None),
47            is_initialized: Cell::new(false),
48            attached_framebuffer: Default::default(),
49        }
50    }
51
52    pub(crate) fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
53        let (sender, receiver) = webgl_channel().unwrap();
54        context.send_command(WebGLCommand::CreateRenderbuffer(sender));
55        receiver
56            .recv()
57            .unwrap()
58            .map(|id| WebGLRenderbuffer::new(context, id, CanGc::note()))
59    }
60
61    pub(crate) fn new(
62        context: &WebGLRenderingContext,
63        id: WebGLRenderbufferId,
64        can_gc: CanGc,
65    ) -> DomRoot<Self> {
66        reflect_dom_object(
67            Box::new(WebGLRenderbuffer::new_inherited(context, id)),
68            &*context.global(),
69            can_gc,
70        )
71    }
72}
73
74impl WebGLRenderbuffer {
75    pub(crate) fn id(&self) -> WebGLRenderbufferId {
76        self.id
77    }
78
79    pub(crate) fn size(&self) -> Option<(i32, i32)> {
80        self.size.get()
81    }
82
83    pub(crate) fn internal_format(&self) -> u32 {
84        self.internal_format.get().unwrap_or(constants::RGBA4)
85    }
86
87    pub(crate) fn mark_initialized(&self) {
88        self.is_initialized.set(true);
89    }
90
91    pub(crate) fn is_initialized(&self) -> bool {
92        self.is_initialized.get()
93    }
94
95    pub(crate) fn bind(&self, target: u32) {
96        self.ever_bound.set(true);
97        self.upcast()
98            .send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id)));
99    }
100
101    pub(crate) fn delete(&self, operation_fallibility: Operation) {
102        if !self.is_deleted.get() {
103            self.is_deleted.set(true);
104
105            /*
106            If a renderbuffer object is deleted while its image is attached to one or more
107            attachment points in a currently bound framebuffer object, then it is as if
108            FramebufferRenderbuffer had been called, with a renderbuffer of zero, for each
109            attachment point to which this image was attached in that framebuffer object.
110            In other words,the renderbuffer image is first detached from all attachment points
111            in that frame-buffer object.
112            - GLES 3.0, 4.4.2.3, "Attaching Renderbuffer Images to a Framebuffer"
113            */
114            let webgl_object = self.upcast();
115            if let Some(context) = webgl_object.context() {
116                if let Some(fb) = context.get_draw_framebuffer_slot().get() {
117                    let _ = fb.detach_renderbuffer(self);
118                }
119                if let Some(fb) = context.get_read_framebuffer_slot().get() {
120                    let _ = fb.detach_renderbuffer(self);
121                }
122            }
123
124            webgl_object.send_with_fallibility(
125                WebGLCommand::DeleteRenderbuffer(self.id),
126                operation_fallibility,
127            );
128        }
129    }
130
131    pub(crate) fn is_deleted(&self) -> bool {
132        self.is_deleted.get()
133    }
134
135    pub(crate) fn ever_bound(&self) -> bool {
136        self.ever_bound.get()
137    }
138
139    pub(crate) fn storage(
140        &self,
141        api_type: GlType,
142        sample_count: i32,
143        internal_format: u32,
144        width: i32,
145        height: i32,
146    ) -> WebGLResult<()> {
147        let Some(context) = self.upcast().context() else {
148            return Err(WebGLError::ContextLost);
149        };
150
151        let webgl_version = context.webgl_version();
152        let is_gles = api_type == GlType::Gles;
153
154        // Validate the internal_format, and save it for completeness
155        // validation.
156        let actual_format = match internal_format {
157            constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
158                internal_format
159            },
160            constants::R8 |
161            constants::R8UI |
162            constants::R8I |
163            constants::R16UI |
164            constants::R16I |
165            constants::R32UI |
166            constants::R32I |
167            constants::RG8 |
168            constants::RG8UI |
169            constants::RG8I |
170            constants::RG16UI |
171            constants::RG16I |
172            constants::RG32UI |
173            constants::RG32I |
174            constants::RGB8 |
175            constants::RGBA8 |
176            constants::SRGB8_ALPHA8 |
177            constants::RGB10_A2 |
178            constants::RGBA8UI |
179            constants::RGBA8I |
180            constants::RGB10_A2UI |
181            constants::RGBA16UI |
182            constants::RGBA16I |
183            constants::RGBA32I |
184            constants::RGBA32UI |
185            constants::DEPTH_COMPONENT24 |
186            constants::DEPTH_COMPONENT32F |
187            constants::DEPTH24_STENCIL8 |
188            constants::DEPTH32F_STENCIL8 => match webgl_version {
189                WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
190                _ => internal_format,
191            },
192            // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.8
193            constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
194            constants::RGB5_A1 => {
195                // 16-bit RGBA formats are not supported on desktop GL.
196                if is_gles {
197                    constants::RGB5_A1
198                } else {
199                    constants::RGBA8
200                }
201            },
202            constants::RGB565 => {
203                // RGB565 is not supported on desktop GL.
204                if is_gles {
205                    constants::RGB565
206                } else {
207                    constants::RGB8
208                }
209            },
210            EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
211            EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
212                if !context
213                    .extension_manager()
214                    .is_half_float_buffer_renderable()
215                {
216                    return Err(WebGLError::InvalidEnum);
217                }
218                internal_format
219            },
220            WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
221                if !context.extension_manager().is_float_buffer_renderable() {
222                    return Err(WebGLError::InvalidEnum);
223                }
224                internal_format
225            },
226            _ => return Err(WebGLError::InvalidEnum),
227        };
228
229        if webgl_version != WebGLVersion::WebGL1 {
230            let (sender, receiver) = webgl_channel().unwrap();
231            self.upcast()
232                .send_command(WebGLCommand::GetInternalFormatIntVec(
233                    constants::RENDERBUFFER,
234                    internal_format,
235                    InternalFormatIntVec::Samples,
236                    sender,
237                ));
238            let samples = receiver.recv().unwrap();
239            if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
240                return Err(WebGLError::InvalidOperation);
241            }
242        }
243
244        self.internal_format.set(Some(internal_format));
245        self.is_initialized.set(false);
246
247        if let Some(fb) = self.attached_framebuffer.get() {
248            fb.update_status();
249        }
250
251        let command = match sample_count {
252            0 => WebGLCommand::RenderbufferStorage(
253                constants::RENDERBUFFER,
254                actual_format,
255                width,
256                height,
257            ),
258            _ => WebGLCommand::RenderbufferStorageMultisample(
259                constants::RENDERBUFFER,
260                sample_count,
261                actual_format,
262                width,
263                height,
264            ),
265        };
266        self.upcast().send_command(command);
267
268        self.size.set(Some((width, height)));
269        Ok(())
270    }
271
272    pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
273        self.attached_framebuffer.set(Some(fb));
274    }
275
276    pub(crate) fn detach_from_framebuffer(&self) {
277        self.attached_framebuffer.set(None);
278    }
279}
280
281impl Drop for WebGLRenderbuffer {
282    fn drop(&mut self) {
283        self.delete(Operation::Fallible);
284    }
285}