Add initial support for WebGL 2 BlitFramebuffer (#26389)

Add initial support for the WebGL2 BlitFramebuffer call.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Istvan <istvan.miklos@h-lab.eu>
This commit is contained in:
Josh Matthews 2025-01-06 13:37:35 -05:00 committed by GitHub
parent 2575a0daf1
commit c43762faea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1686 additions and 31 deletions

View file

@ -7,6 +7,7 @@ use std::cmp;
use std::ptr::{self, NonNull};
use std::rc::Rc;
use bitflags::bitflags;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
webgl_channel, GLContextAttributes, InternalFormatParameter, WebGLCommand, WebGLResult,
@ -3321,6 +3322,107 @@ impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingCont
.RenderbufferStorage(target, internal_format, width, height)
}
/// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4S>
fn BlitFramebuffer(
&self,
src_x0: i32,
src_y0: i32,
src_x1: i32,
src_y1: i32,
dst_x0: i32,
dst_y0: i32,
dst_x1: i32,
dst_y1: i32,
mask: u32,
filter: u32,
) {
bitflags! {
struct BlitFrameBufferFlags: u32 {
const DEPTH = constants::DEPTH_BUFFER_BIT;
const COLOR = constants::COLOR_BUFFER_BIT;
const STENCIL = constants::STENCIL_BUFFER_BIT;
const DEPTH_STENCIL = constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT;
}
};
let Some(bits) = BlitFrameBufferFlags::from_bits(mask) else {
return self.base.webgl_error(InvalidValue);
};
let attributes = self.base.GetContextAttributes().unwrap();
if bits.intersects(BlitFrameBufferFlags::DEPTH_STENCIL) {
match filter {
constants::LINEAR => return self.base.webgl_error(InvalidOperation),
constants::NEAREST => {},
_ => return self.base.webgl_error(InvalidOperation),
}
}
let src_fb = self.base.get_read_framebuffer_slot().get();
let dst_fb = self.base.get_draw_framebuffer_slot().get();
let get_default_formats = || -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
// All attempts to blit to an antialiased back buffer should fail.
if attributes.antialias {
return Err(InvalidOperation);
};
let color = if attributes.alpha {
Some(constants::RGBA8)
} else {
Some(constants::RGB8)
};
let (depth, stencil) = match (attributes.depth, attributes.stencil) {
(true, true) => (
Some(constants::DEPTH24_STENCIL8),
Some(constants::DEPTH24_STENCIL8),
),
(true, false) => (Some(constants::DEPTH_COMPONENT16), None),
(false, true) => (None, Some(constants::STENCIL_INDEX8)),
_ => (None, None),
};
Ok((color, depth, stencil))
};
let (src_color, src_depth, src_stencil) = match src_fb {
Some(fb) => {
handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
},
None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
};
let (dst_color, dst_depth, dst_stencil) = match dst_fb {
Some(fb) => {
handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
},
None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
};
if bits.intersects(BlitFrameBufferFlags::COLOR) && src_color != dst_color {
return self.base.webgl_error(InvalidOperation);
}
if bits.intersects(BlitFrameBufferFlags::DEPTH) && src_depth != dst_depth {
return self.base.webgl_error(InvalidOperation);
}
if bits.intersects(BlitFrameBufferFlags::STENCIL) && src_stencil != dst_stencil {
return self.base.webgl_error(InvalidOperation);
}
let src_width = src_x1.checked_sub(src_x0);
let dst_width = dst_x1.checked_sub(dst_x0);
let src_height = src_y1.checked_sub(src_y0);
let dst_height = dst_y1.checked_sub(dst_y0);
if src_width.is_none() ||
dst_width.is_none() ||
src_height.is_none() ||
dst_height.is_none()
{
return self.base.webgl_error(InvalidOperation);
}
self.base.send_command(WebGLCommand::BlitFrameBuffer(
src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
));
}
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
fn FramebufferRenderbuffer(
&self,

View file

@ -234,6 +234,25 @@ impl WebGLFramebuffer {
self.size.get()
}
pub fn get_attachment_formats(&self) -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
if self.check_status() != constants::FRAMEBUFFER_COMPLETE {
return Err(WebGLError::InvalidFramebufferOperation);
}
let color = match self.attachment(constants::COLOR_ATTACHMENT0) {
Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => Some(rb.internal_format()),
_ => None,
};
let depth = match self.attachment(constants::DEPTH_ATTACHMENT) {
Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => Some(rb.internal_format()),
_ => None,
};
let stencil = match self.attachment(constants::STENCIL_ATTACHMENT) {
Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => Some(rb.internal_format()),
_ => None,
};
Ok((color, depth, stencil))
}
fn check_attachment_constraints<'a>(
&self,
attachment: &Option<WebGLFramebufferAttachment>,

View file

@ -295,8 +295,8 @@ interface mixin WebGL2RenderingContextBase
optional GLuint dstOffset = 0, optional GLuint length = 0);
/* Framebuffer objects */
// void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
// GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
undefined blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
undefined framebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture? texture, GLint level,
GLint layer);
undefined invalidateFramebuffer(GLenum target, sequence<GLenum> attachments);