script/dom/webgl/
webglrenderbuffer.rs1use 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 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 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 constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
194 constants::RGB5_A1 => {
195 if is_gles {
197 constants::RGB5_A1
198 } else {
199 constants::RGBA8
200 }
201 },
202 constants::RGB565 => {
203 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}