use std::cell::Cell;
use canvas_traits::webgl::{
webgl_channel, WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLFramebufferId,
WebGLRenderbufferId, WebGLResult, WebGLTextureId, WebGLVersion,
};
use dom_struct::dom_struct;
use euclid::Size2D;
use webxr_api::Viewport;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::webglobject::WebGLObject;
use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::xrsession::XRSession;
pub enum CompleteForRendering {
Complete,
Incomplete,
MissingColorAttachment,
}
fn log2(n: u32) -> u32 {
31 - n.leading_zeros()
}
#[crown::unrooted_must_root_lint::must_root]
#[derive(Clone, JSTraceable, MallocSizeOf)]
enum WebGLFramebufferAttachment {
Renderbuffer(Dom<WebGLRenderbuffer>),
Texture {
texture: Dom<WebGLTexture>,
level: i32,
},
}
impl WebGLFramebufferAttachment {
fn needs_initialization(&self) -> bool {
match *self {
WebGLFramebufferAttachment::Renderbuffer(ref r) => !r.is_initialized(),
WebGLFramebufferAttachment::Texture { .. } => false,
}
}
fn mark_initialized(&self) {
match *self {
WebGLFramebufferAttachment::Renderbuffer(ref r) => r.mark_initialized(),
WebGLFramebufferAttachment::Texture { .. } => (),
}
}
fn root(&self) -> WebGLFramebufferAttachmentRoot {
match *self {
WebGLFramebufferAttachment::Renderbuffer(ref rb) => {
WebGLFramebufferAttachmentRoot::Renderbuffer(DomRoot::from_ref(rb))
},
WebGLFramebufferAttachment::Texture { ref texture, .. } => {
WebGLFramebufferAttachmentRoot::Texture(DomRoot::from_ref(texture))
},
}
}
fn detach(&self) {
match self {
WebGLFramebufferAttachment::Renderbuffer(rb) => rb.detach_from_framebuffer(),
WebGLFramebufferAttachment::Texture { ref texture, .. } => {
texture.detach_from_framebuffer()
},
}
}
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub enum WebGLFramebufferAttachmentRoot {
Renderbuffer(DomRoot<WebGLRenderbuffer>),
Texture(DomRoot<WebGLTexture>),
}
#[dom_struct]
pub struct WebGLFramebuffer {
webgl_object: WebGLObject,
#[no_trace]
webgl_version: WebGLVersion,
#[no_trace]
id: WebGLFramebufferId,
target: Cell<Option<u32>>,
is_deleted: Cell<bool>,
size: Cell<Option<(i32, i32)>>,
status: Cell<u32>,
colors: Vec<DomRefCell<Option<WebGLFramebufferAttachment>>>,
depth: DomRefCell<Option<WebGLFramebufferAttachment>>,
stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
color_read_buffer: DomRefCell<u32>,
color_draw_buffers: DomRefCell<Vec<u32>>,
is_initialized: Cell<bool>,
xr_session: MutNullableDom<XRSession>,
}
impl WebGLFramebuffer {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
webgl_version: context.webgl_version(),
id,
target: Cell::new(None),
is_deleted: Cell::new(false),
size: Cell::new(None),
status: Cell::new(constants::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
colors: vec![DomRefCell::new(None); context.limits().max_color_attachments as usize],
depth: DomRefCell::new(None),
stencil: DomRefCell::new(None),
depthstencil: DomRefCell::new(None),
color_read_buffer: DomRefCell::new(constants::COLOR_ATTACHMENT0),
color_draw_buffers: DomRefCell::new(vec![constants::COLOR_ATTACHMENT0]),
is_initialized: Cell::new(false),
xr_session: Default::default(),
}
}
pub fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateFramebuffer(sender));
let id = receiver.recv().unwrap()?;
let framebuffer = WebGLFramebuffer::new(context, id);
Some(framebuffer)
}
pub fn maybe_new_webxr(
session: &XRSession,
context: &WebGLRenderingContext,
size: Size2D<i32, Viewport>,
) -> Option<DomRoot<Self>> {
let framebuffer = Self::maybe_new(context)?;
framebuffer.size.set(Some((size.width, size.height)));
framebuffer.status.set(constants::FRAMEBUFFER_COMPLETE);
framebuffer.xr_session.set(Some(session));
Some(framebuffer)
}
pub fn new(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLFramebuffer::new_inherited(context, id)),
&*context.global(),
)
}
}
impl WebGLFramebuffer {
pub fn id(&self) -> WebGLFramebufferId {
self.id
}
fn is_in_xr_session(&self) -> bool {
self.xr_session.get().is_some()
}
pub fn validate_transparent(&self) -> WebGLResult<()> {
if self.is_in_xr_session() {
Err(WebGLError::InvalidOperation)
} else {
Ok(())
}
}
pub fn bind(&self, target: u32) {
if !self.is_in_xr_session() {
self.update_status();
}
self.target.set(Some(target));
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::BindFramebuffer(
target,
WebGLFramebufferBindingRequest::Explicit(self.id),
));
}
pub fn delete(&self, operation_fallibility: Operation) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
let context = self.upcast::<WebGLObject>().context();
let cmd = WebGLCommand::DeleteFramebuffer(self.id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub fn is_deleted(&self) -> bool {
self.is_deleted.get()
}
pub fn size(&self) -> Option<(i32, i32)> {
self.size.get()
}
fn check_attachment_constraints<'a>(
&self,
attachment: &Option<WebGLFramebufferAttachment>,
mut constraints: impl Iterator<Item = &'a u32>,
fb_size: &mut Option<(i32, i32)>,
) -> Result<(), u32> {
let (format, size) = match attachment {
Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
(Some(att_rb.internal_format()), att_rb.size())
},
Some(WebGLFramebufferAttachment::Texture {
texture: ref att_tex,
level,
}) => match att_tex.image_info_at_face(0, *level as u32) {
Some(info) => (
Some(info.internal_format().as_gl_constant()),
Some((info.width() as i32, info.height() as i32)),
),
None => return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
},
None => (None, None),
};
if size.is_some() {
if fb_size.is_some() && size != *fb_size {
return Err(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
} else {
*fb_size = size;
}
}
if let Some(format) = format {
if constraints.all(|c| *c != format) {
return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
}
}
Ok(())
}
pub fn update_status(&self) {
let z = self.depth.borrow();
let s = self.stencil.borrow();
let zs = self.depthstencil.borrow();
let has_z = z.is_some();
let has_s = s.is_some();
let has_zs = zs.is_some();
let is_supported = match self.webgl_version {
WebGLVersion::WebGL1 => !(has_zs && (has_z || has_s)) && !(has_z && has_s),
WebGLVersion::WebGL2 => {
use WebGLFramebufferAttachment::{Renderbuffer, Texture};
match (&*z, &*s) {
(Some(Renderbuffer(a)), Some(Renderbuffer(b))) => a.id() == b.id(),
(Some(Texture { texture: a, .. }), Some(Texture { texture: b, .. })) => {
a.id() == b.id()
},
_ => !has_z || !has_s,
}
},
};
if !is_supported {
return self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
}
let mut fb_size = None;
let attachments = [&*z, &*s, &*zs];
let webgl1_attachment_constraints = &[
&[
constants::DEPTH_COMPONENT16,
constants::DEPTH_COMPONENT24,
constants::DEPTH_COMPONENT32F,
constants::DEPTH24_STENCIL8,
constants::DEPTH32F_STENCIL8,
][..],
&[
constants::STENCIL_INDEX8,
constants::DEPTH24_STENCIL8,
constants::DEPTH32F_STENCIL8,
][..],
&[constants::DEPTH_STENCIL][..],
];
let webgl2_attachment_constraints = &[
&[constants::DEPTH_STENCIL][..],
&[constants::DEPTH_STENCIL][..],
&[][..],
];
let empty_attachment_constrains = &[&[][..], &[][..], &[][..]];
let extra_attachment_constraints = match self.webgl_version {
WebGLVersion::WebGL1 => empty_attachment_constrains,
WebGLVersion::WebGL2 => webgl2_attachment_constraints,
};
let attachment_constraints = webgl1_attachment_constraints
.iter()
.zip(extra_attachment_constraints.iter())
.map(|(a, b)| a.iter().chain(b.iter()));
for (attachment, constraints) in attachments.iter().zip(attachment_constraints) {
if let Err(errnum) =
self.check_attachment_constraints(attachment, constraints, &mut fb_size)
{
return self.status.set(errnum);
}
}
let webgl1_color_constraints = &[
constants::RGB,
constants::RGB565,
constants::RGB5_A1,
constants::RGBA,
constants::RGBA4,
][..];
let webgl2_color_constraints = &[
constants::ALPHA,
constants::LUMINANCE,
constants::LUMINANCE_ALPHA,
constants::R11F_G11F_B10F,
constants::R16F,
constants::R16I,
constants::R16UI,
constants::R32F,
constants::R32I,
constants::R32UI,
constants::R8,
constants::R8_SNORM,
constants::R8I,
constants::R8UI,
constants::RG16F,
constants::RG16I,
constants::RG16UI,
constants::RG32F,
constants::RG32I,
constants::RG32UI,
constants::RG8,
constants::RG8_SNORM,
constants::RG8I,
constants::RG8UI,
constants::RGB10_A2,
constants::RGB10_A2UI,
constants::RGB16F,
constants::RGB16I,
constants::RGB16UI,
constants::RGB32F,
constants::RGB32I,
constants::RGB32UI,
constants::RGB8,
constants::RGB8_SNORM,
constants::RGB8I,
constants::RGB8UI,
constants::RGB9_E5,
constants::RGBA16F,
constants::RGBA16I,
constants::RGBA16UI,
constants::RGBA32F,
constants::RGBA32I,
constants::RGBA32UI,
constants::RGBA8,
constants::RGBA8_SNORM,
constants::RGBA8I,
constants::RGBA8UI,
constants::SRGB8,
constants::SRGB8_ALPHA8,
][..];
let empty_color_constrains = &[][..];
let extra_color_constraints = match self.webgl_version {
WebGLVersion::WebGL1 => empty_color_constrains,
WebGLVersion::WebGL2 => webgl2_color_constraints,
};
let color_constraints = webgl1_color_constraints
.iter()
.chain(extra_color_constraints.iter());
let has_c = self.colors.iter().any(|att| att.borrow().is_some());
for attachment in self.colors.iter() {
let attachment = attachment.borrow();
let constraints = color_constraints.clone();
if let Err(errnum) =
self.check_attachment_constraints(&attachment, constraints, &mut fb_size)
{
return self.status.set(errnum);
}
}
self.size.set(fb_size);
if has_c || has_z || has_zs || has_s {
if self.size.get().is_some_and(|(w, h)| w != 0 && h != 0) {
self.status.set(constants::FRAMEBUFFER_COMPLETE);
} else {
self.status
.set(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
}
} else {
self.status
.set(constants::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
}
}
pub fn check_status(&self) -> u32 {
if let Some(xr_session) = self.xr_session.get() {
if xr_session.is_outside_raf() {
constants::FRAMEBUFFER_UNSUPPORTED
} else {
constants::FRAMEBUFFER_COMPLETE
}
} else {
self.status.get()
}
}
pub fn check_status_for_rendering(&self) -> CompleteForRendering {
let result = self.check_status();
if result != constants::FRAMEBUFFER_COMPLETE {
return CompleteForRendering::Incomplete;
}
if self.xr_session.get().is_some() {
return CompleteForRendering::Complete;
}
if self.colors.iter().all(|att| att.borrow().is_none()) {
return CompleteForRendering::MissingColorAttachment;
}
if !self.is_initialized.get() {
let attachments = [
(&self.depth, constants::DEPTH_BUFFER_BIT),
(&self.stencil, constants::STENCIL_BUFFER_BIT),
(
&self.depthstencil,
constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT,
),
];
let mut clear_bits = 0;
for &(attachment, bits) in &attachments {
if let Some(ref att) = *attachment.borrow() {
if att.needs_initialization() {
att.mark_initialized();
clear_bits |= bits;
}
}
}
for attachment in self.colors.iter() {
if let Some(ref att) = *attachment.borrow() {
if att.needs_initialization() {
att.mark_initialized();
clear_bits |= constants::COLOR_BUFFER_BIT;
}
}
}
self.upcast::<WebGLObject>()
.context()
.initialize_framebuffer(clear_bits);
self.is_initialized.set(true);
}
CompleteForRendering::Complete
}
pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
self.validate_transparent()?;
let binding = self
.attachment_binding(attachment)
.ok_or(WebGLError::InvalidEnum)?;
let rb_id = match rb {
Some(rb) => {
if !rb.ever_bound() {
return Err(WebGLError::InvalidOperation);
}
*binding.borrow_mut() =
Some(WebGLFramebufferAttachment::Renderbuffer(Dom::from_ref(rb)));
rb.attach_to_framebuffer(self);
Some(rb.id())
},
_ => None,
};
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::FramebufferRenderbuffer(
self.target.get().unwrap(),
attachment,
constants::RENDERBUFFER,
rb_id,
));
if rb.is_none() {
self.detach_binding(binding, attachment)?;
}
self.update_status();
self.is_initialized.set(false);
Ok(())
}
fn detach_binding(
&self,
binding: &DomRefCell<Option<WebGLFramebufferAttachment>>,
attachment: u32,
) -> WebGLResult<()> {
self.validate_transparent()?;
if let Some(att) = &*binding.borrow() {
att.detach();
}
*binding.borrow_mut() = None;
if INTERESTING_ATTACHMENT_POINTS.contains(&attachment) {
self.reattach_depth_stencil()?;
}
Ok(())
}
fn attachment_binding(
&self,
attachment: u32,
) -> Option<&DomRefCell<Option<WebGLFramebufferAttachment>>> {
match attachment {
constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
let idx = attachment - constants::COLOR_ATTACHMENT0;
self.colors.get(idx as usize)
},
constants::DEPTH_ATTACHMENT => Some(&self.depth),
constants::STENCIL_ATTACHMENT => Some(&self.stencil),
constants::DEPTH_STENCIL_ATTACHMENT => Some(&self.depthstencil),
_ => None,
}
}
fn reattach_depth_stencil(&self) -> WebGLResult<()> {
self.validate_transparent()?;
let reattach = |attachment: &WebGLFramebufferAttachment, attachment_point| {
let context = self.upcast::<WebGLObject>().context();
match *attachment {
WebGLFramebufferAttachment::Renderbuffer(ref rb) => {
rb.attach_to_framebuffer(self);
context.send_command(WebGLCommand::FramebufferRenderbuffer(
self.target.get().unwrap(),
attachment_point,
constants::RENDERBUFFER,
Some(rb.id()),
));
},
WebGLFramebufferAttachment::Texture { ref texture, level } => {
texture.attach_to_framebuffer(self);
context.send_command(WebGLCommand::FramebufferTexture2D(
self.target.get().unwrap(),
attachment_point,
texture.target().expect("missing texture target"),
Some(texture.id()),
level,
));
},
}
};
if let Some(ref depth) = *self.depth.borrow() {
reattach(depth, constants::DEPTH_ATTACHMENT);
}
if let Some(ref stencil) = *self.stencil.borrow() {
reattach(stencil, constants::STENCIL_ATTACHMENT);
}
if let Some(ref depth_stencil) = *self.depthstencil.borrow() {
reattach(depth_stencil, constants::DEPTH_STENCIL_ATTACHMENT);
}
Ok(())
}
pub fn attachment(&self, attachment: u32) -> Option<WebGLFramebufferAttachmentRoot> {
let binding = self.attachment_binding(attachment)?;
binding
.borrow()
.as_ref()
.map(WebGLFramebufferAttachment::root)
}
pub fn texture2d(
&self,
attachment: u32,
textarget: u32,
texture: Option<&WebGLTexture>,
level: i32,
) -> WebGLResult<()> {
self.validate_transparent()?;
if let Some(texture) = texture {
let is_cube = match textarget {
constants::TEXTURE_2D => false,
constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,
_ => return Err(WebGLError::InvalidEnum),
};
match texture.target() {
Some(constants::TEXTURE_CUBE_MAP) if is_cube => {},
Some(_) if !is_cube => {},
_ => return Err(WebGLError::InvalidOperation),
}
let context = self.upcast::<WebGLObject>().context();
let max_tex_size = if is_cube {
context.limits().max_cube_map_tex_size
} else {
context.limits().max_tex_size
};
if level < 0 || level as u32 > log2(max_tex_size) {
return Err(WebGLError::InvalidValue);
}
}
self.texture2d_even_if_opaque(attachment, textarget, texture, level)
}
pub fn texture2d_even_if_opaque(
&self,
attachment: u32,
textarget: u32,
texture: Option<&WebGLTexture>,
level: i32,
) -> WebGLResult<()> {
let binding = self
.attachment_binding(attachment)
.ok_or(WebGLError::InvalidEnum)?;
let tex_id = match texture {
Some(texture) => {
*binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
texture: Dom::from_ref(texture),
level,
});
texture.attach_to_framebuffer(self);
Some(texture.id())
},
_ => None,
};
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::FramebufferTexture2D(
self.target.get().unwrap(),
attachment,
textarget,
tex_id,
level,
));
if texture.is_none() {
self.detach_binding(binding, attachment)?;
}
self.update_status();
self.is_initialized.set(false);
Ok(())
}
pub fn texture_layer(
&self,
attachment: u32,
texture: Option<&WebGLTexture>,
level: i32,
layer: i32,
) -> WebGLResult<()> {
let binding = self
.attachment_binding(attachment)
.ok_or(WebGLError::InvalidEnum)?;
let context = self.upcast::<WebGLObject>().context();
let tex_id = match texture {
Some(texture) => {
let (max_level, max_layer) = match texture.target() {
Some(constants::TEXTURE_3D) => (
log2(context.limits().max_3d_texture_size),
context.limits().max_3d_texture_size - 1,
),
Some(constants::TEXTURE_2D) => (
log2(context.limits().max_tex_size),
context.limits().max_array_texture_layers - 1,
),
_ => return Err(WebGLError::InvalidOperation),
};
if level < 0 || level as u32 >= max_level {
return Err(WebGLError::InvalidValue);
}
if layer < 0 || layer as u32 >= max_layer {
return Err(WebGLError::InvalidValue);
}
*binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
texture: Dom::from_ref(texture),
level,
});
texture.attach_to_framebuffer(self);
Some(texture.id())
},
_ => None,
};
context.send_command(WebGLCommand::FramebufferTextureLayer(
self.target.get().unwrap(),
attachment,
tex_id,
level,
layer,
));
Ok(())
}
fn with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F)
where
F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32),
{
let rb_id = rb.id();
let attachments = [
(&self.depth, constants::DEPTH_ATTACHMENT),
(&self.stencil, constants::STENCIL_ATTACHMENT),
(&self.depthstencil, constants::DEPTH_STENCIL_ATTACHMENT),
];
fn has_matching_id(
attachment: &DomRefCell<Option<WebGLFramebufferAttachment>>,
target: &WebGLRenderbufferId,
) -> bool {
match *attachment.borrow() {
Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
att_rb.id() == *target
},
_ => false,
}
}
for (attachment, name) in &attachments {
if has_matching_id(attachment, &rb_id) {
closure(attachment, *name);
}
}
for (idx, attachment) in self.colors.iter().enumerate() {
if has_matching_id(attachment, &rb_id) {
let name = constants::COLOR_ATTACHMENT0 + idx as u32;
closure(attachment, name);
}
}
}
fn with_matching_textures<F>(&self, texture: &WebGLTexture, mut closure: F)
where
F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32),
{
let tex_id = texture.id();
let attachments = [
(&self.depth, constants::DEPTH_ATTACHMENT),
(&self.stencil, constants::STENCIL_ATTACHMENT),
(&self.depthstencil, constants::DEPTH_STENCIL_ATTACHMENT),
];
fn has_matching_id(
attachment: &DomRefCell<Option<WebGLFramebufferAttachment>>,
target: &WebGLTextureId,
) -> bool {
matches!(*attachment.borrow(), Some(WebGLFramebufferAttachment::Texture {
texture: ref att_texture,
..
}) if att_texture.id() == *target)
}
for (attachment, name) in &attachments {
if has_matching_id(attachment, &tex_id) {
closure(attachment, *name);
}
}
for (idx, attachment) in self.colors.iter().enumerate() {
if has_matching_id(attachment, &tex_id) {
let name = constants::COLOR_ATTACHMENT0 + idx as u32;
closure(attachment, name);
}
}
}
pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) -> WebGLResult<()> {
self.validate_transparent()?;
let mut depth_or_stencil_updated = false;
self.with_matching_renderbuffers(rb, |att, name| {
depth_or_stencil_updated |= INTERESTING_ATTACHMENT_POINTS.contains(&name);
if let Some(att) = &*att.borrow() {
att.detach();
}
*att.borrow_mut() = None;
self.update_status();
});
if depth_or_stencil_updated {
self.reattach_depth_stencil()?;
}
Ok(())
}
pub fn detach_texture(&self, texture: &WebGLTexture) -> WebGLResult<()> {
self.validate_transparent()?;
let mut depth_or_stencil_updated = false;
self.with_matching_textures(texture, |att, name| {
depth_or_stencil_updated |= INTERESTING_ATTACHMENT_POINTS.contains(&name);
if let Some(att) = &*att.borrow() {
att.detach();
}
*att.borrow_mut() = None;
self.update_status();
});
if depth_or_stencil_updated {
self.reattach_depth_stencil()?;
}
Ok(())
}
pub fn invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer) {
self.with_matching_renderbuffers(rb, |_att, _| {
self.is_initialized.set(false);
self.update_status();
});
}
pub fn invalidate_texture(&self, texture: &WebGLTexture) {
self.with_matching_textures(texture, |_att, _name| {
self.update_status();
});
}
pub fn set_read_buffer(&self, buffer: u32) -> WebGLResult<()> {
let context = self.upcast::<WebGLObject>().context();
match buffer {
constants::NONE => {},
_ if context.valid_color_attachment_enum(buffer) => {},
_ => return Err(WebGLError::InvalidOperation),
};
*self.color_read_buffer.borrow_mut() = buffer;
context.send_command(WebGLCommand::ReadBuffer(buffer));
Ok(())
}
pub fn set_draw_buffers(&self, buffers: Vec<u32>) -> WebGLResult<()> {
let context = self.upcast::<WebGLObject>().context();
if buffers.len() > context.limits().max_draw_buffers as usize {
return Err(WebGLError::InvalidValue);
}
let enums_valid = buffers
.iter()
.all(|&val| val == constants::NONE || context.valid_color_attachment_enum(val));
if !enums_valid {
return Err(WebGLError::InvalidEnum);
}
let values_valid = buffers.iter().enumerate().all(|(i, &val)| {
val == constants::NONE || val == (constants::COLOR_ATTACHMENT0 + i as u32)
});
if !values_valid {
return Err(WebGLError::InvalidOperation);
}
self.color_draw_buffers.borrow_mut().clone_from(&buffers);
context.send_command(WebGLCommand::DrawBuffers(buffers));
Ok(())
}
pub fn read_buffer(&self) -> u32 {
*self.color_read_buffer.borrow()
}
pub fn draw_buffer_i(&self, index: usize) -> u32 {
let buffers = &*self.color_draw_buffers.borrow();
*buffers.get(index).unwrap_or(&constants::NONE)
}
pub fn target(&self) -> Option<u32> {
self.target.get()
}
}
impl Drop for WebGLFramebuffer {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}
static INTERESTING_ATTACHMENT_POINTS: &[u32] = &[
constants::DEPTH_ATTACHMENT,
constants::STENCIL_ATTACHMENT,
constants::DEPTH_STENCIL_ATTACHMENT,
];