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