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::<WebGLObject>()
98            .context()
99            .send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id)));
100    }
101
102    pub(crate) fn delete(&self, operation_fallibility: Operation) {
103        if !self.is_deleted.get() {
104            self.is_deleted.set(true);
105
106            let context = self.upcast::<WebGLObject>().context();
107
108            /*
109            If a renderbuffer object is deleted while its image is attached to one or more
110            attachment points in a currently bound framebuffer object, then it is as if
111            FramebufferRenderbuffer had been called, with a renderbuffer of zero, for each
112            attachment point to which this image was attached in that framebuffer object.
113            In other words,the renderbuffer image is first detached from all attachment points
114            in that frame-buffer object.
115            - GLES 3.0, 4.4.2.3, "Attaching Renderbuffer Images to a Framebuffer"
116            */
117            if let Some(fb) = context.get_draw_framebuffer_slot().get() {
118                let _ = fb.detach_renderbuffer(self);
119            }
120            if let Some(fb) = context.get_read_framebuffer_slot().get() {
121                let _ = fb.detach_renderbuffer(self);
122            }
123
124            let cmd = WebGLCommand::DeleteRenderbuffer(self.id);
125            match operation_fallibility {
126                Operation::Fallible => context.send_command_ignored(cmd),
127                Operation::Infallible => context.send_command(cmd),
128            }
129        }
130    }
131
132    pub(crate) fn is_deleted(&self) -> bool {
133        self.is_deleted.get()
134    }
135
136    pub(crate) fn ever_bound(&self) -> bool {
137        self.ever_bound.get()
138    }
139
140    pub(crate) fn storage(
141        &self,
142        api_type: GlType,
143        sample_count: i32,
144        internal_format: u32,
145        width: i32,
146        height: i32,
147    ) -> WebGLResult<()> {
148        let is_gles = api_type == GlType::Gles;
149        let webgl_version = self.upcast().context().webgl_version();
150
151        // Validate the internal_format, and save it for completeness
152        // validation.
153        let actual_format = match internal_format {
154            constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
155                internal_format
156            },
157            constants::R8 |
158            constants::R8UI |
159            constants::R8I |
160            constants::R16UI |
161            constants::R16I |
162            constants::R32UI |
163            constants::R32I |
164            constants::RG8 |
165            constants::RG8UI |
166            constants::RG8I |
167            constants::RG16UI |
168            constants::RG16I |
169            constants::RG32UI |
170            constants::RG32I |
171            constants::RGB8 |
172            constants::RGBA8 |
173            constants::SRGB8_ALPHA8 |
174            constants::RGB10_A2 |
175            constants::RGBA8UI |
176            constants::RGBA8I |
177            constants::RGB10_A2UI |
178            constants::RGBA16UI |
179            constants::RGBA16I |
180            constants::RGBA32I |
181            constants::RGBA32UI |
182            constants::DEPTH_COMPONENT24 |
183            constants::DEPTH_COMPONENT32F |
184            constants::DEPTH24_STENCIL8 |
185            constants::DEPTH32F_STENCIL8 => match webgl_version {
186                WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
187                _ => internal_format,
188            },
189            // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.8
190            constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
191            constants::RGB5_A1 => {
192                // 16-bit RGBA formats are not supported on desktop GL.
193                if is_gles {
194                    constants::RGB5_A1
195                } else {
196                    constants::RGBA8
197                }
198            },
199            constants::RGB565 => {
200                // RGB565 is not supported on desktop GL.
201                if is_gles {
202                    constants::RGB565
203                } else {
204                    constants::RGB8
205                }
206            },
207            EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
208            EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
209                if !self
210                    .upcast()
211                    .context()
212                    .extension_manager()
213                    .is_half_float_buffer_renderable()
214                {
215                    return Err(WebGLError::InvalidEnum);
216                }
217                internal_format
218            },
219            WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
220                if !self
221                    .upcast()
222                    .context()
223                    .extension_manager()
224                    .is_float_buffer_renderable()
225                {
226                    return Err(WebGLError::InvalidEnum);
227                }
228                internal_format
229            },
230            _ => return Err(WebGLError::InvalidEnum),
231        };
232
233        if webgl_version != WebGLVersion::WebGL1 {
234            let (sender, receiver) = webgl_channel().unwrap();
235            self.upcast::<WebGLObject>().context().send_command(
236                WebGLCommand::GetInternalFormatIntVec(
237                    constants::RENDERBUFFER,
238                    internal_format,
239                    InternalFormatIntVec::Samples,
240                    sender,
241                ),
242            );
243            let samples = receiver.recv().unwrap();
244            if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
245                return Err(WebGLError::InvalidOperation);
246            }
247        }
248
249        self.internal_format.set(Some(internal_format));
250        self.is_initialized.set(false);
251
252        if let Some(fb) = self.attached_framebuffer.get() {
253            fb.update_status();
254        }
255
256        let command = match sample_count {
257            0 => WebGLCommand::RenderbufferStorage(
258                constants::RENDERBUFFER,
259                actual_format,
260                width,
261                height,
262            ),
263            _ => WebGLCommand::RenderbufferStorageMultisample(
264                constants::RENDERBUFFER,
265                sample_count,
266                actual_format,
267                width,
268                height,
269            ),
270        };
271        self.upcast::<WebGLObject>().context().send_command(command);
272
273        self.size.set(Some((width, height)));
274        Ok(())
275    }
276
277    pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
278        self.attached_framebuffer.set(Some(fb));
279    }
280
281    pub(crate) fn detach_from_framebuffer(&self) {
282        self.attached_framebuffer.set(None);
283    }
284}
285
286impl Drop for WebGLRenderbuffer {
287    fn drop(&mut self) {
288        self.delete(Operation::Fallible);
289    }
290}