1use std::cell::Cell;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::reflector::reflect_dom_object;
11use script_bindings::weakref::WeakRef;
12use servo_canvas_traits::webgl::{
13 GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult,
14 WebGLVersion, webgl_channel,
15};
16
17use crate::dom::bindings::codegen::Bindings::EXTColorBufferHalfFloatBinding::EXTColorBufferHalfFloatConstants;
18use crate::dom::bindings::codegen::Bindings::WEBGLColorBufferFloatBinding::WEBGLColorBufferFloatConstants;
19use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
20use crate::dom::bindings::inheritance::Castable;
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::{DomRoot, MutNullableDom};
23use crate::dom::webgl::webglframebuffer::WebGLFramebuffer;
24use crate::dom::webgl::webglobject::WebGLObject;
25use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
26use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
27
28#[derive(JSTraceable, MallocSizeOf)]
29struct DroppableWebGLRenderbuffer {
30 context: WeakRef<WebGLRenderingContext>,
31 #[no_trace]
32 id: WebGLRenderbufferId,
33 is_deleted: Cell<bool>,
34}
35
36impl DroppableWebGLRenderbuffer {
37 fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
38 if let Some(root) = self.context.root() {
39 let result = root.sender().send(command, capture_webgl_backtrace());
40 if matches!(fallibility, Operation::Infallible) {
41 result.expect("Operation failed");
42 }
43 }
44 }
45
46 fn delete(&self, operation_fallibility: Operation) {
47 if !self.is_deleted.get() {
48 self.is_deleted.set(true);
49
50 if let Some(context) = self.context.root() {
60 if let Some(fb) = context.get_draw_framebuffer_slot().get() {
61 let _ = fb.detach_renderbuffer_by_id(&self.id);
62 }
63 if let Some(fb) = context.get_read_framebuffer_slot().get() {
64 let _ = fb.detach_renderbuffer_by_id(&self.id);
65 }
66 }
67
68 self.send_with_fallibility(
69 WebGLCommand::DeleteRenderbuffer(self.id),
70 operation_fallibility,
71 );
72 }
73 }
74}
75
76impl Drop for DroppableWebGLRenderbuffer {
77 fn drop(&mut self) {
78 self.delete(Operation::Fallible);
79 }
80}
81
82#[dom_struct]
83pub(crate) struct WebGLRenderbuffer {
84 webgl_object: WebGLObject,
85 ever_bound: Cell<bool>,
86 size: Cell<Option<(i32, i32)>>,
87 internal_format: Cell<Option<u32>>,
88 is_initialized: Cell<bool>,
89 attached_framebuffer: MutNullableDom<WebGLFramebuffer>,
90 droppable: DroppableWebGLRenderbuffer,
91}
92
93impl WebGLRenderbuffer {
94 fn new_inherited(context: &WebGLRenderingContext, id: WebGLRenderbufferId) -> Self {
95 Self {
96 webgl_object: WebGLObject::new_inherited(context),
97 ever_bound: Cell::new(false),
98 internal_format: Cell::new(None),
99 size: Cell::new(None),
100 is_initialized: Cell::new(false),
101 attached_framebuffer: Default::default(),
102 droppable: DroppableWebGLRenderbuffer {
103 context: WeakRef::new(context),
104 id,
105 is_deleted: Cell::new(false),
106 },
107 }
108 }
109
110 pub(crate) fn maybe_new(
111 cx: &mut JSContext,
112 context: &WebGLRenderingContext,
113 ) -> Option<DomRoot<Self>> {
114 let (sender, receiver) = webgl_channel().unwrap();
115 context.send_command(WebGLCommand::CreateRenderbuffer(sender));
116 receiver
117 .recv()
118 .unwrap()
119 .map(|id| WebGLRenderbuffer::new(cx, context, id))
120 }
121
122 pub(crate) fn new(
123 cx: &mut JSContext,
124 context: &WebGLRenderingContext,
125 id: WebGLRenderbufferId,
126 ) -> DomRoot<Self> {
127 reflect_dom_object(
128 cx,
129 Box::new(WebGLRenderbuffer::new_inherited(context, id)),
130 &*context.global(),
131 )
132 }
133}
134
135impl WebGLRenderbuffer {
136 pub(crate) fn id(&self) -> WebGLRenderbufferId {
137 self.droppable.id
138 }
139
140 pub(crate) fn size(&self) -> Option<(i32, i32)> {
141 self.size.get()
142 }
143
144 pub(crate) fn internal_format(&self) -> u32 {
145 self.internal_format.get().unwrap_or(constants::RGBA4)
146 }
147
148 pub(crate) fn mark_initialized(&self) {
149 self.is_initialized.set(true);
150 }
151
152 pub(crate) fn is_initialized(&self) -> bool {
153 self.is_initialized.get()
154 }
155
156 pub(crate) fn bind(&self, target: u32) {
157 self.ever_bound.set(true);
158 self.upcast()
159 .send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id())));
160 }
161
162 pub(crate) fn delete(&self, operation_fallibility: Operation) {
163 self.droppable.delete(operation_fallibility);
164 }
165
166 pub(crate) fn is_deleted(&self) -> bool {
167 self.droppable.is_deleted.get()
168 }
169
170 pub(crate) fn ever_bound(&self) -> bool {
171 self.ever_bound.get()
172 }
173
174 pub(crate) fn storage(
175 &self,
176 sample_count: i32,
177 internal_format: u32,
178 width: i32,
179 height: i32,
180 ) -> WebGLResult<()> {
181 let Some(context) = self.upcast().context() else {
182 return Err(WebGLError::ContextLost);
183 };
184
185 let webgl_version = context.webgl_version();
186
187 let actual_format = renderbuffer_format(&context, internal_format)?;
190
191 if webgl_version != WebGLVersion::WebGL1 {
192 let (sender, receiver) = webgl_channel().unwrap();
193 self.upcast()
194 .send_command(WebGLCommand::GetInternalFormatIntVec(
195 constants::RENDERBUFFER,
196 internal_format,
197 InternalFormatIntVec::Samples,
198 sender,
199 ));
200 let samples = receiver.recv().unwrap();
201 if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
202 return Err(WebGLError::InvalidOperation);
203 }
204 }
205
206 self.internal_format.set(Some(internal_format));
207 self.is_initialized.set(false);
208
209 if let Some(fb) = self.attached_framebuffer.get() {
210 fb.update_status();
211 }
212
213 let command = match sample_count {
214 0 => WebGLCommand::RenderbufferStorage(
215 constants::RENDERBUFFER,
216 actual_format,
217 width,
218 height,
219 ),
220 _ => WebGLCommand::RenderbufferStorageMultisample(
221 constants::RENDERBUFFER,
222 sample_count,
223 actual_format,
224 width,
225 height,
226 ),
227 };
228 self.upcast().send_command(command);
229
230 self.size.set(Some((width, height)));
231 Ok(())
232 }
233
234 pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
235 self.attached_framebuffer.set(Some(fb));
236 }
237
238 pub(crate) fn detach_from_framebuffer(&self) {
239 self.attached_framebuffer.set(None);
240 }
241}
242
243pub(crate) fn renderbuffer_format(
246 context: &WebGLRenderingContext,
247 internal_format: u32,
248) -> WebGLResult<u32> {
249 let webgl_version = context.webgl_version();
250 let is_gles = context.api_type() == GlType::Gles;
251 Ok(match internal_format {
252 constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
253 internal_format
254 },
255 constants::R8 |
256 constants::R8UI |
257 constants::R8I |
258 constants::R16UI |
259 constants::R16I |
260 constants::R32UI |
261 constants::R32I |
262 constants::RG8 |
263 constants::RG8UI |
264 constants::RG8I |
265 constants::RG16UI |
266 constants::RG16I |
267 constants::RG32UI |
268 constants::RG32I |
269 constants::RGB8 |
270 constants::RGBA8 |
271 constants::SRGB8_ALPHA8 |
272 constants::RGB10_A2 |
273 constants::RGBA8UI |
274 constants::RGBA8I |
275 constants::RGB10_A2UI |
276 constants::RGBA16UI |
277 constants::RGBA16I |
278 constants::RGBA32I |
279 constants::RGBA32UI |
280 constants::DEPTH_COMPONENT24 |
281 constants::DEPTH_COMPONENT32F |
282 constants::DEPTH24_STENCIL8 |
283 constants::DEPTH32F_STENCIL8 => match webgl_version {
284 WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
285 _ => internal_format,
286 },
287 constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
289 constants::RGB5_A1 => {
290 if is_gles {
292 constants::RGB5_A1
293 } else {
294 constants::RGBA8
295 }
296 },
297 constants::RGB565 => {
298 if is_gles {
300 constants::RGB565
301 } else {
302 constants::RGB8
303 }
304 },
305 EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
306 EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
307 if !context
308 .extension_manager()
309 .is_half_float_buffer_renderable()
310 {
311 return Err(WebGLError::InvalidEnum);
312 }
313 internal_format
314 },
315 WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
316 if !context.extension_manager().is_float_buffer_renderable() {
317 return Err(WebGLError::InvalidEnum);
318 }
319 internal_format
320 },
321 _ => return Err(WebGLError::InvalidEnum),
322 })
323}