use std::cell::Cell;
use std::cmp;
use std::ptr::{self, NonNull};
use std::rc::Rc;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
webgl_channel, GLContextAttributes, InternalFormatParameter, WebGLCommand, WebGLResult,
WebGLVersion,
};
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D};
use ipc_channel::ipc::{self, IpcSharedMemory};
use js::jsapi::{JSObject, Type};
use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleValue};
use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array};
use script_layout_interface::HTMLCanvasDataSource;
use servo_config::pref;
use url::Host;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{
WebGL2RenderingContextConstants as constants, WebGL2RenderingContextMethods,
};
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{
WebGLContextAttributes, WebGLRenderingContextMethods,
};
use crate::dom::bindings::codegen::UnionTypes::{
ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence,
HTMLCanvasElementOrOffscreenCanvas,
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement, Int32ArrayOrLongSequence,
Uint32ArrayOrUnsignedLongSequence,
};
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlcanvaselement::LayoutCanvasRenderingContextHelpers;
use crate::dom::promise::Promise;
use crate::dom::webgl_validations::tex_image_2d::{
TexImage2DValidator, TexImage2DValidatorResult, TexStorageValidator, TexStorageValidatorResult,
};
use crate::dom::webgl_validations::WebGLValidator;
use crate::dom::webglactiveinfo::WebGLActiveInfo;
use crate::dom::webglbuffer::WebGLBuffer;
use crate::dom::webglframebuffer::{WebGLFramebuffer, WebGLFramebufferAttachmentRoot};
use crate::dom::webglprogram::WebGLProgram;
use crate::dom::webglquery::WebGLQuery;
use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webglrenderingcontext::{
uniform_get, uniform_typed, Operation, TexPixels, TexSource, VertexAttrib,
WebGLRenderingContext,
};
use crate::dom::webglsampler::{WebGLSampler, WebGLSamplerValue};
use crate::dom::webglshader::WebGLShader;
use crate::dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
use crate::dom::webglsync::WebGLSync;
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::webgltransformfeedback::WebGLTransformFeedback;
use crate::dom::webgluniformlocation::WebGLUniformLocation;
use crate::dom::webglvertexarrayobject::WebGLVertexArrayObject;
use crate::dom::window::Window;
use crate::js::conversions::ToJSValConvertible;
use crate::script_runtime::{CanGc, JSContext};
#[crown::unrooted_must_root_lint::must_root]
#[derive(JSTraceable, MallocSizeOf)]
struct IndexedBinding {
buffer: MutNullableDom<WebGLBuffer>,
start: Cell<i64>,
size: Cell<i64>,
}
impl IndexedBinding {
fn new() -> IndexedBinding {
IndexedBinding {
buffer: MutNullableDom::new(None),
start: Cell::new(0),
size: Cell::new(0),
}
}
}
#[dom_struct]
pub struct WebGL2RenderingContext {
reflector_: Reflector,
base: Dom<WebGLRenderingContext>,
occlusion_query: MutNullableDom<WebGLQuery>,
primitives_query: MutNullableDom<WebGLQuery>,
samplers: Box<[MutNullableDom<WebGLSampler>]>,
bound_copy_read_buffer: MutNullableDom<WebGLBuffer>,
bound_copy_write_buffer: MutNullableDom<WebGLBuffer>,
bound_pixel_pack_buffer: MutNullableDom<WebGLBuffer>,
bound_pixel_unpack_buffer: MutNullableDom<WebGLBuffer>,
bound_transform_feedback_buffer: MutNullableDom<WebGLBuffer>,
bound_uniform_buffer: MutNullableDom<WebGLBuffer>,
indexed_uniform_buffer_bindings: Box<[IndexedBinding]>,
indexed_transform_feedback_buffer_bindings: Box<[IndexedBinding]>,
current_transform_feedback: MutNullableDom<WebGLTransformFeedback>,
texture_pack_row_length: Cell<usize>,
texture_pack_skip_pixels: Cell<usize>,
texture_pack_skip_rows: Cell<usize>,
enable_rasterizer_discard: Cell<bool>,
default_fb_readbuffer: Cell<u32>,
default_fb_drawbuffer: Cell<u32>,
}
fn typedarray_elem_size(typeid: Type) -> usize {
match typeid {
Type::Int8 | Type::Uint8 | Type::Uint8Clamped => 1,
Type::Int16 | Type::Uint16 | Type::Float16 => 2,
Type::Int32 | Type::Uint32 | Type::Float32 => 4,
Type::Int64 | Type::Float64 => 8,
Type::BigInt64 | Type::BigUint64 => 8,
Type::Simd128 | Type::MaxTypedArrayViewType => unreachable!(),
}
}
struct ReadPixelsAllowedFormats<'a> {
array_types: &'a [Type],
channels: usize,
}
struct ReadPixelsSizes {
row_stride: usize,
skipped_bytes: usize,
size: usize,
}
impl WebGL2RenderingContext {
fn new_inherited(
window: &Window,
canvas: &HTMLCanvasElementOrOffscreenCanvas,
size: Size2D<u32>,
attrs: GLContextAttributes,
can_gc: CanGc,
) -> Option<WebGL2RenderingContext> {
let base =
WebGLRenderingContext::new(window, canvas, WebGLVersion::WebGL2, size, attrs, can_gc)?;
let samplers = (0..base.limits().max_combined_texture_image_units)
.map(|_| Default::default())
.collect::<Vec<_>>()
.into();
let indexed_uniform_buffer_bindings = (0..base.limits().max_uniform_buffer_bindings)
.map(|_| IndexedBinding::new())
.collect::<Vec<_>>()
.into();
let indexed_transform_feedback_buffer_bindings =
(0..base.limits().max_transform_feedback_separate_attribs)
.map(|_| IndexedBinding::new())
.collect::<Vec<_>>()
.into();
Some(WebGL2RenderingContext {
reflector_: Reflector::new(),
base: Dom::from_ref(&*base),
occlusion_query: MutNullableDom::new(None),
primitives_query: MutNullableDom::new(None),
samplers,
bound_copy_read_buffer: MutNullableDom::new(None),
bound_copy_write_buffer: MutNullableDom::new(None),
bound_pixel_pack_buffer: MutNullableDom::new(None),
bound_pixel_unpack_buffer: MutNullableDom::new(None),
bound_transform_feedback_buffer: MutNullableDom::new(None),
bound_uniform_buffer: MutNullableDom::new(None),
indexed_uniform_buffer_bindings,
indexed_transform_feedback_buffer_bindings,
current_transform_feedback: MutNullableDom::new(None),
texture_pack_row_length: Cell::new(0),
texture_pack_skip_pixels: Cell::new(0),
texture_pack_skip_rows: Cell::new(0),
enable_rasterizer_discard: Cell::new(false),
default_fb_readbuffer: Cell::new(constants::BACK),
default_fb_drawbuffer: Cell::new(constants::BACK),
})
}
#[allow(crown::unrooted_must_root)]
pub fn new(
window: &Window,
canvas: &HTMLCanvasElementOrOffscreenCanvas,
size: Size2D<u32>,
attrs: GLContextAttributes,
can_gc: CanGc,
) -> Option<DomRoot<WebGL2RenderingContext>> {
WebGL2RenderingContext::new_inherited(window, canvas, size, attrs, can_gc)
.map(|ctx| reflect_dom_object(Box::new(ctx), window, can_gc))
}
#[allow(unsafe_code)]
pub fn is_webgl2_enabled(_cx: JSContext, global: HandleObject) -> bool {
if pref!(dom.webgl2.enabled) {
return true;
}
let global = unsafe { GlobalScope::from_object(global.get()) };
let origin = global.origin();
let host = origin.host();
WEBGL2_ORIGINS
.iter()
.any(|origin| host == Host::parse(origin).ok().as_ref())
}
}
static WEBGL2_ORIGINS: &[&str] = &["www.servoexperiments.com"];
impl WebGL2RenderingContext {
pub fn recreate(&self, size: Size2D<u32>) {
self.base.recreate(size)
}
pub fn current_vao(&self) -> DomRoot<WebGLVertexArrayObject> {
self.base.current_vao_webgl2()
}
pub fn validate_uniform_block_for_draw(&self) {
let program = match self.base.current_program() {
Some(program) => program,
None => return,
};
for uniform_block in program.active_uniform_blocks().iter() {
let data_size = uniform_block.size as usize;
for block in program.active_uniforms().iter() {
let index = match block.bind_index {
Some(index) => index,
None => continue,
};
let indexed = &self.indexed_uniform_buffer_bindings[index as usize];
let buffer = match indexed.buffer.get() {
Some(buffer) => buffer,
None => {
self.base.webgl_error(InvalidOperation);
return;
},
};
if indexed.size.get() == 0 {
if data_size > buffer.capacity() {
self.base.webgl_error(InvalidOperation);
return;
}
} else {
let start = indexed.start.get() as usize;
let mut size = indexed.size.get() as usize;
if start >= size {
self.base.webgl_error(InvalidOperation);
return;
}
size -= start;
if data_size > size {
self.base.webgl_error(InvalidOperation);
return;
}
}
}
}
}
fn validate_vertex_attribs_for_draw(&self) {
let program = match self.base.current_program() {
Some(program) => program,
None => return,
};
let groups = [
[
constants::INT,
constants::INT_VEC2,
constants::INT_VEC3,
constants::INT_VEC4,
],
[
constants::UNSIGNED_INT,
constants::UNSIGNED_INT_VEC2,
constants::UNSIGNED_INT_VEC3,
constants::UNSIGNED_INT_VEC4,
],
[
constants::FLOAT,
constants::FLOAT_VEC2,
constants::FLOAT_VEC3,
constants::FLOAT_VEC4,
],
];
let vao = self.current_vao();
for prog_attrib in program.active_attribs().iter() {
let attrib = handle_potential_webgl_error!(
self.base,
vao.get_vertex_attrib(prog_attrib.location.unwrap_or(u32::MAX))
.ok_or(InvalidOperation),
return
);
let current_vertex_attrib = self.base.current_vertex_attribs()[prog_attrib
.location
.map(|l| l as usize)
.unwrap_or(usize::MAX)];
let attrib_data_base_type = if !attrib.enabled_as_array {
match current_vertex_attrib {
VertexAttrib::Int(_, _, _, _) => constants::INT,
VertexAttrib::Uint(_, _, _, _) => constants::UNSIGNED_INT,
VertexAttrib::Float(_, _, _, _) => constants::FLOAT,
}
} else {
attrib.type_
};
let contains = groups
.iter()
.find(|g| g.contains(&attrib_data_base_type) && g.contains(&prog_attrib.type_));
if contains.is_none() {
self.base.webgl_error(InvalidOperation);
return;
}
}
}
pub fn base_context(&self) -> DomRoot<WebGLRenderingContext> {
DomRoot::from_ref(&*self.base)
}
fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
match target {
constants::COPY_READ_BUFFER => Ok(self.bound_copy_read_buffer.get()),
constants::COPY_WRITE_BUFFER => Ok(self.bound_copy_write_buffer.get()),
constants::PIXEL_PACK_BUFFER => Ok(self.bound_pixel_pack_buffer.get()),
constants::PIXEL_UNPACK_BUFFER => Ok(self.bound_pixel_unpack_buffer.get()),
constants::TRANSFORM_FEEDBACK_BUFFER => Ok(self.bound_transform_feedback_buffer.get()),
constants::UNIFORM_BUFFER => Ok(self.bound_uniform_buffer.get()),
constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()),
_ => self.base.bound_buffer(target),
}
}
pub fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> {
match usage {
constants::STATIC_READ |
constants::DYNAMIC_READ |
constants::STREAM_READ |
constants::STATIC_COPY |
constants::DYNAMIC_COPY |
constants::STREAM_COPY => Ok(usage),
_ => self.base.buffer_usage(usage),
}
}
fn unbind_from(&self, slot: &MutNullableDom<WebGLBuffer>, buffer: &WebGLBuffer) {
if slot.get().is_some_and(|b| buffer == &*b) {
buffer.decrement_attached_counter(Operation::Infallible);
slot.set(None);
}
}
fn calc_read_pixel_formats(
&self,
pixel_type: u32,
format: u32,
) -> WebGLResult<ReadPixelsAllowedFormats> {
let array_types = match pixel_type {
constants::BYTE => &[Type::Int8][..],
constants::SHORT => &[Type::Int16][..],
constants::INT => &[Type::Int32][..],
constants::UNSIGNED_BYTE => &[Type::Uint8, Type::Uint8Clamped][..],
constants::UNSIGNED_SHORT |
constants::UNSIGNED_SHORT_4_4_4_4 |
constants::UNSIGNED_SHORT_5_5_5_1 |
constants::UNSIGNED_SHORT_5_6_5 => &[Type::Uint16][..],
constants::UNSIGNED_INT |
constants::UNSIGNED_INT_2_10_10_10_REV |
constants::UNSIGNED_INT_10F_11F_11F_REV |
constants::UNSIGNED_INT_5_9_9_9_REV => &[Type::Uint32][..],
constants::FLOAT => &[Type::Float32][..],
constants::HALF_FLOAT => &[Type::Uint16][..],
_ => return Err(InvalidEnum),
};
let channels = match format {
constants::ALPHA | constants::RED | constants::RED_INTEGER => 1,
constants::RG | constants::RG_INTEGER => 2,
constants::RGB | constants::RGB_INTEGER => 3,
constants::RGBA | constants::RGBA_INTEGER => 4,
_ => return Err(InvalidEnum),
};
Ok(ReadPixelsAllowedFormats {
array_types,
channels,
})
}
fn calc_read_pixel_sizes(
&self,
width: i32,
height: i32,
bytes_per_pixel: usize,
) -> WebGLResult<ReadPixelsSizes> {
if width < 0 || height < 0 {
return Err(InvalidValue);
}
let pixels_per_row = if self.texture_pack_row_length.get() > 0 {
self.texture_pack_row_length.get()
} else {
width as usize
};
if self.texture_pack_skip_pixels.get() + width as usize > pixels_per_row {
return Err(InvalidOperation);
}
let bytes_per_row = pixels_per_row
.checked_mul(bytes_per_pixel)
.ok_or(InvalidOperation)?;
let row_padding_bytes = {
let pack_alignment = self.base.get_texture_packing_alignment() as usize;
match bytes_per_row % pack_alignment {
0 => 0,
remainder => pack_alignment - remainder,
}
};
let row_stride = bytes_per_row + row_padding_bytes;
let size = if width == 0 || height == 0 {
0
} else {
let full_row_bytes = row_stride
.checked_mul(height as usize - 1)
.ok_or(InvalidOperation)?;
let last_row_bytes = bytes_per_pixel
.checked_mul(width as usize)
.ok_or(InvalidOperation)?;
full_row_bytes
.checked_add(last_row_bytes)
.ok_or(InvalidOperation)?
};
let skipped_bytes = {
let skipped_row_bytes = self
.texture_pack_skip_rows
.get()
.checked_mul(row_stride)
.ok_or(InvalidOperation)?;
let skipped_pixel_bytes = self
.texture_pack_skip_pixels
.get()
.checked_mul(bytes_per_pixel)
.ok_or(InvalidOperation)?;
skipped_row_bytes
.checked_add(skipped_pixel_bytes)
.ok_or(InvalidOperation)?
};
Ok(ReadPixelsSizes {
row_stride,
skipped_bytes,
size,
})
}
#[allow(unsafe_code, clippy::too_many_arguments)]
fn read_pixels_into(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
format: u32,
pixel_type: u32,
dst: &mut ArrayBufferView,
dst_elem_offset: u32,
) {
handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
if self.bound_pixel_pack_buffer.get().is_some() {
return self.base.webgl_error(InvalidOperation);
}
let fb_slot = self.base.get_draw_framebuffer_slot();
let fb_readbuffer_valid = match fb_slot.get() {
Some(fb) => fb.attachment(fb.read_buffer()).is_some(),
None => self.default_fb_readbuffer.get() != constants::NONE,
};
if !fb_readbuffer_valid {
return self.base.webgl_error(InvalidOperation);
}
let dst_byte_offset = {
let dst_elem_size = typedarray_elem_size(dst.get_array_type());
dst_elem_offset as usize * dst_elem_size
};
if dst_byte_offset > dst.len() {
return self.base.webgl_error(InvalidValue);
}
let dst_array_type = dst.get_array_type();
let ReadPixelsAllowedFormats {
array_types: allowed_array_types,
channels,
} = match self.calc_read_pixel_formats(pixel_type, format) {
Ok(result) => result,
Err(error) => return self.base.webgl_error(error),
};
if !allowed_array_types.contains(&dst_array_type) {
return self.base.webgl_error(InvalidOperation);
}
if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
return self.base.webgl_error(InvalidOperation);
}
let bytes_per_pixel = typedarray_elem_size(dst_array_type) * channels;
let ReadPixelsSizes {
row_stride,
skipped_bytes,
size,
} = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
Ok(result) => result,
Err(error) => return self.base.webgl_error(error),
};
let dst_end = dst_byte_offset + skipped_bytes + size;
let dst_pixels = unsafe { dst.as_mut_slice() };
if dst_pixels.len() < dst_end {
return self.base.webgl_error(InvalidOperation);
}
let dst_byte_offset = {
let margin_left = cmp::max(0, -x) as usize;
let margin_top = cmp::max(0, -y) as usize;
dst_byte_offset +
skipped_bytes +
margin_left * bytes_per_pixel +
margin_top * row_stride
};
let src_rect = {
let (fb_width, fb_height) = handle_potential_webgl_error!(
self.base,
self.base
.get_current_framebuffer_size()
.ok_or(InvalidOperation),
return
);
let src_origin = Point2D::new(x, y);
let src_size = Size2D::new(width as u32, height as u32);
let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
match pixels::clip(src_origin, src_size.to_u64(), fb_size.to_u64()) {
Some(rect) => rect.to_u32(),
None => return,
}
};
let src_row_bytes = handle_potential_webgl_error!(
self.base,
src_rect
.size
.width
.checked_mul(bytes_per_pixel as u32)
.ok_or(InvalidOperation),
return
);
let (sender, receiver) = ipc::bytes_channel().unwrap();
self.base.send_command(WebGLCommand::ReadPixels(
src_rect, format, pixel_type, sender,
));
let src = receiver.recv().unwrap();
for i in 0..src_rect.size.height as usize {
let src_start = i * src_row_bytes as usize;
let dst_start = dst_byte_offset + i * row_stride;
dst_pixels[dst_start..dst_start + src_row_bytes as usize]
.copy_from_slice(&src[src_start..src_start + src_row_bytes as usize]);
}
}
fn uniform_vec_section_uint(
&self,
vec: Uint32ArrayOrUnsignedLongSequence,
offset: u32,
length: u32,
uniform_size: usize,
uniform_location: &WebGLUniformLocation,
) -> WebGLResult<Vec<u32>> {
let vec = match vec {
Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
};
self.base
.uniform_vec_section::<u32>(vec, offset, length, uniform_size, uniform_location)
}
#[allow(unsafe_code)]
fn get_default_fb_attachment_param(
&self,
attachment: u32,
pname: u32,
mut retval: MutableHandleValue,
) -> WebGLResult<()> {
match attachment {
constants::BACK | constants::DEPTH | constants::STENCIL => {},
_ => return Err(InvalidEnum),
}
if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
retval.set(NullValue());
return Ok(());
}
let attrs = self
.GetContextAttributes()
.unwrap_or_else(WebGLContextAttributes::empty);
let intval = match pname {
constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => match attachment {
constants::DEPTH if !attrs.depth => constants::NONE as _,
constants::STENCIL if !attrs.stencil => constants::NONE as _,
_ => constants::FRAMEBUFFER_DEFAULT as _,
},
constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE => match attachment {
constants::BACK => 8,
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE => match attachment {
constants::BACK if attrs.alpha => 8,
constants::BACK => return Err(InvalidOperation),
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE => match attachment {
constants::DEPTH if attrs.depth => 24,
constants::DEPTH => return Err(InvalidOperation),
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE => match attachment {
constants::STENCIL if attrs.stencil => 8,
constants::STENCIL => return Err(InvalidOperation),
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE => match attachment {
constants::DEPTH if attrs.depth => constants::UNSIGNED_NORMALIZED as _,
constants::STENCIL if attrs.stencil => constants::UNSIGNED_INT as _,
constants::DEPTH => return Err(InvalidOperation),
constants::STENCIL => return Err(InvalidOperation),
_ => constants::UNSIGNED_NORMALIZED as _,
},
constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => match attachment {
constants::DEPTH if !attrs.depth => return Err(InvalidOperation),
constants::STENCIL if !attrs.stencil => return Err(InvalidOperation),
_ => constants::LINEAR as _,
},
_ => return Err(InvalidEnum),
};
retval.set(Int32Value(intval));
Ok(())
}
#[allow(unsafe_code)]
fn get_specific_fb_attachment_param(
&self,
cx: JSContext,
fb: &WebGLFramebuffer,
target: u32,
attachment: u32,
pname: u32,
mut rval: MutableHandleValue,
) -> WebGLResult<()> {
use crate::dom::webglframebuffer::WebGLFramebufferAttachmentRoot::{Renderbuffer, Texture};
match attachment {
constants::DEPTH_ATTACHMENT | constants::STENCIL_ATTACHMENT => {},
constants::DEPTH_STENCIL_ATTACHMENT => {
if pname == constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE {
return Err(InvalidOperation);
}
let a = fb.attachment(constants::DEPTH_ATTACHMENT);
let b = fb.attachment(constants::STENCIL_ATTACHMENT);
match (a, b) {
(Some(Renderbuffer(ref a)), Some(Renderbuffer(ref b))) if a.id() == b.id() => {
},
(Some(Texture(ref a)), Some(Texture(ref b))) if a.id() == b.id() => {},
_ => return Err(InvalidOperation),
}
},
constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
let last_slot =
constants::COLOR_ATTACHMENT0 + self.base.limits().max_color_attachments - 1;
if last_slot < attachment {
return Err(InvalidEnum);
}
},
_ => return Err(InvalidEnum),
}
let attachment = match attachment {
constants::DEPTH_STENCIL_ATTACHMENT => constants::DEPTH_ATTACHMENT,
_ => attachment,
};
if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
match fb.attachment(attachment) {
Some(Renderbuffer(rb)) => unsafe {
rb.to_jsval(*cx, rval);
},
Some(Texture(texture)) => unsafe {
texture.to_jsval(*cx, rval);
},
_ => rval.set(NullValue()),
}
return Ok(());
}
match pname {
constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => {},
_ => match fb.attachment(attachment) {
Some(webgl_attachment) => match pname {
constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE |
constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => {},
_ => match webgl_attachment {
WebGLFramebufferAttachmentRoot::Renderbuffer(_) => return Err(InvalidEnum),
WebGLFramebufferAttachmentRoot::Texture(_) => match pname {
constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER => {},
_ => return Err(InvalidEnum),
},
},
},
None => return Err(InvalidOperation),
},
}
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetFramebufferAttachmentParameter(
target, attachment, pname, sender,
));
let retval = receiver.recv().unwrap();
rval.set(Int32Value(retval));
Ok(())
}
fn clearbuffer_array_size(&self, buffer: u32, draw_buffer: i32) -> WebGLResult<usize> {
match buffer {
constants::COLOR => {
if draw_buffer < 0 || draw_buffer as u32 >= self.base.limits().max_draw_buffers {
return Err(InvalidValue);
}
Ok(4)
},
constants::DEPTH | constants::STENCIL | constants::DEPTH_STENCIL => {
if draw_buffer != 0 {
return Err(InvalidValue);
}
Ok(1)
},
_ => unreachable!(),
}
}
fn clear_buffer<T: Clone>(
&self,
buffer: u32,
draw_buffer: i32,
valid_buffers: &[u32],
src_offset: u32,
array: Vec<T>,
msg: fn(u32, i32, Vec<T>) -> WebGLCommand,
) {
if !valid_buffers.contains(&buffer) {
return self.base.webgl_error(InvalidEnum);
}
let array_size = handle_potential_webgl_error!(
self.base,
self.clearbuffer_array_size(buffer, draw_buffer),
return
);
let src_offset = src_offset as usize;
if array.len() < src_offset + array_size {
return self.base.webgl_error(InvalidValue);
}
let array = array[src_offset..src_offset + array_size].to_vec();
self.base.send_command(msg(buffer, draw_buffer, array));
}
fn valid_fb_attachment_values(&self, target: u32, attachments: &[u32]) -> bool {
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => {
self.base.webgl_error(InvalidEnum);
return false;
},
};
if let Some(fb) = fb_slot.get() {
if fb.check_status() != constants::FRAMEBUFFER_COMPLETE {
return false;
}
for &attachment in attachments {
match attachment {
constants::DEPTH_ATTACHMENT |
constants::STENCIL_ATTACHMENT |
constants::DEPTH_STENCIL_ATTACHMENT => {},
constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
let last_slot = constants::COLOR_ATTACHMENT0 +
self.base.limits().max_color_attachments -
1;
if last_slot < attachment {
return false;
}
},
_ => return false,
}
}
} else {
for &attachment in attachments {
match attachment {
constants::COLOR | constants::DEPTH | constants::STENCIL => {},
_ => return false,
}
}
}
true
}
fn vertex_attrib_i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) {
if index >= self.base.limits().max_vertex_attribs {
return self.base.webgl_error(InvalidValue);
}
self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Int(x, y, z, w);
self.current_vao()
.set_vertex_attrib_type(index, constants::INT);
self.base
.send_command(WebGLCommand::VertexAttribI(index, x, y, z, w));
}
fn vertex_attrib_u(&self, index: u32, x: u32, y: u32, z: u32, w: u32) {
if index >= self.base.limits().max_vertex_attribs {
return self.base.webgl_error(InvalidValue);
}
self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Uint(x, y, z, w);
self.current_vao()
.set_vertex_attrib_type(index, constants::UNSIGNED_INT);
self.base
.send_command(WebGLCommand::VertexAttribU(index, x, y, z, w));
}
#[allow(clippy::too_many_arguments)]
fn tex_storage(
&self,
dimensions: u8,
target: u32,
levels: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
) {
let expected_dimensions = match target {
constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP => 2,
constants::TEXTURE_3D | constants::TEXTURE_2D_ARRAY => 3,
_ => return self.base.webgl_error(InvalidEnum),
};
if dimensions != expected_dimensions {
return self.base.webgl_error(InvalidEnum);
}
let validator = TexStorageValidator::new(
&self.base,
dimensions,
target,
levels,
internal_format,
width,
height,
depth,
);
let TexStorageValidatorResult {
texture,
target,
levels,
internal_format,
width,
height,
depth,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return, };
handle_potential_webgl_error!(
self.base,
texture.storage(target, levels, internal_format, width, height, depth)
);
}
}
impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingContext {
fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
self.base.Canvas()
}
fn Flush(&self) {
self.base.Flush()
}
fn Finish(&self) {
self.base.Finish()
}
fn DrawingBufferWidth(&self) -> i32 {
self.base.DrawingBufferWidth()
}
fn DrawingBufferHeight(&self) -> i32 {
self.base.DrawingBufferHeight()
}
fn GetBufferParameter(
&self,
_cx: JSContext,
target: u32,
parameter: u32,
mut retval: MutableHandleValue,
) {
let buffer = handle_potential_webgl_error!(
self.base,
self.bound_buffer(target),
return retval.set(NullValue())
);
self.base.get_buffer_param(buffer, parameter, retval)
}
#[allow(unsafe_code)]
fn GetParameter(&self, cx: JSContext, parameter: u32, mut rval: MutableHandleValue) {
match parameter {
constants::VERSION => unsafe {
"WebGL 2.0".to_jsval(*cx, rval);
return;
},
constants::SHADING_LANGUAGE_VERSION => unsafe {
"WebGL GLSL ES 3.00".to_jsval(*cx, rval);
return;
},
constants::MAX_CLIENT_WAIT_TIMEOUT_WEBGL => {
rval.set(DoubleValue(
self.base.limits().max_client_wait_timeout_webgl.as_nanos() as f64,
));
return;
},
constants::MAX_SERVER_WAIT_TIMEOUT => {
rval.set(DoubleValue(
self.base.limits().max_server_wait_timeout.as_nanos() as f64,
));
return;
},
constants::SAMPLER_BINDING => unsafe {
let idx = (self.base.textures().active_unit_enum() - constants::TEXTURE0) as usize;
assert!(idx < self.samplers.len());
let sampler = self.samplers[idx].get();
sampler.to_jsval(*cx, rval);
return;
},
constants::COPY_READ_BUFFER_BINDING => unsafe {
self.bound_copy_read_buffer.get().to_jsval(*cx, rval);
return;
},
constants::COPY_WRITE_BUFFER_BINDING => unsafe {
self.bound_copy_write_buffer.get().to_jsval(*cx, rval);
return;
},
constants::PIXEL_PACK_BUFFER_BINDING => unsafe {
self.bound_pixel_pack_buffer.get().to_jsval(*cx, rval);
return;
},
constants::PIXEL_UNPACK_BUFFER_BINDING => unsafe {
self.bound_pixel_unpack_buffer.get().to_jsval(*cx, rval);
return;
},
constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => unsafe {
self.bound_transform_feedback_buffer
.get()
.to_jsval(*cx, rval);
return;
},
constants::UNIFORM_BUFFER_BINDING => unsafe {
self.bound_uniform_buffer.get().to_jsval(*cx, rval);
return;
},
constants::TRANSFORM_FEEDBACK_BINDING => unsafe {
self.current_transform_feedback.get().to_jsval(*cx, rval);
return;
},
constants::ELEMENT_ARRAY_BUFFER_BINDING => unsafe {
let buffer = self.current_vao().element_array_buffer().get();
buffer.to_jsval(*cx, rval);
return;
},
constants::VERTEX_ARRAY_BINDING => unsafe {
let vao = self.current_vao();
let vao = vao.id().map(|_| &*vao);
vao.to_jsval(*cx, rval);
return;
},
constants::READ_FRAMEBUFFER_BINDING => unsafe {
self.base
.get_read_framebuffer_slot()
.get()
.to_jsval(*cx, rval);
return;
},
constants::READ_BUFFER => {
let buffer = match self.base.get_read_framebuffer_slot().get() {
Some(fb) => fb.read_buffer(),
None => self.default_fb_readbuffer.get(),
};
rval.set(UInt32Value(buffer));
return;
},
constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => {
let buffer = match self.base.get_read_framebuffer_slot().get() {
Some(fb) => {
let idx = parameter - constants::DRAW_BUFFER0;
fb.draw_buffer_i(idx as usize)
},
None if parameter == constants::DRAW_BUFFER0 => {
self.default_fb_readbuffer.get()
},
None => constants::NONE,
};
rval.set(UInt32Value(buffer));
return;
},
constants::MAX_TEXTURE_LOD_BIAS => {
rval.set(DoubleValue(self.base.limits().max_texture_lod_bias as f64));
return;
},
constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => {
rval.set(DoubleValue(
self.base.limits().max_combined_fragment_uniform_components as f64,
));
return;
},
constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => {
rval.set(DoubleValue(
self.base.limits().max_combined_vertex_uniform_components as f64,
));
return;
},
constants::MAX_ELEMENT_INDEX => {
rval.set(DoubleValue(self.base.limits().max_element_index as f64));
return;
},
constants::MAX_UNIFORM_BLOCK_SIZE => {
rval.set(DoubleValue(
self.base.limits().max_uniform_block_size as f64,
));
return;
},
constants::MIN_PROGRAM_TEXEL_OFFSET => {
rval.set(Int32Value(self.base.limits().min_program_texel_offset));
return;
},
_ => {},
}
let limit = match parameter {
constants::MAX_3D_TEXTURE_SIZE => Some(self.base.limits().max_3d_texture_size),
constants::MAX_ARRAY_TEXTURE_LAYERS => {
Some(self.base.limits().max_array_texture_layers)
},
constants::MAX_COLOR_ATTACHMENTS => Some(self.base.limits().max_color_attachments),
constants::MAX_COMBINED_UNIFORM_BLOCKS => {
Some(self.base.limits().max_combined_uniform_blocks)
},
constants::MAX_DRAW_BUFFERS => Some(self.base.limits().max_draw_buffers),
constants::MAX_ELEMENTS_INDICES => Some(self.base.limits().max_elements_indices),
constants::MAX_ELEMENTS_VERTICES => Some(self.base.limits().max_elements_vertices),
constants::MAX_FRAGMENT_INPUT_COMPONENTS => {
Some(self.base.limits().max_fragment_input_components)
},
constants::MAX_FRAGMENT_UNIFORM_BLOCKS => {
Some(self.base.limits().max_fragment_uniform_blocks)
},
constants::MAX_FRAGMENT_UNIFORM_COMPONENTS => {
Some(self.base.limits().max_fragment_uniform_components)
},
constants::MAX_PROGRAM_TEXEL_OFFSET => {
Some(self.base.limits().max_program_texel_offset)
},
constants::MAX_SAMPLES => Some(self.base.limits().max_samples),
constants::MAX_UNIFORM_BUFFER_BINDINGS => {
Some(self.base.limits().max_uniform_buffer_bindings)
},
constants::MAX_VARYING_COMPONENTS => Some(self.base.limits().max_varying_components),
constants::MAX_VERTEX_OUTPUT_COMPONENTS => {
Some(self.base.limits().max_vertex_output_components)
},
constants::MAX_VERTEX_UNIFORM_BLOCKS => {
Some(self.base.limits().max_vertex_uniform_blocks)
},
constants::MAX_VERTEX_UNIFORM_COMPONENTS => {
Some(self.base.limits().max_vertex_uniform_components)
},
constants::UNIFORM_BUFFER_OFFSET_ALIGNMENT => {
Some(self.base.limits().uniform_buffer_offset_alignment)
},
_ => None,
};
if let Some(limit) = limit {
rval.set(UInt32Value(limit));
return;
}
self.base.GetParameter(cx, parameter, rval)
}
fn GetTexParameter(&self, cx: JSContext, target: u32, pname: u32, retval: MutableHandleValue) {
self.base.GetTexParameter(cx, target, pname, retval)
}
fn GetError(&self) -> u32 {
self.base.GetError()
}
fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
self.base.GetContextAttributes()
}
fn IsContextLost(&self) -> bool {
self.base.IsContextLost()
}
fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
self.base.GetSupportedExtensions()
}
fn GetExtension(&self, cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> {
self.base.GetExtension(cx, name)
}
fn GetFramebufferAttachmentParameter(
&self,
cx: JSContext,
target: u32,
attachment: u32,
pname: u32,
mut rval: MutableHandleValue,
) {
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => {
self.base.webgl_error(InvalidEnum);
rval.set(NullValue());
return;
},
};
if let Some(fb) = fb_slot.get() {
handle_potential_webgl_error!(
self.base,
fb.validate_transparent(),
return rval.set(NullValue())
);
handle_potential_webgl_error!(
self.base,
self.get_specific_fb_attachment_param(cx, &fb, target, attachment, pname, rval),
rval.set(NullValue())
)
} else {
handle_potential_webgl_error!(
self.base,
self.get_default_fb_attachment_param(attachment, pname, rval),
rval.set(NullValue())
)
}
}
fn GetRenderbufferParameter(
&self,
cx: JSContext,
target: u32,
pname: u32,
retval: MutableHandleValue,
) {
self.base
.GetRenderbufferParameter(cx, target, pname, retval)
}
fn ActiveTexture(&self, texture: u32) {
self.base.ActiveTexture(texture)
}
fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
self.base.BlendColor(r, g, b, a)
}
fn BlendEquation(&self, mode: u32) {
self.base.BlendEquation(mode)
}
fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
self.base.BlendEquationSeparate(mode_rgb, mode_alpha)
}
fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
self.base.BlendFunc(src_factor, dest_factor)
}
fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
self.base
.BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha)
}
fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
self.base.AttachShader(program, shader)
}
fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
self.base.DetachShader(program, shader)
}
fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
self.base.BindAttribLocation(program, index, name)
}
fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
let current_vao;
let slot = match target {
constants::COPY_READ_BUFFER => &self.bound_copy_read_buffer,
constants::COPY_WRITE_BUFFER => &self.bound_copy_write_buffer,
constants::PIXEL_PACK_BUFFER => &self.bound_pixel_pack_buffer,
constants::PIXEL_UNPACK_BUFFER => &self.bound_pixel_unpack_buffer,
constants::TRANSFORM_FEEDBACK_BUFFER => &self.bound_transform_feedback_buffer,
constants::UNIFORM_BUFFER => &self.bound_uniform_buffer,
constants::ELEMENT_ARRAY_BUFFER => {
current_vao = self.current_vao();
current_vao.element_array_buffer()
},
_ => return self.base.BindBuffer(target, buffer),
};
self.base.bind_buffer_maybe(slot, target, buffer);
}
fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
handle_potential_webgl_error!(
self.base,
self.base.validate_new_framebuffer_binding(framebuffer),
return
);
let (bind_read, bind_draw) = match target {
constants::FRAMEBUFFER => (true, true),
constants::READ_FRAMEBUFFER => (true, false),
constants::DRAW_FRAMEBUFFER => (false, true),
_ => return self.base.webgl_error(InvalidEnum),
};
if bind_read {
self.base.bind_framebuffer_to(
target,
framebuffer,
self.base.get_read_framebuffer_slot(),
);
}
if bind_draw {
self.base.bind_framebuffer_to(
target,
framebuffer,
self.base.get_draw_framebuffer_slot(),
);
}
}
fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
self.base.BindRenderbuffer(target, renderbuffer)
}
fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
self.base.BindTexture(target, texture)
}
fn GenerateMipmap(&self, target: u32) {
self.base.GenerateMipmap(target)
}
fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) {
let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
let bound_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
self.base.buffer_data(target, data, usage, bound_buffer)
}
fn BufferData(&self, target: u32, size: i64, usage: u32) {
let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
let bound_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
self.base.buffer_data_(target, size, usage, bound_buffer)
}
#[allow(unsafe_code)]
fn BufferData__(
&self,
target: u32,
data: CustomAutoRooterGuard<ArrayBufferView>,
usage: u32,
elem_offset: u32,
length: u32,
) {
let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
let bound_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
let bound_buffer =
handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
let elem_size = typedarray_elem_size(data.get_array_type());
let elem_count = data.len() / elem_size;
let elem_offset = elem_offset as usize;
let byte_offset = elem_offset * elem_size;
if byte_offset > data.len() {
return self.base.webgl_error(InvalidValue);
}
let copy_count = if length == 0 {
elem_count - elem_offset
} else {
length as usize
};
if copy_count == 0 {
return;
}
let copy_bytes = copy_count * elem_size;
if byte_offset + copy_bytes > data.len() {
return self.base.webgl_error(InvalidValue);
}
let data_end = byte_offset + copy_bytes;
let data: &[u8] = unsafe { &data.as_slice()[byte_offset..data_end] };
handle_potential_webgl_error!(self.base, bound_buffer.buffer_data(target, data, usage));
}
fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) {
let bound_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
self.base
.buffer_sub_data(target, offset, data, bound_buffer)
}
#[allow(unsafe_code)]
fn BufferSubData_(
&self,
target: u32,
dst_byte_offset: i64,
src_data: CustomAutoRooterGuard<ArrayBufferView>,
src_elem_offset: u32,
length: u32,
) {
let bound_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
let bound_buffer =
handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
let src_elem_size = typedarray_elem_size(src_data.get_array_type());
let src_elem_count = src_data.len() / src_elem_size;
let src_elem_offset = src_elem_offset as usize;
let src_byte_offset = src_elem_offset * src_elem_size;
if dst_byte_offset < 0 || src_byte_offset > src_data.len() {
return self.base.webgl_error(InvalidValue);
}
let copy_count = if length == 0 {
src_elem_count - src_elem_offset
} else {
length as usize
};
if copy_count == 0 {
return;
}
let copy_bytes = copy_count * src_elem_size;
let dst_byte_offset = dst_byte_offset as usize;
if dst_byte_offset + copy_bytes > bound_buffer.capacity() ||
src_byte_offset + copy_bytes > src_data.len()
{
return self.base.webgl_error(InvalidValue);
}
let (sender, receiver) = ipc::bytes_channel().unwrap();
self.base.send_command(WebGLCommand::BufferSubData(
target,
dst_byte_offset as isize,
receiver,
));
let src_end = src_byte_offset + copy_bytes;
let data: &[u8] = unsafe { &src_data.as_slice()[src_byte_offset..src_end] };
sender.send(data).unwrap();
}
fn CopyBufferSubData(
&self,
read_target: u32,
write_target: u32,
read_offset: i64,
write_offset: i64,
size: i64,
) {
if read_offset < 0 || write_offset < 0 || size < 0 {
return self.base.webgl_error(InvalidValue);
}
let read_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(read_target), return);
let read_buffer =
handle_potential_webgl_error!(self.base, read_buffer.ok_or(InvalidOperation), return);
let write_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(write_target), return);
let write_buffer =
handle_potential_webgl_error!(self.base, write_buffer.ok_or(InvalidOperation), return);
let read_until = read_offset + size;
let write_until = write_offset + size;
if read_until as usize > read_buffer.capacity() ||
write_until as usize > write_buffer.capacity()
{
return self.base.webgl_error(InvalidValue);
}
if read_target == write_target {
let is_separate = read_until <= write_offset || write_until <= read_offset;
if !is_separate {
return self.base.webgl_error(InvalidValue);
}
}
let src_is_elemarray = read_buffer
.target()
.is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
let dst_is_elemarray = write_buffer
.target()
.is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
if src_is_elemarray != dst_is_elemarray {
return self.base.webgl_error(InvalidOperation);
}
self.base.send_command(WebGLCommand::CopyBufferSubData(
read_target,
write_target,
read_offset,
write_offset,
size,
));
}
#[allow(unsafe_code)]
fn GetBufferSubData(
&self,
target: u32,
src_byte_offset: i64,
mut dst_buffer: CustomAutoRooterGuard<ArrayBufferView>,
dst_elem_offset: u32,
length: u32,
) {
let bound_buffer =
handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
let bound_buffer =
handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
let dst_elem_size = typedarray_elem_size(dst_buffer.get_array_type());
let dst_elem_count = dst_buffer.len() / dst_elem_size;
let dst_elem_offset = dst_elem_offset as usize;
let dst_byte_offset = dst_elem_offset * dst_elem_size;
if src_byte_offset < 0 || dst_byte_offset > dst_buffer.len() {
return self.base.webgl_error(InvalidValue);
}
let copy_count = if length == 0 {
dst_elem_count - dst_elem_offset
} else {
length as usize
};
if copy_count == 0 {
return;
}
let copy_bytes = copy_count * dst_elem_size;
let src_byte_offset = src_byte_offset as usize;
if src_byte_offset + copy_bytes > bound_buffer.capacity() ||
dst_byte_offset + copy_bytes > dst_buffer.len()
{
return self.base.webgl_error(InvalidValue);
}
let (sender, receiver) = ipc::bytes_channel().unwrap();
self.base.send_command(WebGLCommand::GetBufferSubData(
target,
src_byte_offset,
copy_bytes,
sender,
));
let data = receiver.recv().unwrap();
let dst_end = dst_byte_offset + copy_bytes;
unsafe {
dst_buffer.as_mut_slice()[dst_byte_offset..dst_end].copy_from_slice(&data);
}
}
#[allow(unsafe_code)]
fn CompressedTexImage2D(
&self,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
pixels: CustomAutoRooterGuard<ArrayBufferView>,
src_offset: u32,
src_length_override: u32,
) {
let mut data = unsafe { pixels.as_slice() };
let start = src_offset as usize;
let end = (src_offset + src_length_override) as usize;
if start > data.len() || end > data.len() {
self.base.webgl_error(InvalidValue);
return;
}
if src_length_override != 0 {
data = &data[start..end];
}
self.base.compressed_tex_image_2d(
target,
level,
internal_format,
width,
height,
border,
data,
)
}
#[allow(unsafe_code)]
fn CompressedTexSubImage2D(
&self,
target: u32,
level: i32,
xoffset: i32,
yoffset: i32,
width: i32,
height: i32,
format: u32,
pixels: CustomAutoRooterGuard<ArrayBufferView>,
src_offset: u32,
src_length_override: u32,
) {
let mut data = unsafe { pixels.as_slice() };
let start = src_offset as usize;
let end = (src_offset + src_length_override) as usize;
if start > data.len() || end > data.len() {
self.base.webgl_error(InvalidValue);
return;
}
if src_length_override != 0 {
data = &data[start..end];
}
self.base.compressed_tex_sub_image_2d(
target, level, xoffset, yoffset, width, height, format, data,
)
}
fn CopyTexImage2D(
&self,
target: u32,
level: i32,
internal_format: u32,
x: i32,
y: i32,
width: i32,
height: i32,
border: i32,
) {
self.base
.CopyTexImage2D(target, level, internal_format, x, y, width, height, border)
}
fn CopyTexSubImage2D(
&self,
target: u32,
level: i32,
xoffset: i32,
yoffset: i32,
x: i32,
y: i32,
width: i32,
height: i32,
) {
self.base
.CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height)
}
fn Clear(&self, mask: u32) {
self.base.Clear(mask)
}
fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
self.base.ClearColor(red, green, blue, alpha)
}
fn ClearDepth(&self, depth: f32) {
self.base.ClearDepth(depth)
}
fn ClearStencil(&self, stencil: i32) {
self.base.ClearStencil(stencil)
}
fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
self.base.ColorMask(r, g, b, a)
}
fn CullFace(&self, mode: u32) {
self.base.CullFace(mode)
}
fn FrontFace(&self, mode: u32) {
self.base.FrontFace(mode)
}
fn DepthFunc(&self, func: u32) {
self.base.DepthFunc(func)
}
fn DepthMask(&self, flag: bool) {
self.base.DepthMask(flag)
}
fn DepthRange(&self, near: f32, far: f32) {
self.base.DepthRange(near, far)
}
fn Enable(&self, cap: u32) {
match cap {
constants::RASTERIZER_DISCARD => {
self.enable_rasterizer_discard.set(true);
self.base.send_command(WebGLCommand::Enable(cap));
},
_ => self.base.Enable(cap),
}
}
fn Disable(&self, cap: u32) {
match cap {
constants::RASTERIZER_DISCARD => {
self.enable_rasterizer_discard.set(false);
self.base.send_command(WebGLCommand::Disable(cap));
},
_ => self.base.Disable(cap),
}
}
fn CompileShader(&self, shader: &WebGLShader) {
self.base.CompileShader(shader)
}
fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> {
self.base.CreateBuffer()
}
fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
self.base.CreateFramebuffer()
}
fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
self.base.CreateRenderbuffer()
}
fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> {
self.base.CreateTexture()
}
fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> {
self.base.CreateProgram()
}
fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
self.base.CreateShader(shader_type)
}
fn CreateVertexArray(&self) -> Option<DomRoot<WebGLVertexArrayObject>> {
self.base.create_vertex_array_webgl2()
}
fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
let buffer = match buffer {
Some(buffer) => buffer,
None => return,
};
handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
if buffer.is_marked_for_deletion() {
return;
}
self.current_vao().unbind_buffer(buffer);
self.unbind_from(self.base.array_buffer_slot(), buffer);
self.unbind_from(&self.bound_copy_read_buffer, buffer);
self.unbind_from(&self.bound_copy_write_buffer, buffer);
self.unbind_from(&self.bound_pixel_pack_buffer, buffer);
self.unbind_from(&self.bound_pixel_unpack_buffer, buffer);
self.unbind_from(&self.bound_transform_feedback_buffer, buffer);
self.unbind_from(&self.bound_uniform_buffer, buffer);
for binding in self.indexed_uniform_buffer_bindings.iter() {
self.unbind_from(&binding.buffer, buffer);
}
for binding in self.indexed_transform_feedback_buffer_bindings.iter() {
self.unbind_from(&binding.buffer, buffer);
}
buffer.mark_for_deletion(Operation::Infallible);
}
fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
self.base.DeleteFramebuffer(framebuffer)
}
fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
self.base.DeleteRenderbuffer(renderbuffer)
}
fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
self.base.DeleteTexture(texture)
}
fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
self.base.DeleteProgram(program)
}
fn DeleteShader(&self, shader: Option<&WebGLShader>) {
self.base.DeleteShader(shader)
}
fn DeleteVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) {
self.base.delete_vertex_array_webgl2(vertex_array);
}
fn DrawArrays(&self, mode: u32, first: i32, count: i32) {
self.validate_uniform_block_for_draw();
self.validate_vertex_attribs_for_draw();
self.base.DrawArrays(mode, first, count)
}
fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
self.validate_uniform_block_for_draw();
self.validate_vertex_attribs_for_draw();
self.base.DrawElements(mode, count, type_, offset)
}
fn EnableVertexAttribArray(&self, attrib_id: u32) {
self.base.EnableVertexAttribArray(attrib_id)
}
fn DisableVertexAttribArray(&self, attrib_id: u32) {
self.base.DisableVertexAttribArray(attrib_id)
}
fn GetActiveUniform(
&self,
program: &WebGLProgram,
index: u32,
) -> Option<DomRoot<WebGLActiveInfo>> {
self.base.GetActiveUniform(program, index)
}
fn GetActiveAttrib(
&self,
program: &WebGLProgram,
index: u32,
) -> Option<DomRoot<WebGLActiveInfo>> {
self.base.GetActiveAttrib(program, index)
}
fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
self.base.GetAttribLocation(program, name)
}
fn GetFragDataLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return -1);
handle_potential_webgl_error!(self.base, program.get_frag_data_location(name), -1)
}
fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
self.base.GetProgramInfoLog(program)
}
fn GetProgramParameter(
&self,
cx: JSContext,
program: &WebGLProgram,
param_id: u32,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return retval.set(NullValue())
);
if program.is_deleted() {
self.base.webgl_error(InvalidOperation);
return retval.set(NullValue());
}
match param_id {
constants::TRANSFORM_FEEDBACK_VARYINGS => {
retval.set(Int32Value(program.transform_feedback_varyings_length()))
},
constants::TRANSFORM_FEEDBACK_BUFFER_MODE => {
retval.set(Int32Value(program.transform_feedback_buffer_mode()))
},
_ => self.base.GetProgramParameter(cx, program, param_id, retval),
}
}
fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
self.base.GetShaderInfoLog(shader)
}
fn GetShaderParameter(
&self,
cx: JSContext,
shader: &WebGLShader,
param_id: u32,
retval: MutableHandleValue,
) {
self.base.GetShaderParameter(cx, shader, param_id, retval)
}
fn GetShaderPrecisionFormat(
&self,
shader_type: u32,
precision_type: u32,
) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
self.base
.GetShaderPrecisionFormat(shader_type, precision_type)
}
#[allow(unsafe_code)]
fn GetIndexedParameter(
&self,
cx: JSContext,
target: u32,
index: u32,
mut retval: MutableHandleValue,
) {
let bindings = match target {
constants::TRANSFORM_FEEDBACK_BUFFER_BINDING |
constants::TRANSFORM_FEEDBACK_BUFFER_SIZE |
constants::TRANSFORM_FEEDBACK_BUFFER_START => {
&self.indexed_transform_feedback_buffer_bindings
},
constants::UNIFORM_BUFFER_BINDING |
constants::UNIFORM_BUFFER_SIZE |
constants::UNIFORM_BUFFER_START => &self.indexed_uniform_buffer_bindings,
_ => {
self.base.webgl_error(InvalidEnum);
return retval.set(NullValue());
},
};
let binding = match bindings.get(index as usize) {
Some(binding) => binding,
None => {
self.base.webgl_error(InvalidValue);
return retval.set(NullValue());
},
};
match target {
constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => unsafe {
binding.buffer.get().to_jsval(*cx, retval)
},
constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => {
retval.set(Int32Value(binding.start.get() as _))
},
constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => {
retval.set(Int32Value(binding.size.get() as _))
},
_ => unreachable!(),
}
}
fn GetUniformLocation(
&self,
program: &WebGLProgram,
name: DOMString,
) -> Option<DomRoot<WebGLUniformLocation>> {
self.base.GetUniformLocation(program, name)
}
fn GetVertexAttrib(&self, cx: JSContext, index: u32, pname: u32, retval: MutableHandleValue) {
self.base.GetVertexAttrib(cx, index, pname, retval)
}
fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
self.base.GetVertexAttribOffset(index, pname)
}
fn Hint(&self, target: u32, mode: u32) {
self.base.Hint(target, mode)
}
fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
self.base.IsBuffer(buffer)
}
fn IsEnabled(&self, cap: u32) -> bool {
match cap {
constants::RASTERIZER_DISCARD => self.enable_rasterizer_discard.get(),
_ => self.base.IsEnabled(cap),
}
}
fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
self.base.IsFramebuffer(frame_buffer)
}
fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
self.base.IsProgram(program)
}
fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
self.base.IsRenderbuffer(render_buffer)
}
fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
self.base.IsShader(shader)
}
fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
self.base.IsTexture(texture)
}
fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool {
self.base.is_vertex_array_webgl2(vertex_array)
}
fn LineWidth(&self, width: f32) {
self.base.LineWidth(width)
}
fn PixelStorei(&self, param_name: u32, param_value: i32) {
if param_value < 0 {
return self.base.webgl_error(InvalidValue);
}
match param_name {
constants::PACK_ROW_LENGTH => self.texture_pack_row_length.set(param_value as _),
constants::PACK_SKIP_PIXELS => self.texture_pack_skip_pixels.set(param_value as _),
constants::PACK_SKIP_ROWS => self.texture_pack_skip_rows.set(param_value as _),
_ => self.base.PixelStorei(param_name, param_value),
}
}
fn PolygonOffset(&self, factor: f32, units: f32) {
self.base.PolygonOffset(factor, units)
}
fn ReadPixels(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
format: u32,
pixel_type: u32,
mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
) {
let pixels =
handle_potential_webgl_error!(self.base, pixels.as_mut().ok_or(InvalidValue), return);
self.read_pixels_into(x, y, width, height, format, pixel_type, pixels, 0)
}
fn ReadPixels_(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
format: u32,
pixel_type: u32,
dst_byte_offset: i64,
) {
handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
let dst = match self.bound_pixel_pack_buffer.get() {
Some(buffer) => buffer,
None => return self.base.webgl_error(InvalidOperation),
};
if dst_byte_offset < 0 {
return self.base.webgl_error(InvalidValue);
}
let dst_byte_offset = dst_byte_offset as usize;
if dst_byte_offset > dst.capacity() {
return self.base.webgl_error(InvalidOperation);
}
let ReadPixelsAllowedFormats {
array_types: _,
channels: bytes_per_pixel,
} = match self.calc_read_pixel_formats(pixel_type, format) {
Ok(result) => result,
Err(error) => return self.base.webgl_error(error),
};
if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
return self.base.webgl_error(InvalidOperation);
}
let ReadPixelsSizes {
row_stride: _,
skipped_bytes,
size,
} = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
Ok(result) => result,
Err(error) => return self.base.webgl_error(error),
};
let dst_end = dst_byte_offset + skipped_bytes + size;
if dst.capacity() < dst_end {
return self.base.webgl_error(InvalidOperation);
}
{
let (fb_width, fb_height) = handle_potential_webgl_error!(
self.base,
self.base
.get_current_framebuffer_size()
.ok_or(InvalidOperation),
return
);
let src_origin = Point2D::new(x, y);
let src_size = Size2D::new(width as u32, height as u32);
let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
if pixels::clip(src_origin, src_size.to_u64(), fb_size.to_u64()).is_none() {
return;
}
}
let src_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
self.base.send_command(WebGLCommand::PixelStorei(
constants::PACK_ALIGNMENT,
self.base.get_texture_packing_alignment() as _,
));
self.base.send_command(WebGLCommand::PixelStorei(
constants::PACK_ROW_LENGTH,
self.texture_pack_row_length.get() as _,
));
self.base.send_command(WebGLCommand::PixelStorei(
constants::PACK_SKIP_ROWS,
self.texture_pack_skip_rows.get() as _,
));
self.base.send_command(WebGLCommand::PixelStorei(
constants::PACK_SKIP_PIXELS,
self.texture_pack_skip_pixels.get() as _,
));
self.base.send_command(WebGLCommand::ReadPixelsPP(
src_rect,
format,
pixel_type,
dst_byte_offset,
));
}
#[allow(unsafe_code)]
fn ReadPixels__(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
format: u32,
pixel_type: u32,
mut dst: CustomAutoRooterGuard<ArrayBufferView>,
dst_elem_offset: u32,
) {
self.read_pixels_into(
x,
y,
width,
height,
format,
pixel_type,
&mut dst,
dst_elem_offset,
)
}
fn SampleCoverage(&self, value: f32, invert: bool) {
self.base.SampleCoverage(value, invert)
}
fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
self.base.Scissor(x, y, width, height)
}
fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
self.base.StencilFunc(func, ref_, mask)
}
fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
self.base.StencilFuncSeparate(face, func, ref_, mask)
}
fn StencilMask(&self, mask: u32) {
self.base.StencilMask(mask)
}
fn StencilMaskSeparate(&self, face: u32, mask: u32) {
self.base.StencilMaskSeparate(face, mask)
}
fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
self.base.StencilOp(fail, zfail, zpass)
}
fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
self.base.StencilOpSeparate(face, fail, zfail, zpass)
}
fn LinkProgram(&self, program: &WebGLProgram) {
self.base.LinkProgram(program)
}
fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
self.base.ShaderSource(shader, source)
}
fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
self.base.GetShaderSource(shader)
}
fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
self.base.Uniform1f(location, val)
}
fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
self.base.Uniform1i(location, val)
}
fn Uniform1iv(
&self,
location: Option<&WebGLUniformLocation>,
v: Int32ArrayOrLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform1iv(location, v, src_offset, src_length)
}
fn Uniform1ui(&self, location: Option<&WebGLUniformLocation>, val: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL | constants::UNSIGNED_INT => (),
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform1ui(location.id(), val));
Ok(())
});
}
fn Uniform1uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL |
constants::UNSIGNED_INT |
constants::SAMPLER_2D |
constants::SAMPLER_CUBE => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section_uint(val, src_offset, src_length, 1, location)?;
match location.type_() {
constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
for &v in val
.iter()
.take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
{
if v >= self.base.limits().max_combined_texture_image_units {
return Err(InvalidValue);
}
}
},
_ => {},
}
self.base
.send_command(WebGLCommand::Uniform1uiv(location.id(), val));
Ok(())
});
}
fn Uniform1fv(
&self,
location: Option<&WebGLUniformLocation>,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform1fv(location, v, src_offset, src_length);
}
fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
self.base.Uniform2f(location, x, y)
}
fn Uniform2fv(
&self,
location: Option<&WebGLUniformLocation>,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform2fv(location, v, src_offset, src_length);
}
fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
self.base.Uniform2i(location, x, y)
}
fn Uniform2iv(
&self,
location: Option<&WebGLUniformLocation>,
v: Int32ArrayOrLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform2iv(location, v, src_offset, src_length)
}
fn Uniform2ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform2ui(location.id(), x, y));
Ok(())
});
}
fn Uniform2uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section_uint(val, src_offset, src_length, 2, location)?;
self.base
.send_command(WebGLCommand::Uniform2uiv(location.id(), val));
Ok(())
});
}
fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
self.base.Uniform3f(location, x, y, z)
}
fn Uniform3fv(
&self,
location: Option<&WebGLUniformLocation>,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform3fv(location, v, src_offset, src_length);
}
fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
self.base.Uniform3i(location, x, y, z)
}
fn Uniform3iv(
&self,
location: Option<&WebGLUniformLocation>,
v: Int32ArrayOrLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform3iv(location, v, src_offset, src_length)
}
fn Uniform3ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform3ui(location.id(), x, y, z));
Ok(())
});
}
fn Uniform3uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section_uint(val, src_offset, src_length, 3, location)?;
self.base
.send_command(WebGLCommand::Uniform3uiv(location.id(), val));
Ok(())
});
}
fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
self.base.Uniform4i(location, x, y, z, w)
}
fn Uniform4iv(
&self,
location: Option<&WebGLUniformLocation>,
v: Int32ArrayOrLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform4iv(location, v, src_offset, src_length)
}
fn Uniform4ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32, w: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform4ui(location.id(), x, y, z, w));
Ok(())
});
}
fn Uniform4uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section_uint(val, src_offset, src_length, 4, location)?;
self.base
.send_command(WebGLCommand::Uniform4uiv(location.id(), val));
Ok(())
});
}
fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
self.base.Uniform4f(location, x, y, z, w)
}
fn Uniform4fv(
&self,
location: Option<&WebGLUniformLocation>,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.uniform4fv(location, v, src_offset, src_length);
}
fn UniformMatrix2fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base
.uniform_matrix_2fv(location, transpose, v, src_offset, src_length)
}
fn UniformMatrix3fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base
.uniform_matrix_3fv(location, transpose, v, src_offset, src_length)
}
fn UniformMatrix4fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
v: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base
.uniform_matrix_4fv(location, transpose, v, src_offset, src_length)
}
fn UniformMatrix3x2fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
val: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::FLOAT_MAT3x2 => {},
_ => return Err(InvalidOperation),
}
let val = self.base.uniform_matrix_section(
val,
src_offset,
src_length,
transpose,
3 * 2,
location,
)?;
self.base
.send_command(WebGLCommand::UniformMatrix3x2fv(location.id(), val));
Ok(())
});
}
fn UniformMatrix4x2fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
val: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::FLOAT_MAT4x2 => {},
_ => return Err(InvalidOperation),
}
let val = self.base.uniform_matrix_section(
val,
src_offset,
src_length,
transpose,
4 * 2,
location,
)?;
self.base
.send_command(WebGLCommand::UniformMatrix4x2fv(location.id(), val));
Ok(())
});
}
fn UniformMatrix2x3fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
val: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::FLOAT_MAT2x3 => {},
_ => return Err(InvalidOperation),
}
let val = self.base.uniform_matrix_section(
val,
src_offset,
src_length,
transpose,
2 * 3,
location,
)?;
self.base
.send_command(WebGLCommand::UniformMatrix2x3fv(location.id(), val));
Ok(())
});
}
fn UniformMatrix4x3fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
val: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::FLOAT_MAT4x3 => {},
_ => return Err(InvalidOperation),
}
let val = self.base.uniform_matrix_section(
val,
src_offset,
src_length,
transpose,
4 * 3,
location,
)?;
self.base
.send_command(WebGLCommand::UniformMatrix4x3fv(location.id(), val));
Ok(())
});
}
fn UniformMatrix2x4fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
val: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::FLOAT_MAT2x4 => {},
_ => return Err(InvalidOperation),
}
let val = self.base.uniform_matrix_section(
val,
src_offset,
src_length,
transpose,
2 * 4,
location,
)?;
self.base
.send_command(WebGLCommand::UniformMatrix2x4fv(location.id(), val));
Ok(())
});
}
fn UniformMatrix3x4fv(
&self,
location: Option<&WebGLUniformLocation>,
transpose: bool,
val: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::FLOAT_MAT3x4 => {},
_ => return Err(InvalidOperation),
}
let val = self.base.uniform_matrix_section(
val,
src_offset,
src_length,
transpose,
3 * 4,
location,
)?;
self.base
.send_command(WebGLCommand::UniformMatrix3x4fv(location.id(), val));
Ok(())
});
}
#[allow(unsafe_code)]
fn GetUniform(
&self,
cx: JSContext,
program: &WebGLProgram,
location: &WebGLUniformLocation,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.uniform_check_program(program, location),
return retval.set(NullValue())
);
let triple = (&*self.base, program.id(), location.id());
match location.type_() {
constants::UNSIGNED_INT => retval.set(UInt32Value(uniform_get(
triple,
WebGLCommand::GetUniformUint,
))),
constants::UNSIGNED_INT_VEC2 => unsafe {
uniform_typed::<Uint32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformUint2),
retval,
)
},
constants::UNSIGNED_INT_VEC3 => unsafe {
uniform_typed::<Uint32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformUint3),
retval,
)
},
constants::UNSIGNED_INT_VEC4 => unsafe {
uniform_typed::<Uint32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformUint4),
retval,
)
},
constants::FLOAT_MAT2x3 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat2x3),
retval,
)
},
constants::FLOAT_MAT2x4 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat2x4),
retval,
)
},
constants::FLOAT_MAT3x2 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat3x2),
retval,
)
},
constants::FLOAT_MAT3x4 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat3x4),
retval,
)
},
constants::FLOAT_MAT4x2 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat4x2),
retval,
)
},
constants::FLOAT_MAT4x3 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat4x3),
retval,
)
},
constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => {
retval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
},
_ => self.base.GetUniform(cx, program, location, retval),
}
}
fn UseProgram(&self, program: Option<&WebGLProgram>) {
self.base.UseProgram(program)
}
fn ValidateProgram(&self, program: &WebGLProgram) {
self.base.ValidateProgram(program)
}
fn VertexAttrib1f(&self, indx: u32, x: f32) {
self.base.VertexAttrib1f(indx, x)
}
fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
self.base.VertexAttrib1fv(indx, v)
}
fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) {
self.base.VertexAttrib2f(indx, x, y)
}
fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
self.base.VertexAttrib2fv(indx, v)
}
fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) {
self.base.VertexAttrib3f(indx, x, y, z)
}
fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
self.base.VertexAttrib3fv(indx, v)
}
fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
self.base.VertexAttrib4f(indx, x, y, z, w)
}
fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
self.base.VertexAttrib4fv(indx, v)
}
fn VertexAttribI4i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) {
self.vertex_attrib_i(index, x, y, z, w)
}
fn VertexAttribI4iv(&self, index: u32, v: Int32ArrayOrLongSequence) {
let values = match v {
Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
Int32ArrayOrLongSequence::LongSequence(v) => v,
};
if values.len() < 4 {
return self.base.webgl_error(InvalidValue);
}
self.vertex_attrib_i(index, values[0], values[1], values[2], values[3]);
}
fn VertexAttribI4ui(&self, index: u32, x: u32, y: u32, z: u32, w: u32) {
self.vertex_attrib_u(index, x, y, z, w)
}
fn VertexAttribI4uiv(&self, index: u32, v: Uint32ArrayOrUnsignedLongSequence) {
let values = match v {
Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
};
if values.len() < 4 {
return self.base.webgl_error(InvalidValue);
}
self.vertex_attrib_u(index, values[0], values[1], values[2], values[3]);
}
fn VertexAttribPointer(
&self,
attrib_id: u32,
size: i32,
data_type: u32,
normalized: bool,
stride: i32,
offset: i64,
) {
self.base
.VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset)
}
fn VertexAttribIPointer(&self, index: u32, size: i32, type_: u32, stride: i32, offset: i64) {
match type_ {
constants::BYTE |
constants::UNSIGNED_BYTE |
constants::SHORT |
constants::UNSIGNED_SHORT |
constants::INT |
constants::UNSIGNED_INT => {},
_ => return self.base.webgl_error(InvalidEnum),
};
self.base
.VertexAttribPointer(index, size, type_, false, stride, offset)
}
fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
self.base.Viewport(x, y, width, height)
}
fn TexImage2D(
&self,
target: u32,
level: i32,
internal_format: i32,
width: i32,
height: i32,
border: i32,
format: u32,
data_type: u32,
pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
) -> Fallible<()> {
self.base.TexImage2D(
target,
level,
internal_format,
width,
height,
border,
format,
data_type,
pixels,
)
}
fn TexImage2D_(
&self,
target: u32,
level: i32,
internal_format: i32,
format: u32,
data_type: u32,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
) -> ErrorResult {
self.base
.TexImage2D_(target, level, internal_format, format, data_type, source)
}
fn TexImage2D__(
&self,
target: u32,
level: i32,
internalformat: i32,
width: i32,
height: i32,
border: i32,
format: u32,
type_: u32,
pbo_offset: i64,
) -> Fallible<()> {
let pixel_unpack_buffer = match self.bound_pixel_unpack_buffer.get() {
Some(pixel_unpack_buffer) => pixel_unpack_buffer,
None => {
self.base.webgl_error(InvalidOperation);
return Ok(());
},
};
if let Some(tf_buffer) = self.bound_transform_feedback_buffer.get() {
if pixel_unpack_buffer == tf_buffer {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
}
if pbo_offset < 0 || pbo_offset as usize > pixel_unpack_buffer.capacity() {
self.base.webgl_error(InvalidValue);
return Ok(());
}
let unpacking_alignment = self.base.texture_unpacking_alignment();
let validator = TexImage2DValidator::new(
&self.base,
target,
level,
internalformat as u32,
width,
height,
border,
format,
type_,
);
let TexImage2DValidatorResult {
texture,
target,
width,
height,
level,
border,
internal_format,
format,
data_type,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()),
};
self.base.tex_image_2d(
&texture,
target,
data_type,
internal_format,
format,
level,
border,
unpacking_alignment,
Size2D::new(width, height),
TexSource::BufferOffset(pbo_offset),
);
Ok(())
}
fn TexImage2D___(
&self,
target: u32,
level: i32,
internalformat: i32,
width: i32,
height: i32,
border: i32,
format: u32,
type_: u32,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
) -> Fallible<()> {
if self.bound_pixel_unpack_buffer.get().is_some() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let validator = TexImage2DValidator::new(
&self.base,
target,
level,
internalformat as u32,
width,
height,
border,
format,
type_,
);
let TexImage2DValidatorResult {
texture,
target,
width: _,
height: _,
level,
border,
internal_format,
format,
data_type,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()),
};
let unpacking_alignment = self.base.texture_unpacking_alignment();
let pixels = match self.base.get_image_pixels(source)? {
Some(pixels) => pixels,
None => return Ok(()),
};
self.base.tex_image_2d(
&texture,
target,
data_type,
internal_format,
format,
level,
border,
unpacking_alignment,
pixels.size(),
TexSource::Pixels(pixels),
);
Ok(())
}
#[allow(unsafe_code)]
fn TexImage2D____(
&self,
target: u32,
level: i32,
internalformat: i32,
width: i32,
height: i32,
border: i32,
format: u32,
type_: u32,
src_data: CustomAutoRooterGuard<ArrayBufferView>,
src_offset: u32,
) -> Fallible<()> {
if self.bound_pixel_unpack_buffer.get().is_some() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let validator = TexImage2DValidator::new(
&self.base,
target,
level,
internalformat as u32,
width,
height,
border,
format,
type_,
);
let TexImage2DValidatorResult {
texture,
target,
width,
height,
level,
border,
internal_format,
format,
data_type,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()),
};
let unpacking_alignment = self.base.texture_unpacking_alignment();
let src_elem_size = typedarray_elem_size(src_data.get_array_type());
let src_byte_offset = src_offset as usize * src_elem_size;
if src_data.len() <= src_byte_offset {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let buff = IpcSharedMemory::from_bytes(unsafe { &src_data.as_slice()[src_byte_offset..] });
let expected_byte_length = match self.base.validate_tex_image_2d_data(
width,
height,
format,
data_type,
unpacking_alignment,
Some(&*src_data),
) {
Ok(byte_length) => byte_length,
Err(()) => return Ok(()),
};
if expected_byte_length as usize > buff.len() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let size = Size2D::new(width, height);
self.base.tex_image_2d(
&texture,
target,
data_type,
internal_format,
format,
level,
border,
unpacking_alignment,
size,
TexSource::Pixels(TexPixels::from_array(buff, size)),
);
Ok(())
}
fn TexSubImage2D(
&self,
target: u32,
level: i32,
xoffset: i32,
yoffset: i32,
width: i32,
height: i32,
format: u32,
data_type: u32,
pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
) -> Fallible<()> {
self.base.TexSubImage2D(
target, level, xoffset, yoffset, width, height, format, data_type, pixels,
)
}
fn TexSubImage2D_(
&self,
target: u32,
level: i32,
xoffset: i32,
yoffset: i32,
format: u32,
data_type: u32,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
) -> ErrorResult {
self.base
.TexSubImage2D_(target, level, xoffset, yoffset, format, data_type, source)
}
fn TexParameterf(&self, target: u32, name: u32, value: f32) {
self.base.TexParameterf(target, name, value)
}
fn TexParameteri(&self, target: u32, name: u32, value: i32) {
self.base.TexParameteri(target, name, value)
}
fn CheckFramebufferStatus(&self, target: u32) -> u32 {
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => {
self.base.webgl_error(InvalidEnum);
return 0;
},
};
match fb_slot.get() {
Some(fb) => fb.check_status(),
None => constants::FRAMEBUFFER_COMPLETE,
}
}
fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
self.base
.RenderbufferStorage(target, internal_format, width, height)
}
fn FramebufferRenderbuffer(
&self,
target: u32,
attachment: u32,
renderbuffertarget: u32,
rb: Option<&WebGLRenderbuffer>,
) {
if let Some(rb) = rb {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(rb), return);
}
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => return self.base.webgl_error(InvalidEnum),
};
if renderbuffertarget != constants::RENDERBUFFER {
return self.base.webgl_error(InvalidEnum);
}
match fb_slot.get() {
Some(fb) => match attachment {
constants::DEPTH_STENCIL_ATTACHMENT => {
handle_potential_webgl_error!(
self.base,
fb.renderbuffer(constants::DEPTH_ATTACHMENT, rb)
);
handle_potential_webgl_error!(
self.base,
fb.renderbuffer(constants::STENCIL_ATTACHMENT, rb)
);
},
_ => handle_potential_webgl_error!(self.base, fb.renderbuffer(attachment, rb)),
},
None => self.base.webgl_error(InvalidOperation),
};
}
fn FramebufferTexture2D(
&self,
target: u32,
attachment: u32,
textarget: u32,
texture: Option<&WebGLTexture>,
level: i32,
) {
if let Some(texture) = texture {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(texture), return);
}
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => return self.base.webgl_error(InvalidEnum),
};
match fb_slot.get() {
Some(fb) => handle_potential_webgl_error!(
self.base,
fb.texture2d(attachment, textarget, texture, level)
),
None => self.base.webgl_error(InvalidOperation),
}
}
fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
self.base.GetAttachedShaders(program)
}
fn DrawArraysInstanced(&self, mode: u32, first: i32, count: i32, primcount: i32) {
self.validate_uniform_block_for_draw();
self.validate_vertex_attribs_for_draw();
handle_potential_webgl_error!(
self.base,
self.base
.draw_arrays_instanced(mode, first, count, primcount)
)
}
fn DrawElementsInstanced(
&self,
mode: u32,
count: i32,
type_: u32,
offset: i64,
primcount: i32,
) {
self.validate_uniform_block_for_draw();
self.validate_vertex_attribs_for_draw();
handle_potential_webgl_error!(
self.base,
self.base
.draw_elements_instanced(mode, count, type_, offset, primcount)
)
}
fn DrawRangeElements(
&self,
mode: u32,
start: u32,
end: u32,
count: i32,
type_: u32,
offset: i64,
) {
if end < start {
self.base.webgl_error(InvalidValue);
return;
}
self.validate_uniform_block_for_draw();
self.validate_vertex_attribs_for_draw();
handle_potential_webgl_error!(
self.base,
self.base
.draw_elements_instanced(mode, count, type_, offset, 1)
)
}
fn VertexAttribDivisor(&self, index: u32, divisor: u32) {
self.base.vertex_attrib_divisor(index, divisor);
}
fn CreateQuery(&self) -> Option<DomRoot<WebGLQuery>> {
Some(WebGLQuery::new(&self.base))
}
#[rustfmt::skip]
fn DeleteQuery(&self, query: Option<&WebGLQuery>) {
if let Some(query) = query {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
if let Some(query_target) = query.target() {
let slot = match query_target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
&self.occlusion_query
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
&self.primitives_query
},
_ => unreachable!(),
};
if let Some(stored_query) = slot.get() {
if stored_query.target() == query.target() {
slot.set(None);
}
}
}
query.delete(Operation::Infallible);
}
}
fn IsQuery(&self, query: Option<&WebGLQuery>) -> bool {
match query {
Some(query) => self.base.validate_ownership(query).is_ok() && query.is_valid(),
None => false,
}
}
fn CreateSampler(&self) -> Option<DomRoot<WebGLSampler>> {
Some(WebGLSampler::new(&self.base))
}
fn DeleteSampler(&self, sampler: Option<&WebGLSampler>) {
if let Some(sampler) = sampler {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
for slot in self.samplers.iter() {
if slot.get().is_some_and(|s| sampler == &*s) {
slot.set(None);
}
}
sampler.delete(Operation::Infallible);
}
}
fn IsSampler(&self, sampler: Option<&WebGLSampler>) -> bool {
match sampler {
Some(sampler) => self.base.validate_ownership(sampler).is_ok() && sampler.is_valid(),
None => false,
}
}
#[rustfmt::skip]
fn BeginQuery(&self, target: u32, query: &WebGLQuery) {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
let active_query = match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
&self.occlusion_query
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
&self.primitives_query
},
_ => {
self.base.webgl_error(InvalidEnum);
return;
},
};
if active_query.get().is_some() {
self.base.webgl_error(InvalidOperation);
return;
}
let result = query.begin(&self.base, target);
match result {
Ok(_) => active_query.set(Some(query)),
Err(error) => self.base.webgl_error(error),
}
}
#[rustfmt::skip]
fn EndQuery(&self, target: u32) {
let active_query = match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
self.occlusion_query.take()
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
self.primitives_query.take()
},
_ => {
self.base.webgl_error(InvalidEnum);
return;
},
};
match active_query {
None => self.base.webgl_error(InvalidOperation),
Some(query) => {
let result = query.end(&self.base, target);
if let Err(error) = result {
self.base.webgl_error(error);
}
},
}
}
#[rustfmt::skip]
fn GetQuery(&self, target: u32, pname: u32) -> Option<DomRoot<WebGLQuery>> {
if pname != constants::CURRENT_QUERY {
self.base.webgl_error(InvalidEnum);
return None;
}
let active_query = match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
self.occlusion_query.get()
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
self.primitives_query.get()
},
_ => {
self.base.webgl_error(InvalidEnum);
None
},
};
if let Some(query) = active_query.as_ref() {
if query.target() != Some(target) {
return None;
}
}
active_query
}
#[rustfmt::skip]
fn GetQueryParameter(&self, _cx: JSContext, query: &WebGLQuery, pname: u32, mut retval: MutableHandleValue) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(query),
return retval.set(NullValue())
);
match query.get_parameter(&self.base, pname) {
Ok(value) => match pname {
constants::QUERY_RESULT => retval.set(UInt32Value(value)),
constants::QUERY_RESULT_AVAILABLE => retval.set(BooleanValue(value != 0)),
_ => unreachable!(),
},
Err(error) => {
self.base.webgl_error(error);
retval.set(NullValue())
},
}
}
fn FenceSync(&self, condition: u32, flags: u32) -> Option<DomRoot<WebGLSync>> {
if flags != 0 {
self.base.webgl_error(InvalidValue);
return None;
}
if condition != constants::SYNC_GPU_COMMANDS_COMPLETE {
self.base.webgl_error(InvalidEnum);
return None;
}
Some(WebGLSync::new(&self.base))
}
fn IsSync(&self, sync: Option<&WebGLSync>) -> bool {
match sync {
Some(sync) => {
if !sync.is_valid() {
return false;
}
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(sync),
return false
);
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::IsSync(sync.id(), sender));
receiver.recv().unwrap()
},
None => false,
}
}
fn ClientWaitSync(&self, sync: &WebGLSync, flags: u32, timeout: u64) -> u32 {
if !sync.is_valid() {
self.base.webgl_error(InvalidOperation);
return constants::WAIT_FAILED;
}
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(sync),
return constants::WAIT_FAILED
);
if flags != 0 && flags != constants::SYNC_FLUSH_COMMANDS_BIT {
self.base.webgl_error(InvalidValue);
return constants::WAIT_FAILED;
}
if timeout > self.base.limits().max_client_wait_timeout_webgl.as_nanos() as u64 {
self.base.webgl_error(InvalidOperation);
return constants::WAIT_FAILED;
}
match sync.client_wait_sync(&self.base, flags, timeout) {
Some(status) => status,
None => constants::WAIT_FAILED,
}
}
fn WaitSync(&self, sync: &WebGLSync, flags: u32, timeout: i64) {
if !sync.is_valid() {
self.base.webgl_error(InvalidOperation);
return;
}
handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
if flags != 0 {
self.base.webgl_error(InvalidValue);
return;
}
if timeout != constants::TIMEOUT_IGNORED {
self.base.webgl_error(InvalidValue);
return;
}
self.base
.send_command(WebGLCommand::WaitSync(sync.id(), flags, timeout));
}
fn GetSyncParameter(
&self,
_cx: JSContext,
sync: &WebGLSync,
pname: u32,
mut retval: MutableHandleValue,
) {
if !sync.is_valid() {
self.base.webgl_error(InvalidOperation);
return retval.set(NullValue());
}
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(sync),
return retval.set(NullValue())
);
match pname {
constants::OBJECT_TYPE | constants::SYNC_CONDITION | constants::SYNC_FLAGS => {
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetSyncParameter(sync.id(), pname, sender));
retval.set(UInt32Value(receiver.recv().unwrap()))
},
constants::SYNC_STATUS => match sync.get_sync_status(pname, &self.base) {
Some(status) => retval.set(UInt32Value(status)),
None => retval.set(UInt32Value(constants::UNSIGNALED)),
},
_ => {
self.base.webgl_error(InvalidEnum);
retval.set(NullValue())
},
}
}
fn DeleteSync(&self, sync: Option<&WebGLSync>) {
if let Some(sync) = sync {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
sync.delete(Operation::Infallible);
}
}
fn BindSampler(&self, unit: u32, sampler: Option<&WebGLSampler>) {
if let Some(sampler) = sampler {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
if unit as usize >= self.samplers.len() {
self.base.webgl_error(InvalidValue);
return;
}
let result = sampler.bind(&self.base, unit);
match result {
Ok(_) => self.samplers[unit as usize].set(Some(sampler)),
Err(error) => self.base.webgl_error(error),
}
}
}
fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) {
self.base.bind_vertex_array_webgl2(array);
}
fn SamplerParameteri(&self, sampler: &WebGLSampler, pname: u32, param: i32) {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
let param = WebGLSamplerValue::GLenum(param as u32);
let result = sampler.set_parameter(&self.base, pname, param);
if let Err(error) = result {
self.base.webgl_error(error);
}
}
fn SamplerParameterf(&self, sampler: &WebGLSampler, pname: u32, param: f32) {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
let param = WebGLSamplerValue::Float(param);
let result = sampler.set_parameter(&self.base, pname, param);
if let Err(error) = result {
self.base.webgl_error(error);
}
}
fn GetSamplerParameter(
&self,
_cx: JSContext,
sampler: &WebGLSampler,
pname: u32,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(sampler),
return retval.set(NullValue())
);
match sampler.get_parameter(&self.base, pname) {
Ok(value) => match value {
WebGLSamplerValue::GLenum(value) => retval.set(UInt32Value(value)),
WebGLSamplerValue::Float(value) => retval.set(DoubleValue(value as f64)),
},
Err(error) => {
self.base.webgl_error(error);
retval.set(NullValue())
},
}
}
fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> {
Some(WebGLTransformFeedback::new(&self.base))
}
fn DeleteTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) {
if let Some(tf) = tf {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(tf), return);
if tf.is_active() {
self.base.webgl_error(InvalidOperation);
return;
}
tf.delete(Operation::Infallible);
self.current_transform_feedback.set(None);
}
}
fn IsTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) -> bool {
match tf {
Some(tf) => {
if !tf.is_valid() {
return false;
}
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(tf),
return false
);
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::IsTransformFeedback(tf.id(), sender));
receiver.recv().unwrap()
},
None => false,
}
}
fn BindTransformFeedback(&self, target: u32, tf: Option<&WebGLTransformFeedback>) {
if target != constants::TRANSFORM_FEEDBACK {
self.base.webgl_error(InvalidEnum);
return;
}
match tf {
Some(transform_feedback) => {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(transform_feedback),
return
);
if !transform_feedback.is_valid() {
self.base.webgl_error(InvalidOperation);
return;
}
if let Some(current_tf) = self.current_transform_feedback.get() {
if current_tf.is_active() && !current_tf.is_paused() {
self.base.webgl_error(InvalidOperation);
return;
}
}
transform_feedback.bind(&self.base, target);
self.current_transform_feedback
.set(Some(transform_feedback));
},
None => self
.base
.send_command(WebGLCommand::BindTransformFeedback(target, 0)),
}
}
#[allow(non_snake_case)]
fn BeginTransformFeedback(&self, primitiveMode: u32) {
match primitiveMode {
constants::POINTS | constants::LINES | constants::TRIANGLES => {},
_ => {
self.base.webgl_error(InvalidEnum);
return;
},
};
let current_tf = match self.current_transform_feedback.get() {
Some(current_tf) => current_tf,
None => {
self.base.webgl_error(InvalidOperation);
return;
},
};
if current_tf.is_active() {
self.base.webgl_error(InvalidOperation);
return;
};
let program = match self.base.current_program() {
Some(program) => program,
None => {
self.base.webgl_error(InvalidOperation);
return;
},
};
if !program.is_linked() || program.transform_feedback_varyings_length() == 0 {
self.base.webgl_error(InvalidOperation);
return;
};
current_tf.begin(&self.base, primitiveMode);
}
fn EndTransformFeedback(&self) {
if let Some(current_tf) = self.current_transform_feedback.get() {
if !current_tf.is_active() {
self.base.webgl_error(InvalidOperation);
return;
}
current_tf.end(&self.base);
}
}
fn ResumeTransformFeedback(&self) {
if let Some(current_tf) = self.current_transform_feedback.get() {
if !current_tf.is_active() || !current_tf.is_paused() {
self.base.webgl_error(InvalidOperation);
return;
}
current_tf.resume(&self.base);
}
}
fn PauseTransformFeedback(&self) {
if let Some(current_tf) = self.current_transform_feedback.get() {
if !current_tf.is_active() || current_tf.is_paused() {
self.base.webgl_error(InvalidOperation);
return;
}
current_tf.pause(&self.base);
}
}
#[allow(non_snake_case)]
fn TransformFeedbackVaryings(
&self,
program: &WebGLProgram,
varyings: Vec<DOMString>,
bufferMode: u32,
) {
handle_potential_webgl_error!(self.base, program.validate(), return);
let strs = varyings
.iter()
.map(|name| String::from(name.to_owned()))
.collect::<Vec<String>>();
match bufferMode {
constants::INTERLEAVED_ATTRIBS => {
self.base
.send_command(WebGLCommand::TransformFeedbackVaryings(
program.id(),
strs,
bufferMode,
));
},
constants::SEPARATE_ATTRIBS => {
let max_tf_sp_att =
self.base.limits().max_transform_feedback_separate_attribs as usize;
if strs.len() >= max_tf_sp_att {
self.base.webgl_error(InvalidValue);
return;
}
self.base
.send_command(WebGLCommand::TransformFeedbackVaryings(
program.id(),
strs,
bufferMode,
));
},
_ => self.base.webgl_error(InvalidEnum),
}
}
fn GetTransformFeedbackVarying(
&self,
program: &WebGLProgram,
index: u32,
) -> Option<DomRoot<WebGLActiveInfo>> {
handle_potential_webgl_error!(self.base, program.validate(), return None);
if index >= program.transform_feedback_varyings_length() as u32 {
self.base.webgl_error(InvalidValue);
return None;
}
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetTransformFeedbackVarying(
program.id(),
index,
sender,
));
let (size, ty, name) = receiver.recv().unwrap();
Some(WebGLActiveInfo::new(
self.base.global().as_window(),
size,
ty,
DOMString::from(name),
))
}
fn BindBufferBase(&self, target: u32, index: u32, buffer: Option<&WebGLBuffer>) {
let (generic_slot, indexed_bindings) = match target {
constants::TRANSFORM_FEEDBACK_BUFFER => (
&self.bound_transform_feedback_buffer,
&self.indexed_transform_feedback_buffer_bindings,
),
constants::UNIFORM_BUFFER => (
&self.bound_uniform_buffer,
&self.indexed_uniform_buffer_bindings,
),
_ => return self.base.webgl_error(InvalidEnum),
};
let indexed_binding = match indexed_bindings.get(index as usize) {
Some(slot) => slot,
None => return self.base.webgl_error(InvalidValue),
};
if let Some(buffer) = buffer {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
if buffer.is_marked_for_deletion() {
return self.base.webgl_error(InvalidOperation);
}
handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
buffer.increment_attached_counter();
buffer.increment_attached_counter();
}
self.base.send_command(WebGLCommand::BindBufferBase(
target,
index,
buffer.map(|b| b.id()),
));
for slot in &[generic_slot, &indexed_binding.buffer] {
if let Some(old) = slot.get() {
old.decrement_attached_counter(Operation::Infallible);
}
slot.set(buffer);
}
indexed_binding.start.set(0);
indexed_binding.size.set(0);
}
fn BindBufferRange(
&self,
target: u32,
index: u32,
buffer: Option<&WebGLBuffer>,
offset: i64,
size: i64,
) {
let (generic_slot, indexed_bindings) = match target {
constants::TRANSFORM_FEEDBACK_BUFFER => (
&self.bound_transform_feedback_buffer,
&self.indexed_transform_feedback_buffer_bindings,
),
constants::UNIFORM_BUFFER => (
&self.bound_uniform_buffer,
&self.indexed_uniform_buffer_bindings,
),
_ => return self.base.webgl_error(InvalidEnum),
};
let indexed_binding = match indexed_bindings.get(index as usize) {
Some(slot) => slot,
None => return self.base.webgl_error(InvalidValue),
};
if offset < 0 || size < 0 {
return self.base.webgl_error(InvalidValue);
}
if buffer.is_some() && size == 0 {
return self.base.webgl_error(InvalidValue);
}
match target {
constants::TRANSFORM_FEEDBACK_BUFFER => {
if size % 4 != 0 && offset % 4 != 0 {
return self.base.webgl_error(InvalidValue);
}
},
constants::UNIFORM_BUFFER => {
let offset_alignment = self.base.limits().uniform_buffer_offset_alignment;
if offset % offset_alignment as i64 != 0 {
return self.base.webgl_error(InvalidValue);
}
},
_ => unreachable!(),
}
if let Some(buffer) = buffer {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
if buffer.is_marked_for_deletion() {
return self.base.webgl_error(InvalidOperation);
}
handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
buffer.increment_attached_counter();
buffer.increment_attached_counter();
}
self.base.send_command(WebGLCommand::BindBufferRange(
target,
index,
buffer.map(|b| b.id()),
offset,
size,
));
for slot in &[generic_slot, &indexed_binding.buffer] {
if let Some(old) = slot.get() {
old.decrement_attached_counter(Operation::Infallible);
}
slot.set(buffer);
}
indexed_binding.start.set(offset);
indexed_binding.size.set(size);
}
fn GetUniformIndices(&self, program: &WebGLProgram, names: Vec<DOMString>) -> Option<Vec<u32>> {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return None
);
let indices = handle_potential_webgl_error!(
self.base,
program.get_uniform_indices(names),
return None
);
Some(indices)
}
#[allow(unsafe_code)]
fn GetActiveUniforms(
&self,
cx: JSContext,
program: &WebGLProgram,
indices: Vec<u32>,
pname: u32,
mut rval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return rval.set(NullValue())
);
let values = handle_potential_webgl_error!(
self.base,
program.get_active_uniforms(indices, pname),
return rval.set(NullValue())
);
match pname {
constants::UNIFORM_SIZE |
constants::UNIFORM_TYPE |
constants::UNIFORM_BLOCK_INDEX |
constants::UNIFORM_OFFSET |
constants::UNIFORM_ARRAY_STRIDE |
constants::UNIFORM_MATRIX_STRIDE => unsafe {
values.to_jsval(*cx, rval);
},
constants::UNIFORM_IS_ROW_MAJOR => unsafe {
let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>();
values.to_jsval(*cx, rval);
},
_ => unreachable!(),
}
}
fn GetUniformBlockIndex(&self, program: &WebGLProgram, block_name: DOMString) -> u32 {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return constants::INVALID_INDEX
);
handle_potential_webgl_error!(
self.base,
program.get_uniform_block_index(block_name),
constants::INVALID_INDEX
)
}
#[allow(unsafe_code)]
fn GetActiveUniformBlockParameter(
&self,
cx: JSContext,
program: &WebGLProgram,
block_index: u32,
pname: u32,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return retval.set(NullValue())
);
let values = handle_potential_webgl_error!(
self.base,
program.get_active_uniform_block_parameter(block_index, pname),
return retval.set(NullValue())
);
match pname {
constants::UNIFORM_BLOCK_BINDING |
constants::UNIFORM_BLOCK_DATA_SIZE |
constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => {
assert!(values.len() == 1);
retval.set(UInt32Value(values[0] as u32))
},
constants::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
let values = values.iter().map(|&v| v as u32).collect::<Vec<_>>();
rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
Uint32Array::create(*cx, CreateWith::Slice(&values), result.handle_mut()).unwrap();
retval.set(ObjectValue(result.get()))
},
constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {
assert!(values.len() == 1);
retval.set(BooleanValue(values[0] != 0))
},
_ => unreachable!(),
}
}
fn GetActiveUniformBlockName(
&self,
program: &WebGLProgram,
block_index: u32,
) -> Option<DOMString> {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return None
);
let name = handle_potential_webgl_error!(
self.base,
program.get_active_uniform_block_name(block_index),
return None
);
Some(DOMString::from(name))
}
fn UniformBlockBinding(&self, program: &WebGLProgram, block_index: u32, block_binding: u32) {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return);
if block_binding >= self.base.limits().max_uniform_buffer_bindings {
return self.base.webgl_error(InvalidValue);
}
handle_potential_webgl_error!(
self.base,
program.bind_uniform_block(block_index, block_binding)
)
}
fn ClearBufferfv(
&self,
buffer: u32,
draw_buffer: i32,
values: Float32ArrayOrUnrestrictedFloatSequence,
src_offset: u32,
) {
let array = match values {
Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
};
self.clear_buffer::<f32>(
buffer,
draw_buffer,
&[constants::COLOR, constants::DEPTH],
src_offset,
array,
WebGLCommand::ClearBufferfv,
)
}
fn ClearBufferiv(
&self,
buffer: u32,
draw_buffer: i32,
values: Int32ArrayOrLongSequence,
src_offset: u32,
) {
let array = match values {
Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
Int32ArrayOrLongSequence::LongSequence(v) => v,
};
self.clear_buffer::<i32>(
buffer,
draw_buffer,
&[constants::COLOR, constants::STENCIL],
src_offset,
array,
WebGLCommand::ClearBufferiv,
)
}
fn ClearBufferuiv(
&self,
buffer: u32,
draw_buffer: i32,
values: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
) {
let array = match values {
Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
};
self.clear_buffer::<u32>(
buffer,
draw_buffer,
&[constants::COLOR],
src_offset,
array,
WebGLCommand::ClearBufferuiv,
)
}
fn ClearBufferfi(&self, buffer: u32, draw_buffer: i32, depth: f32, stencil: i32) {
if buffer != constants::DEPTH_STENCIL {
return self.base.webgl_error(InvalidEnum);
}
handle_potential_webgl_error!(
self.base,
self.clearbuffer_array_size(buffer, draw_buffer),
return
);
self.base.send_command(WebGLCommand::ClearBufferfi(
buffer,
draw_buffer,
depth,
stencil,
));
}
fn InvalidateFramebuffer(&self, target: u32, attachments: Vec<u32>) {
if !self.valid_fb_attachment_values(target, &attachments) {
return;
}
self.base
.send_command(WebGLCommand::InvalidateFramebuffer(target, attachments))
}
fn InvalidateSubFramebuffer(
&self,
target: u32,
attachments: Vec<u32>,
x: i32,
y: i32,
width: i32,
height: i32,
) {
if !self.valid_fb_attachment_values(target, &attachments) {
return;
}
if width < 0 || height < 0 {
return self.base.webgl_error(InvalidValue);
}
self.base
.send_command(WebGLCommand::InvalidateSubFramebuffer(
target,
attachments,
x,
y,
width,
height,
))
}
fn FramebufferTextureLayer(
&self,
target: u32,
attachment: u32,
texture: Option<&WebGLTexture>,
level: i32,
layer: i32,
) {
if let Some(tex) = texture {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(tex), return);
}
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => return self.base.webgl_error(InvalidEnum),
};
match fb_slot.get() {
Some(fb) => handle_potential_webgl_error!(
self.base,
fb.texture_layer(attachment, texture, level, layer)
),
None => self.base.webgl_error(InvalidOperation),
}
}
#[allow(unsafe_code)]
fn GetInternalformatParameter(
&self,
cx: JSContext,
target: u32,
internal_format: u32,
pname: u32,
mut retval: MutableHandleValue,
) {
if target != constants::RENDERBUFFER {
self.base.webgl_error(InvalidEnum);
return retval.set(NullValue());
}
match handle_potential_webgl_error!(
self.base,
InternalFormatParameter::from_u32(pname),
return retval.set(NullValue())
) {
InternalFormatParameter::IntVec(param) => unsafe {
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetInternalFormatIntVec(
target,
internal_format,
param,
sender,
));
rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
Int32Array::create(
*cx,
CreateWith::Slice(&receiver.recv().unwrap()),
rval.handle_mut(),
)
.unwrap();
retval.set(ObjectValue(rval.get()))
},
}
}
fn RenderbufferStorageMultisample(
&self,
target: u32,
samples: i32,
internal_format: u32,
width: i32,
height: i32,
) {
self.base
.renderbuffer_storage(target, samples, internal_format, width, height)
}
fn ReadBuffer(&self, src: u32) {
match src {
constants::BACK | constants::NONE => {},
_ if self.base.valid_color_attachment_enum(src) => {},
_ => return self.base.webgl_error(InvalidEnum),
}
if let Some(fb) = self.base.get_read_framebuffer_slot().get() {
handle_potential_webgl_error!(self.base, fb.set_read_buffer(src))
} else {
match src {
constants::NONE | constants::BACK => {},
_ => return self.base.webgl_error(InvalidOperation),
}
self.default_fb_readbuffer.set(src);
self.base.send_command(WebGLCommand::ReadBuffer(src));
}
}
fn DrawBuffers(&self, buffers: Vec<u32>) {
if let Some(fb) = self.base.get_draw_framebuffer_slot().get() {
handle_potential_webgl_error!(self.base, fb.set_draw_buffers(buffers))
} else {
if buffers.len() != 1 {
return self.base.webgl_error(InvalidOperation);
}
match buffers[0] {
constants::NONE | constants::BACK => {},
_ => return self.base.webgl_error(InvalidOperation),
}
self.default_fb_drawbuffer.set(buffers[0]);
self.base.send_command(WebGLCommand::DrawBuffers(buffers));
}
}
fn TexStorage2D(
&self,
target: u32,
levels: i32,
internal_format: u32,
width: i32,
height: i32,
) {
self.tex_storage(2, target, levels, internal_format, width, height, 1)
}
fn TexStorage3D(
&self,
target: u32,
levels: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
) {
self.tex_storage(3, target, levels, internal_format, width, height, depth)
}
#[cfg(feature = "webxr")]
fn MakeXRCompatible(&self, can_gc: CanGc) -> Rc<Promise> {
let p = Promise::new(&self.global(), can_gc);
p.resolve_native(&());
p
}
}
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGL2RenderingContext> {
#[allow(unsafe_code)]
fn canvas_data_source(self) -> HTMLCanvasDataSource {
let this = self.unsafe_get();
unsafe { (*this.base.to_layout().unsafe_get()).layout_handle() }
}
}