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::<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 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 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 constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
191 constants::RGB5_A1 => {
192 if is_gles {
194 constants::RGB5_A1
195 } else {
196 constants::RGBA8
197 }
198 },
199 constants::RGB565 => {
200 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}