1use std::cell::Cell;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::reflector::reflect_dom_object_with_cx;
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(associated_memory)]
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_with_cx(
128 Box::new(WebGLRenderbuffer::new_inherited(context, id)),
129 &*context.global(),
130 cx,
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 api_type: GlType,
177 sample_count: i32,
178 internal_format: u32,
179 width: i32,
180 height: i32,
181 ) -> WebGLResult<()> {
182 let Some(context) = self.upcast().context() else {
183 return Err(WebGLError::ContextLost);
184 };
185
186 let webgl_version = context.webgl_version();
187 let is_gles = api_type == GlType::Gles;
188
189 let actual_format = match internal_format {
192 constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
193 internal_format
194 },
195 constants::R8 |
196 constants::R8UI |
197 constants::R8I |
198 constants::R16UI |
199 constants::R16I |
200 constants::R32UI |
201 constants::R32I |
202 constants::RG8 |
203 constants::RG8UI |
204 constants::RG8I |
205 constants::RG16UI |
206 constants::RG16I |
207 constants::RG32UI |
208 constants::RG32I |
209 constants::RGB8 |
210 constants::RGBA8 |
211 constants::SRGB8_ALPHA8 |
212 constants::RGB10_A2 |
213 constants::RGBA8UI |
214 constants::RGBA8I |
215 constants::RGB10_A2UI |
216 constants::RGBA16UI |
217 constants::RGBA16I |
218 constants::RGBA32I |
219 constants::RGBA32UI |
220 constants::DEPTH_COMPONENT24 |
221 constants::DEPTH_COMPONENT32F |
222 constants::DEPTH24_STENCIL8 |
223 constants::DEPTH32F_STENCIL8 => match webgl_version {
224 WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
225 _ => internal_format,
226 },
227 constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
229 constants::RGB5_A1 => {
230 if is_gles {
232 constants::RGB5_A1
233 } else {
234 constants::RGBA8
235 }
236 },
237 constants::RGB565 => {
238 if is_gles {
240 constants::RGB565
241 } else {
242 constants::RGB8
243 }
244 },
245 EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
246 EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
247 if !context
248 .extension_manager()
249 .is_half_float_buffer_renderable()
250 {
251 return Err(WebGLError::InvalidEnum);
252 }
253 internal_format
254 },
255 WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
256 if !context.extension_manager().is_float_buffer_renderable() {
257 return Err(WebGLError::InvalidEnum);
258 }
259 internal_format
260 },
261 _ => return Err(WebGLError::InvalidEnum),
262 };
263
264 if webgl_version != WebGLVersion::WebGL1 {
265 let (sender, receiver) = webgl_channel().unwrap();
266 self.upcast()
267 .send_command(WebGLCommand::GetInternalFormatIntVec(
268 constants::RENDERBUFFER,
269 internal_format,
270 InternalFormatIntVec::Samples,
271 sender,
272 ));
273 let samples = receiver.recv().unwrap();
274 if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
275 return Err(WebGLError::InvalidOperation);
276 }
277 }
278
279 self.internal_format.set(Some(internal_format));
280 self.is_initialized.set(false);
281
282 if let Some(fb) = self.attached_framebuffer.get() {
283 fb.update_status();
284 }
285
286 let command = match sample_count {
287 0 => WebGLCommand::RenderbufferStorage(
288 constants::RENDERBUFFER,
289 actual_format,
290 width,
291 height,
292 ),
293 _ => WebGLCommand::RenderbufferStorageMultisample(
294 constants::RENDERBUFFER,
295 sample_count,
296 actual_format,
297 width,
298 height,
299 ),
300 };
301 self.upcast().send_command(command);
302
303 self.size.set(Some((width, height)));
304 Ok(())
305 }
306
307 pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
308 self.attached_framebuffer.set(Some(fb));
309 }
310
311 pub(crate) fn detach_from_framebuffer(&self) {
312 self.attached_framebuffer.set(None);
313 }
314}