1use std::cell::Cell;
7
8use dom_struct::dom_struct;
9use script_bindings::reflector::reflect_dom_object;
10use script_bindings::weakref::WeakRef;
11use servo_canvas_traits::webgl::{
12 GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult,
13 WebGLVersion, webgl_channel,
14};
15
16use crate::dom::bindings::codegen::Bindings::EXTColorBufferHalfFloatBinding::EXTColorBufferHalfFloatConstants;
17use crate::dom::bindings::codegen::Bindings::WEBGLColorBufferFloatBinding::WEBGLColorBufferFloatConstants;
18use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
19use crate::dom::bindings::inheritance::Castable;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::{DomRoot, MutNullableDom};
22use crate::dom::webgl::webglframebuffer::WebGLFramebuffer;
23use crate::dom::webgl::webglobject::WebGLObject;
24use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
25use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
26use crate::script_runtime::CanGc;
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(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
111 let (sender, receiver) = webgl_channel().unwrap();
112 context.send_command(WebGLCommand::CreateRenderbuffer(sender));
113 receiver
114 .recv()
115 .unwrap()
116 .map(|id| WebGLRenderbuffer::new(context, id, CanGc::deprecated_note()))
117 }
118
119 pub(crate) fn new(
120 context: &WebGLRenderingContext,
121 id: WebGLRenderbufferId,
122 can_gc: CanGc,
123 ) -> DomRoot<Self> {
124 reflect_dom_object(
125 Box::new(WebGLRenderbuffer::new_inherited(context, id)),
126 &*context.global(),
127 can_gc,
128 )
129 }
130}
131
132impl WebGLRenderbuffer {
133 pub(crate) fn id(&self) -> WebGLRenderbufferId {
134 self.droppable.id
135 }
136
137 pub(crate) fn size(&self) -> Option<(i32, i32)> {
138 self.size.get()
139 }
140
141 pub(crate) fn internal_format(&self) -> u32 {
142 self.internal_format.get().unwrap_or(constants::RGBA4)
143 }
144
145 pub(crate) fn mark_initialized(&self) {
146 self.is_initialized.set(true);
147 }
148
149 pub(crate) fn is_initialized(&self) -> bool {
150 self.is_initialized.get()
151 }
152
153 pub(crate) fn bind(&self, target: u32) {
154 self.ever_bound.set(true);
155 self.upcast()
156 .send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id())));
157 }
158
159 pub(crate) fn delete(&self, operation_fallibility: Operation) {
160 self.droppable.delete(operation_fallibility);
161 }
162
163 pub(crate) fn is_deleted(&self) -> bool {
164 self.droppable.is_deleted.get()
165 }
166
167 pub(crate) fn ever_bound(&self) -> bool {
168 self.ever_bound.get()
169 }
170
171 pub(crate) fn storage(
172 &self,
173 api_type: GlType,
174 sample_count: i32,
175 internal_format: u32,
176 width: i32,
177 height: i32,
178 ) -> WebGLResult<()> {
179 let Some(context) = self.upcast().context() else {
180 return Err(WebGLError::ContextLost);
181 };
182
183 let webgl_version = context.webgl_version();
184 let is_gles = api_type == GlType::Gles;
185
186 let actual_format = match internal_format {
189 constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
190 internal_format
191 },
192 constants::R8 |
193 constants::R8UI |
194 constants::R8I |
195 constants::R16UI |
196 constants::R16I |
197 constants::R32UI |
198 constants::R32I |
199 constants::RG8 |
200 constants::RG8UI |
201 constants::RG8I |
202 constants::RG16UI |
203 constants::RG16I |
204 constants::RG32UI |
205 constants::RG32I |
206 constants::RGB8 |
207 constants::RGBA8 |
208 constants::SRGB8_ALPHA8 |
209 constants::RGB10_A2 |
210 constants::RGBA8UI |
211 constants::RGBA8I |
212 constants::RGB10_A2UI |
213 constants::RGBA16UI |
214 constants::RGBA16I |
215 constants::RGBA32I |
216 constants::RGBA32UI |
217 constants::DEPTH_COMPONENT24 |
218 constants::DEPTH_COMPONENT32F |
219 constants::DEPTH24_STENCIL8 |
220 constants::DEPTH32F_STENCIL8 => match webgl_version {
221 WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
222 _ => internal_format,
223 },
224 constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
226 constants::RGB5_A1 => {
227 if is_gles {
229 constants::RGB5_A1
230 } else {
231 constants::RGBA8
232 }
233 },
234 constants::RGB565 => {
235 if is_gles {
237 constants::RGB565
238 } else {
239 constants::RGB8
240 }
241 },
242 EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
243 EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
244 if !context
245 .extension_manager()
246 .is_half_float_buffer_renderable()
247 {
248 return Err(WebGLError::InvalidEnum);
249 }
250 internal_format
251 },
252 WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
253 if !context.extension_manager().is_float_buffer_renderable() {
254 return Err(WebGLError::InvalidEnum);
255 }
256 internal_format
257 },
258 _ => return Err(WebGLError::InvalidEnum),
259 };
260
261 if webgl_version != WebGLVersion::WebGL1 {
262 let (sender, receiver) = webgl_channel().unwrap();
263 self.upcast()
264 .send_command(WebGLCommand::GetInternalFormatIntVec(
265 constants::RENDERBUFFER,
266 internal_format,
267 InternalFormatIntVec::Samples,
268 sender,
269 ));
270 let samples = receiver.recv().unwrap();
271 if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
272 return Err(WebGLError::InvalidOperation);
273 }
274 }
275
276 self.internal_format.set(Some(internal_format));
277 self.is_initialized.set(false);
278
279 if let Some(fb) = self.attached_framebuffer.get() {
280 fb.update_status();
281 }
282
283 let command = match sample_count {
284 0 => WebGLCommand::RenderbufferStorage(
285 constants::RENDERBUFFER,
286 actual_format,
287 width,
288 height,
289 ),
290 _ => WebGLCommand::RenderbufferStorageMultisample(
291 constants::RENDERBUFFER,
292 sample_count,
293 actual_format,
294 width,
295 height,
296 ),
297 };
298 self.upcast().send_command(command);
299
300 self.size.set(Some((width, height)));
301 Ok(())
302 }
303
304 pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
305 self.attached_framebuffer.set(Some(fb));
306 }
307
308 pub(crate) fn detach_from_framebuffer(&self) {
309 self.attached_framebuffer.set(None);
310 }
311}