1use std::cell::Cell;
7
8use dom_struct::dom_struct;
9#[cfg(feature = "webxr")]
10use euclid::Size2D;
11use js::context::JSContext;
12use script_bindings::cell::DomRefCell;
13use script_bindings::reflector::reflect_dom_object;
14use script_bindings::weakref::WeakRef;
15use servo_canvas_traits::webgl::{
16 WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLFramebufferId,
17 WebGLRenderbufferId, WebGLResult, WebGLTextureId, WebGLVersion, webgl_channel,
18};
19#[cfg(feature = "webxr")]
20use webxr_api::Viewport;
21
22use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::reflector::DomGlobal;
25#[cfg(feature = "webxr")]
26use crate::dom::bindings::root::MutNullableDom;
27use crate::dom::bindings::root::{Dom, DomRoot};
28use crate::dom::webgl::webglobject::WebGLObject;
29use crate::dom::webgl::webglrenderbuffer::WebGLRenderbuffer;
30use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
31use crate::dom::webgl::webgltexture::WebGLTexture;
32#[cfg(feature = "webxr")]
33use crate::dom::xrsession::XRSession;
34
35pub(crate) enum CompleteForRendering {
36 Complete,
37 Incomplete,
38 MissingColorAttachment,
39}
40
41#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
42#[derive(Clone, JSTraceable, MallocSizeOf)]
43enum WebGLFramebufferAttachment {
44 Renderbuffer(Dom<WebGLRenderbuffer>),
45 Texture {
46 texture: Dom<WebGLTexture>,
47 level: i32,
48 },
49}
50
51impl WebGLFramebufferAttachment {
52 fn needs_initialization(&self) -> bool {
53 match *self {
54 WebGLFramebufferAttachment::Renderbuffer(ref r) => !r.is_initialized(),
55 WebGLFramebufferAttachment::Texture { .. } => false,
56 }
57 }
58
59 fn mark_initialized(&self) {
60 match *self {
61 WebGLFramebufferAttachment::Renderbuffer(ref r) => r.mark_initialized(),
62 WebGLFramebufferAttachment::Texture { .. } => (),
63 }
64 }
65
66 fn root(&self) -> WebGLFramebufferAttachmentRoot {
67 match self {
68 WebGLFramebufferAttachment::Renderbuffer(rb) => {
69 WebGLFramebufferAttachmentRoot::Renderbuffer(DomRoot::from_ref(rb))
70 },
71 WebGLFramebufferAttachment::Texture { texture, .. } => {
72 WebGLFramebufferAttachmentRoot::Texture(DomRoot::from_ref(texture))
73 },
74 }
75 }
76
77 fn detach(&self) {
78 match self {
79 WebGLFramebufferAttachment::Renderbuffer(rb) => rb.detach_from_framebuffer(),
80 WebGLFramebufferAttachment::Texture { texture, .. } => {
81 texture.detach_from_framebuffer()
82 },
83 }
84 }
85}
86
87#[derive(Clone, JSTraceable, MallocSizeOf)]
88pub(crate) enum WebGLFramebufferAttachmentRoot {
89 Renderbuffer(DomRoot<WebGLRenderbuffer>),
90 Texture(DomRoot<WebGLTexture>),
91}
92
93#[derive(JSTraceable, MallocSizeOf)]
94struct DroppableWebGLFramebuffer {
95 #[no_trace]
96 id: WebGLFramebufferId,
97 is_deleted: Cell<bool>,
98 context: WeakRef<WebGLRenderingContext>,
99}
100
101impl DroppableWebGLFramebuffer {
102 fn new(
103 id: WebGLFramebufferId,
104 is_deleted: Cell<bool>,
105 context: WeakRef<WebGLRenderingContext>,
106 ) -> Self {
107 Self {
108 id,
109 is_deleted,
110 context,
111 }
112 }
113}
114
115impl DroppableWebGLFramebuffer {
116 pub(crate) fn id(&self) -> WebGLFramebufferId {
117 self.id
118 }
119 pub(crate) fn is_deleted(&self) -> bool {
120 self.is_deleted.get()
121 }
122
123 pub(crate) fn set_deleted(&self, deleted: bool) {
124 self.is_deleted.set(deleted);
125 }
126
127 pub(crate) fn delete(&self, operation_fallibility: Operation) {
128 if !self.is_deleted() {
129 self.set_deleted(true);
130 if let Some(context) = self.context.root() {
131 let cmd = WebGLCommand::DeleteFramebuffer(self.id());
132 match operation_fallibility {
133 Operation::Fallible => context.send_command_ignored(cmd),
134 Operation::Infallible => context.send_command(cmd),
135 }
136 }
137 }
138 }
139}
140
141impl Drop for DroppableWebGLFramebuffer {
142 fn drop(&mut self) {
143 self.delete(Operation::Fallible);
144 }
145}
146
147#[dom_struct]
148pub(crate) struct WebGLFramebuffer {
149 webgl_object: WebGLObject,
150 #[no_trace]
151 webgl_version: WebGLVersion,
152 target: Cell<Option<u32>>,
153 size: Cell<Option<(i32, i32)>>,
154 status: Cell<u32>,
155 colors: Vec<DomRefCell<Option<WebGLFramebufferAttachment>>>,
158 depth: DomRefCell<Option<WebGLFramebufferAttachment>>,
159 stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
160 depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
161 color_read_buffer: Cell<u32>,
162 color_draw_buffers: DomRefCell<Vec<u32>>,
163 is_initialized: Cell<bool>,
164 #[cfg(feature = "webxr")]
167 xr_session: MutNullableDom<XRSession>,
168 droppable: DroppableWebGLFramebuffer,
169}
170
171impl WebGLFramebuffer {
172 fn new_inherited(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> Self {
173 Self {
174 webgl_object: WebGLObject::new_inherited(context),
175 webgl_version: context.webgl_version(),
176 target: Cell::new(None),
177 size: Cell::new(None),
178 status: Cell::new(constants::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
179 colors: vec![DomRefCell::new(None); context.limits().max_color_attachments as usize],
180 depth: DomRefCell::new(None),
181 stencil: DomRefCell::new(None),
182 depthstencil: DomRefCell::new(None),
183 color_read_buffer: Cell::new(constants::COLOR_ATTACHMENT0),
184 color_draw_buffers: DomRefCell::new(vec![constants::COLOR_ATTACHMENT0]),
185 is_initialized: Cell::new(false),
186 #[cfg(feature = "webxr")]
187 xr_session: Default::default(),
188 droppable: DroppableWebGLFramebuffer::new(id, Cell::new(false), WeakRef::new(context)),
189 }
190 }
191
192 pub(crate) fn maybe_new(
193 cx: &mut JSContext,
194 context: &WebGLRenderingContext,
195 ) -> Option<DomRoot<Self>> {
196 let (sender, receiver) = webgl_channel().unwrap();
197 context.send_command(WebGLCommand::CreateFramebuffer(sender));
198 let id = receiver.recv().unwrap()?;
199 let framebuffer = WebGLFramebuffer::new(cx, context, id);
200 Some(framebuffer)
201 }
202
203 #[cfg(feature = "webxr")]
206 pub(crate) fn maybe_new_webxr(
207 cx: &mut JSContext,
208 session: &XRSession,
209 context: &WebGLRenderingContext,
210 size: Size2D<i32, Viewport>,
211 ) -> Option<DomRoot<Self>> {
212 let framebuffer = Self::maybe_new(cx, context)?;
213 framebuffer.size.set(Some((size.width, size.height)));
214 framebuffer.status.set(constants::FRAMEBUFFER_COMPLETE);
215 framebuffer.xr_session.set(Some(session));
216 Some(framebuffer)
217 }
218
219 pub(crate) fn new(
220 cx: &mut JSContext,
221 context: &WebGLRenderingContext,
222 id: WebGLFramebufferId,
223 ) -> DomRoot<Self> {
224 reflect_dom_object(
225 cx,
226 Box::new(WebGLFramebuffer::new_inherited(context, id)),
227 &*context.global(),
228 )
229 }
230}
231
232impl WebGLFramebuffer {
233 pub(crate) fn id(&self) -> WebGLFramebufferId {
234 self.droppable.id()
235 }
236
237 #[cfg(feature = "webxr")]
238 fn is_in_xr_session(&self) -> bool {
239 self.xr_session.get().is_some()
240 }
241
242 #[cfg(not(feature = "webxr"))]
243 fn is_in_xr_session(&self) -> bool {
244 false
245 }
246
247 pub(crate) fn validate_transparent(&self) -> WebGLResult<()> {
248 if self.is_in_xr_session() {
249 Err(WebGLError::InvalidOperation)
250 } else {
251 Ok(())
252 }
253 }
254
255 pub(crate) fn bind(&self, target: u32) {
256 if !self.is_in_xr_session() {
257 self.update_status();
261 }
262
263 self.target.set(Some(target));
264 self.upcast().send_command(WebGLCommand::BindFramebuffer(
265 target,
266 WebGLFramebufferBindingRequest::Explicit(self.id()),
267 ));
268 }
269
270 pub(crate) fn delete(&self, operation_fallibility: Operation) {
271 self.droppable.delete(operation_fallibility);
272 }
273
274 pub(crate) fn is_deleted(&self) -> bool {
275 self.droppable.is_deleted()
279 }
280
281 pub(crate) fn size(&self) -> Option<(i32, i32)> {
282 self.size.get()
283 }
284
285 pub(crate) fn get_attachment_formats(
286 &self,
287 ) -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
288 if self.check_status() != constants::FRAMEBUFFER_COMPLETE {
289 return Err(WebGLError::InvalidFramebufferOperation);
290 }
291 let color = match self.attachment(constants::COLOR_ATTACHMENT0) {
292 Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => Some(rb.internal_format()),
293 _ => None,
294 };
295 let depth = match self.attachment(constants::DEPTH_ATTACHMENT) {
296 Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => Some(rb.internal_format()),
297 _ => None,
298 };
299 let stencil = match self.attachment(constants::STENCIL_ATTACHMENT) {
300 Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => Some(rb.internal_format()),
301 _ => None,
302 };
303 Ok((color, depth, stencil))
304 }
305
306 fn check_attachment_constraints<'a>(
307 &self,
308 attachment: &Option<WebGLFramebufferAttachment>,
309 mut constraints: impl Iterator<Item = &'a u32>,
310 fb_size: &mut Option<(i32, i32)>,
311 ) -> Result<(), u32> {
312 let (format, size) = match attachment {
314 Some(WebGLFramebufferAttachment::Renderbuffer(att_rb)) => {
315 (Some(att_rb.internal_format()), att_rb.size())
316 },
317 Some(WebGLFramebufferAttachment::Texture {
318 texture: att_tex,
319 level,
320 }) => match att_tex.image_info_at_face(0, *level as u32) {
321 Some(info) => (
322 Some(info.internal_format().as_gl_constant()),
323 Some((info.width() as i32, info.height() as i32)),
324 ),
325 None => return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
326 },
327 None => (None, None),
328 };
329
330 if size.is_some() {
333 if fb_size.is_some() && size != *fb_size {
334 return Err(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
335 } else {
336 *fb_size = size;
337 }
338 }
339
340 if let Some(format) = format &&
341 constraints.all(|c| *c != format)
342 {
343 return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
344 }
345
346 Ok(())
347 }
348
349 pub(crate) fn update_status(&self) {
350 let z = self.depth.borrow();
351 let s = self.stencil.borrow();
352 let zs = self.depthstencil.borrow();
353 let has_z = z.is_some();
354 let has_s = s.is_some();
355 let has_zs = zs.is_some();
356
357 let is_supported = match self.webgl_version {
358 WebGLVersion::WebGL1 => !(has_zs && (has_z || has_s)) && !(has_z && has_s),
372
373 WebGLVersion::WebGL2 => {
381 use WebGLFramebufferAttachment::{Renderbuffer, Texture};
382 match (&*z, &*s) {
383 (Some(Renderbuffer(a)), Some(Renderbuffer(b))) => a.id() == b.id(),
384 (Some(Texture { texture: a, .. }), Some(Texture { texture: b, .. })) => {
385 a.id() == b.id()
386 },
387 _ => !has_z || !has_s,
388 }
389 },
390 };
391 if !is_supported {
392 return self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
393 }
394
395 let mut fb_size = None;
396
397 let attachments = [&*z, &*s, &*zs];
398 let webgl1_attachment_constraints = &[
399 &[
400 constants::DEPTH_COMPONENT16,
401 constants::DEPTH_COMPONENT24,
402 constants::DEPTH_COMPONENT32F,
403 constants::DEPTH24_STENCIL8,
404 constants::DEPTH32F_STENCIL8,
405 ][..],
406 &[
407 constants::STENCIL_INDEX8,
408 constants::DEPTH24_STENCIL8,
409 constants::DEPTH32F_STENCIL8,
410 ][..],
411 &[constants::DEPTH_STENCIL][..],
412 ];
413 let webgl2_attachment_constraints = &[
414 &[constants::DEPTH_STENCIL][..],
415 &[constants::DEPTH_STENCIL][..],
416 &[][..],
417 ];
418 let empty_attachment_constrains = &[&[][..], &[][..], &[][..]];
419 let extra_attachment_constraints = match self.webgl_version {
420 WebGLVersion::WebGL1 => empty_attachment_constrains,
421 WebGLVersion::WebGL2 => webgl2_attachment_constraints,
422 };
423 let attachment_constraints = webgl1_attachment_constraints
424 .iter()
425 .zip(extra_attachment_constraints.iter())
426 .map(|(a, b)| a.iter().chain(b.iter()));
427
428 for (attachment, constraints) in attachments.iter().zip(attachment_constraints) {
429 if let Err(errnum) =
430 self.check_attachment_constraints(attachment, constraints, &mut fb_size)
431 {
432 return self.status.set(errnum);
433 }
434 }
435
436 let webgl1_color_constraints = &[
437 constants::RGB,
438 constants::RGB565,
439 constants::RGB5_A1,
440 constants::RGBA,
441 constants::RGBA4,
442 ][..];
443 let webgl2_color_constraints = &[
444 constants::ALPHA,
445 constants::LUMINANCE,
446 constants::LUMINANCE_ALPHA,
447 constants::R11F_G11F_B10F,
448 constants::R16F,
449 constants::R16I,
450 constants::R16UI,
451 constants::R32F,
452 constants::R32I,
453 constants::R32UI,
454 constants::R8,
455 constants::R8_SNORM,
456 constants::R8I,
457 constants::R8UI,
458 constants::RG16F,
459 constants::RG16I,
460 constants::RG16UI,
461 constants::RG32F,
462 constants::RG32I,
463 constants::RG32UI,
464 constants::RG8,
465 constants::RG8_SNORM,
466 constants::RG8I,
467 constants::RG8UI,
468 constants::RGB10_A2,
469 constants::RGB10_A2UI,
470 constants::RGB16F,
471 constants::RGB16I,
472 constants::RGB16UI,
473 constants::RGB32F,
474 constants::RGB32I,
475 constants::RGB32UI,
476 constants::RGB8,
477 constants::RGB8_SNORM,
478 constants::RGB8I,
479 constants::RGB8UI,
480 constants::RGB9_E5,
481 constants::RGBA16F,
482 constants::RGBA16I,
483 constants::RGBA16UI,
484 constants::RGBA32F,
485 constants::RGBA32I,
486 constants::RGBA32UI,
487 constants::RGBA8,
488 constants::RGBA8_SNORM,
489 constants::RGBA8I,
490 constants::RGBA8UI,
491 constants::SRGB8,
492 constants::SRGB8_ALPHA8,
493 ][..];
494 let empty_color_constrains = &[][..];
495 let extra_color_constraints = match self.webgl_version {
496 WebGLVersion::WebGL1 => empty_color_constrains,
497 WebGLVersion::WebGL2 => webgl2_color_constraints,
498 };
499 let color_constraints = webgl1_color_constraints
500 .iter()
501 .chain(extra_color_constraints.iter());
502
503 let has_c = self.colors.iter().any(|att| att.borrow().is_some());
504 for attachment in self.colors.iter() {
505 let attachment = attachment.borrow();
506 let constraints = color_constraints.clone();
507 if let Err(errnum) =
508 self.check_attachment_constraints(&attachment, constraints, &mut fb_size)
509 {
510 return self.status.set(errnum);
511 }
512 }
513
514 self.size.set(fb_size);
515
516 if has_c || has_z || has_zs || has_s {
517 if self.size.get().is_some_and(|(w, h)| w != 0 && h != 0) {
518 self.status.set(constants::FRAMEBUFFER_COMPLETE);
519 } else {
520 self.status
521 .set(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
522 }
523 } else {
524 self.status
525 .set(constants::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
526 }
527 }
528
529 pub(crate) fn check_status(&self) -> u32 {
530 #[cfg(feature = "webxr")]
533 if let Some(xr_session) = self.xr_session.get() {
534 return if xr_session.is_outside_raf() {
535 constants::FRAMEBUFFER_UNSUPPORTED
536 } else {
537 constants::FRAMEBUFFER_COMPLETE
538 };
539 }
540
541 self.status.get()
542 }
546
547 pub(crate) fn check_status_for_rendering(&self) -> CompleteForRendering {
548 let result = self.check_status();
549 if result != constants::FRAMEBUFFER_COMPLETE {
550 return CompleteForRendering::Incomplete;
551 }
552
553 #[cfg(feature = "webxr")]
556 if self.xr_session.get().is_some() {
557 return CompleteForRendering::Complete;
558 }
559
560 if self.colors.iter().all(|att| att.borrow().is_none()) {
561 return CompleteForRendering::MissingColorAttachment;
562 }
563
564 if !self.is_initialized.get() {
565 let attachments = [
566 (&self.depth, constants::DEPTH_BUFFER_BIT),
567 (&self.stencil, constants::STENCIL_BUFFER_BIT),
568 (
569 &self.depthstencil,
570 constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT,
571 ),
572 ];
573 let colors = self
574 .colors
575 .iter()
576 .map(|attachment| (attachment, constants::COLOR_BUFFER_BIT));
577 let mut clear_bits = 0;
579 for (attachment, bits) in attachments.into_iter().chain(colors.clone()) {
580 if let Some(ref att) = *attachment.borrow() &&
581 att.needs_initialization()
582 {
583 clear_bits |= bits;
584 }
585 }
586 for (attachment, _) in attachments.into_iter().chain(colors) {
587 if let Some(ref att) = *attachment.borrow() {
588 att.mark_initialized();
589 }
590 }
591
592 if let Some(context) = self.upcast().context() {
593 context.initialize_framebuffer(clear_bits);
594 }
595
596 self.is_initialized.set(true);
597 }
598
599 CompleteForRendering::Complete
604 }
605
606 pub(crate) fn initialize_for_reading(&self, draw_framebuffer: Option<&WebGLFramebuffer>) {
609 if self.is_initialized.get() {
610 return;
611 }
612 let bind_draw = |request| {
613 self.upcast().send_command(WebGLCommand::BindFramebuffer(
614 constants::DRAW_FRAMEBUFFER,
615 request,
616 ))
617 };
618 bind_draw(WebGLFramebufferBindingRequest::Explicit(self.id()));
619 self.check_status_for_rendering();
620 bind_draw(
621 draw_framebuffer.map_or(WebGLFramebufferBindingRequest::Default, |fb| {
622 WebGLFramebufferBindingRequest::Explicit(fb.id())
623 }),
624 );
625 }
626
627 pub(crate) fn renderbuffer(
628 &self,
629 attachment: u32,
630 rb: Option<&WebGLRenderbuffer>,
631 ) -> WebGLResult<()> {
632 self.validate_transparent()?;
635
636 let binding = self
637 .attachment_binding(attachment)
638 .ok_or(WebGLError::InvalidEnum)?;
639
640 let rb_id = match rb {
641 Some(rb) => {
642 if !rb.ever_bound() {
643 return Err(WebGLError::InvalidOperation);
644 }
645 *binding.borrow_mut() =
646 Some(WebGLFramebufferAttachment::Renderbuffer(Dom::from_ref(rb)));
647 self.mirror_alias(binding, attachment);
648 rb.attach_to_framebuffer(self);
649 Some(rb.id())
650 },
651
652 _ => None,
653 };
654
655 self.upcast()
656 .send_command(WebGLCommand::FramebufferRenderbuffer(
657 self.target.get().unwrap(),
658 attachment,
659 constants::RENDERBUFFER,
660 rb_id,
661 ));
662
663 if rb.is_none() {
664 self.detach_binding(binding, attachment)?;
665 }
666
667 self.update_status();
668 self.is_initialized.set(false);
669 Ok(())
670 }
671
672 fn detach_binding(
673 &self,
674 binding: &DomRefCell<Option<WebGLFramebufferAttachment>>,
675 attachment: u32,
676 ) -> WebGLResult<()> {
677 self.validate_transparent()?;
680
681 for slot in std::iter::once(binding).chain(self.aliased_binding(attachment)) {
682 if let Some(att) = &*slot.borrow() {
683 att.detach();
684 }
685 *slot.borrow_mut() = None;
686 }
687 if INTERESTING_ATTACHMENT_POINTS.contains(&attachment) {
688 self.reattach_depth_stencil()?;
689 }
690 Ok(())
691 }
692
693 fn attachment_binding(
694 &self,
695 attachment: u32,
696 ) -> Option<&DomRefCell<Option<WebGLFramebufferAttachment>>> {
697 match attachment {
698 constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
699 let idx = attachment - constants::COLOR_ATTACHMENT0;
700 self.colors.get(idx as usize)
701 },
702 constants::DEPTH_ATTACHMENT => Some(&self.depth),
703 constants::STENCIL_ATTACHMENT => Some(&self.stencil),
704 constants::DEPTH_STENCIL_ATTACHMENT => match self.webgl_version {
705 WebGLVersion::WebGL1 => Some(&self.depthstencil),
706 WebGLVersion::WebGL2 => Some(&self.depth),
707 },
708 _ => None,
709 }
710 }
711
712 fn aliased_binding(
715 &self,
716 attachment: u32,
717 ) -> Option<&DomRefCell<Option<WebGLFramebufferAttachment>>> {
718 (self.webgl_version == WebGLVersion::WebGL2 &&
719 attachment == constants::DEPTH_STENCIL_ATTACHMENT)
720 .then_some(&self.stencil)
721 }
722
723 fn mirror_alias(
724 &self,
725 binding: &DomRefCell<Option<WebGLFramebufferAttachment>>,
726 attachment: u32,
727 ) {
728 if let Some(alias) = self.aliased_binding(attachment) {
729 *alias.borrow_mut() = binding.borrow().clone();
730 }
731 }
732
733 fn reattach_depth_stencil(&self) -> WebGLResult<()> {
734 self.validate_transparent()?;
737
738 if self.webgl_version == WebGLVersion::WebGL2 {
740 return Ok(());
741 }
742
743 let reattach = |attachment: &WebGLFramebufferAttachment, attachment_point| {
744 let webgl_object = self.upcast();
745 match *attachment {
746 WebGLFramebufferAttachment::Renderbuffer(ref rb) => {
747 rb.attach_to_framebuffer(self);
748 webgl_object.send_command(WebGLCommand::FramebufferRenderbuffer(
749 self.target.get().unwrap(),
750 attachment_point,
751 constants::RENDERBUFFER,
752 Some(rb.id()),
753 ));
754 },
755 WebGLFramebufferAttachment::Texture { ref texture, level } => {
756 texture.attach_to_framebuffer(self);
757 webgl_object.send_command(WebGLCommand::FramebufferTexture2D(
758 self.target.get().unwrap(),
759 attachment_point,
760 texture.target().expect("missing texture target"),
761 Some(texture.id()),
762 level,
763 ));
764 },
765 }
766 };
767
768 if let Some(ref depth) = *self.depth.borrow() {
773 reattach(depth, constants::DEPTH_ATTACHMENT);
774 }
775 if let Some(ref stencil) = *self.stencil.borrow() {
776 reattach(stencil, constants::STENCIL_ATTACHMENT);
777 }
778 if let Some(ref depth_stencil) = *self.depthstencil.borrow() {
779 reattach(depth_stencil, constants::DEPTH_STENCIL_ATTACHMENT);
780 }
781 Ok(())
782 }
783
784 pub(crate) fn attachment(&self, attachment: u32) -> Option<WebGLFramebufferAttachmentRoot> {
785 let binding = self.attachment_binding(attachment)?;
786 binding
787 .borrow()
788 .as_ref()
789 .map(WebGLFramebufferAttachment::root)
790 }
791
792 pub(crate) fn texture2d(
793 &self,
794 attachment: u32,
795 textarget: u32,
796 texture: Option<&WebGLTexture>,
797 level: i32,
798 ) -> WebGLResult<()> {
799 self.validate_transparent()?;
802 if let Some(texture) = texture {
803 let is_cube = match textarget {
815 constants::TEXTURE_2D => false,
816
817 constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
818 constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
819 constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
820 constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
821 constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
822 constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,
823
824 _ => return Err(WebGLError::InvalidEnum),
825 };
826
827 match texture.target() {
828 Some(constants::TEXTURE_CUBE_MAP) if is_cube => {},
829 Some(_) if !is_cube => {},
830 _ => return Err(WebGLError::InvalidOperation),
831 }
832
833 let Some(context) = self.upcast().context() else {
834 return Err(WebGLError::ContextLost);
835 };
836 let max_tex_size = if is_cube {
837 context.limits().max_cube_map_tex_size
838 } else {
839 context.limits().max_tex_size
840 };
841 if level < 0 || level as u32 > max_tex_size.ilog2() {
842 return Err(WebGLError::InvalidValue);
843 }
844 }
845 self.texture2d_even_if_opaque(attachment, textarget, texture, level)
846 }
847
848 pub(crate) fn texture2d_even_if_opaque(
849 &self,
850 attachment: u32,
851 textarget: u32,
852 texture: Option<&WebGLTexture>,
853 level: i32,
854 ) -> WebGLResult<()> {
855 let binding = self
856 .attachment_binding(attachment)
857 .ok_or(WebGLError::InvalidEnum)?;
858
859 let tex_id = match texture {
860 Some(texture) => {
863 *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
864 texture: Dom::from_ref(texture),
865 level,
866 });
867 self.mirror_alias(binding, attachment);
868 texture.attach_to_framebuffer(self);
869
870 Some(texture.id())
871 },
872
873 _ => None,
874 };
875
876 self.upcast()
877 .send_command(WebGLCommand::FramebufferTexture2D(
878 self.target.get().unwrap(),
879 attachment,
880 textarget,
881 tex_id,
882 level,
883 ));
884
885 if texture.is_none() {
886 self.detach_binding(binding, attachment)?;
887 }
888
889 self.update_status();
890 self.is_initialized.set(false);
891 Ok(())
892 }
893
894 pub(crate) fn texture_layer(
895 &self,
896 attachment: u32,
897 texture: Option<&WebGLTexture>,
898 level: i32,
899 layer: i32,
900 ) -> WebGLResult<()> {
901 let binding = self
902 .attachment_binding(attachment)
903 .ok_or(WebGLError::InvalidEnum)?;
904
905 let Some(context) = self.upcast().context() else {
906 return Err(WebGLError::ContextLost);
907 };
908
909 let tex_id = match texture {
910 Some(texture) => {
911 let (max_level, max_layer) = match texture.target() {
912 Some(constants::TEXTURE_3D) => (
913 context.limits().max_3d_texture_size.ilog2(),
914 context.limits().max_3d_texture_size - 1,
915 ),
916 Some(constants::TEXTURE_2D) => (
917 context.limits().max_tex_size.ilog2(),
918 context.limits().max_array_texture_layers - 1,
919 ),
920 _ => return Err(WebGLError::InvalidOperation),
921 };
922
923 if level < 0 || level as u32 >= max_level {
924 return Err(WebGLError::InvalidValue);
925 }
926 if layer < 0 || layer as u32 >= max_layer {
927 return Err(WebGLError::InvalidValue);
928 }
929
930 *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
931 texture: Dom::from_ref(texture),
932 level,
933 });
934 self.mirror_alias(binding, attachment);
935 texture.attach_to_framebuffer(self);
936
937 Some(texture.id())
938 },
939 _ => None,
940 };
941
942 context.send_command(WebGLCommand::FramebufferTextureLayer(
943 self.target.get().unwrap(),
944 attachment,
945 tex_id,
946 level,
947 layer,
948 ));
949 Ok(())
950 }
951
952 fn with_matching_renderbuffers_id<F>(&self, rb_id: &WebGLRenderbufferId, mut closure: F)
953 where
954 F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32),
955 {
956 let attachments = [
957 (&self.depth, constants::DEPTH_ATTACHMENT),
958 (&self.stencil, constants::STENCIL_ATTACHMENT),
959 (&self.depthstencil, constants::DEPTH_STENCIL_ATTACHMENT),
960 ];
961
962 fn has_matching_id(
963 attachment: &DomRefCell<Option<WebGLFramebufferAttachment>>,
964 target: &WebGLRenderbufferId,
965 ) -> bool {
966 match *attachment.borrow() {
967 Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
968 att_rb.id() == *target
969 },
970 _ => false,
971 }
972 }
973
974 for (attachment, name) in &attachments {
975 if has_matching_id(attachment, rb_id) {
976 closure(attachment, *name);
977 }
978 }
979
980 for (idx, attachment) in self.colors.iter().enumerate() {
981 if has_matching_id(attachment, rb_id) {
982 let name = constants::COLOR_ATTACHMENT0 + idx as u32;
983 closure(attachment, name);
984 }
985 }
986 }
987
988 fn with_matching_textures_id<F>(&self, tex_id: WebGLTextureId, mut closure: F)
989 where
990 F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32),
991 {
992 let attachments = [
993 (&self.depth, constants::DEPTH_ATTACHMENT),
994 (&self.stencil, constants::STENCIL_ATTACHMENT),
995 (&self.depthstencil, constants::DEPTH_STENCIL_ATTACHMENT),
996 ];
997
998 fn has_matching_id(
999 attachment: &DomRefCell<Option<WebGLFramebufferAttachment>>,
1000 target: &WebGLTextureId,
1001 ) -> bool {
1002 matches!(*attachment.borrow(), Some(WebGLFramebufferAttachment::Texture {
1003 texture: ref att_texture,
1004 ..
1005 }) if att_texture.id() == *target)
1006 }
1007
1008 for (attachment, name) in &attachments {
1009 if has_matching_id(attachment, &tex_id) {
1010 closure(attachment, *name);
1011 }
1012 }
1013
1014 for (idx, attachment) in self.colors.iter().enumerate() {
1015 if has_matching_id(attachment, &tex_id) {
1016 let name = constants::COLOR_ATTACHMENT0 + idx as u32;
1017 closure(attachment, name);
1018 }
1019 }
1020 }
1021
1022 pub(crate) fn detach_renderbuffer_by_id(&self, rb_id: &WebGLRenderbufferId) -> WebGLResult<()> {
1023 self.validate_transparent()?;
1026
1027 let mut depth_or_stencil_updated = false;
1028 self.with_matching_renderbuffers_id(rb_id, |att, name| {
1029 depth_or_stencil_updated |= INTERESTING_ATTACHMENT_POINTS.contains(&name);
1030 if let Some(att) = &*att.borrow() {
1031 att.detach();
1032 }
1033 *att.borrow_mut() = None;
1034 self.update_status();
1035 });
1036
1037 if depth_or_stencil_updated {
1038 self.reattach_depth_stencil()?;
1039 }
1040 Ok(())
1041 }
1042
1043 pub(crate) fn detach_texture(&self, tex_id: WebGLTextureId) -> WebGLResult<()> {
1044 self.validate_transparent()?;
1047
1048 let mut depth_or_stencil_updated = false;
1049 self.with_matching_textures_id(tex_id, |att, name| {
1050 depth_or_stencil_updated |= INTERESTING_ATTACHMENT_POINTS.contains(&name);
1051 if let Some(att) = &*att.borrow() {
1052 att.detach();
1053 }
1054 *att.borrow_mut() = None;
1055 self.update_status();
1056 });
1057
1058 if depth_or_stencil_updated {
1059 self.reattach_depth_stencil()?;
1060 }
1061 Ok(())
1062 }
1063
1064 pub(crate) fn invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer) {
1065 self.with_matching_renderbuffers_id(&rb.id(), |_att, _| {
1066 self.is_initialized.set(false);
1067 self.update_status();
1068 });
1069 }
1070
1071 pub(crate) fn invalidate_texture(&self, texture: &WebGLTexture) {
1072 self.with_matching_textures_id(texture.id(), |_att, _name| {
1073 self.update_status();
1074 });
1075 }
1076
1077 pub(crate) fn set_read_buffer(&self, buffer: u32) -> WebGLResult<()> {
1078 let Some(context) = self.upcast().context() else {
1079 return Err(WebGLError::ContextLost);
1080 };
1081
1082 match buffer {
1083 constants::NONE => {},
1084 _ if context.valid_color_attachment_enum(buffer) => {},
1085 _ => return Err(WebGLError::InvalidOperation),
1086 };
1087
1088 self.color_read_buffer.set(buffer);
1089 context.send_command(WebGLCommand::ReadBuffer(buffer));
1090 Ok(())
1091 }
1092
1093 pub(crate) fn set_draw_buffers(&self, buffers: Vec<u32>) -> WebGLResult<()> {
1094 let Some(context) = self.upcast().context() else {
1095 return Err(WebGLError::ContextLost);
1096 };
1097
1098 if buffers.len() > context.limits().max_draw_buffers as usize {
1099 return Err(WebGLError::InvalidValue);
1100 }
1101
1102 let enums_valid = buffers
1103 .iter()
1104 .all(|&val| val == constants::NONE || context.valid_color_attachment_enum(val));
1105 if !enums_valid {
1106 return Err(WebGLError::InvalidEnum);
1107 }
1108
1109 let values_valid = buffers.iter().enumerate().all(|(i, &val)| {
1110 val == constants::NONE || val == (constants::COLOR_ATTACHMENT0 + i as u32)
1111 });
1112 if !values_valid {
1113 return Err(WebGLError::InvalidOperation);
1114 }
1115
1116 self.color_draw_buffers.borrow_mut().clone_from(&buffers);
1117 context.send_command(WebGLCommand::DrawBuffers(buffers));
1118 Ok(())
1119 }
1120
1121 pub(crate) fn read_buffer(&self) -> u32 {
1122 self.color_read_buffer.get()
1123 }
1124
1125 pub(crate) fn draw_buffer_i(&self, index: usize) -> u32 {
1126 let buffers = &*self.color_draw_buffers.borrow();
1127 *buffers.get(index).unwrap_or(&constants::NONE)
1128 }
1129
1130 pub(crate) fn target(&self) -> Option<u32> {
1131 self.target.get()
1132 }
1133}
1134
1135static INTERESTING_ATTACHMENT_POINTS: &[u32] = &[
1136 constants::DEPTH_ATTACHMENT,
1137 constants::STENCIL_ATTACHMENT,
1138 constants::DEPTH_STENCIL_ATTACHMENT,
1139];