diff options
Diffstat (limited to 'components/script/dom/webgl2renderingcontext.rs')
-rw-r--r-- | components/script/dom/webgl2renderingcontext.rs | 4472 |
1 files changed, 4472 insertions, 0 deletions
diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs new file mode 100644 index 00000000000..b438056e622 --- /dev/null +++ b/components/script/dom/webgl2renderingcontext.rs @@ -0,0 +1,4472 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants; +use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextMethods; +use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; +use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; +use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer; +use crate::dom::bindings::codegen::UnionTypes::Float32ArrayOrUnrestrictedFloatSequence; +use crate::dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement; +use crate::dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence; +use crate::dom::bindings::codegen::UnionTypes::Uint32ArrayOrUnsignedLongSequence; +use crate::dom::bindings::error::{ErrorResult, Fallible}; +use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::reflector::{reflect_dom_object, 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::{HTMLCanvasElement, LayoutCanvasRenderingContextHelpers}; +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::JSContext; +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, UInt32Value}; +use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; +use js::rust::{CustomAutoRooterGuard, HandleObject}; +use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array}; +use script_layout_interface::HTMLCanvasDataSource; +use servo_config::pref; +use std::cell::Cell; +use std::cmp; +use std::ptr::{self, NonNull}; +use url::Host; + +#[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 => 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: &HTMLCanvasElement, + size: Size2D<u32>, + attrs: GLContextAttributes, + ) -> Option<WebGL2RenderingContext> { + let base = WebGLRenderingContext::new(window, canvas, WebGLVersion::WebGL2, size, attrs)?; + + 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: 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(unrooted_must_root)] + pub fn new( + window: &Window, + canvas: &HTMLCanvasElement, + size: Size2D<u32>, + attrs: GLContextAttributes, + ) -> Option<DomRoot<WebGL2RenderingContext>> { + WebGL2RenderingContext::new_inherited(window, canvas, size, attrs) + .map(|ctx| reflect_dom_object(Box::new(ctx), window)) + } + + #[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()) + } +} + +/// List of domains for which WebGL 2 is enabled automatically, regardless +/// of the status of the dom.webgl2.enabled preference. +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 as u32) + .ok_or(InvalidOperation), + return + ); + + let current_vertex_attrib = + self.base.current_vertex_attribs()[prog_attrib.location as usize]; + 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().map_or(false, |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); + } + + // See also https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.36 + 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)?; + let result = full_row_bytes + .checked_add(last_row_bytes) + .ok_or(InvalidOperation)?; + result + }; + 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)?; + let result = skipped_row_bytes + .checked_add(skipped_pixel_bytes) + .ok_or(InvalidOperation)?; + result + }; + Ok(ReadPixelsSizes { + row_stride, + skipped_bytes, + size, + }) + } + + #[allow(unsafe_code)] + 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) -> WebGLResult<JSVal> { + match attachment { + constants::BACK | constants::DEPTH | constants::STENCIL => {}, + _ => return Err(InvalidEnum), + } + + if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME { + return Ok(NullValue()); + } + + 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), + }; + Ok(Int32Value(intval)) + } + + #[allow(unsafe_code)] + fn get_specific_fb_attachment_param( + &self, + cx: JSContext, + fb: &WebGLFramebuffer, + target: u32, + attachment: u32, + pname: u32, + ) -> WebGLResult<JSVal> { + 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 { + rooted!(in(*cx) let mut rval = NullValue()); + match fb.attachment(attachment) { + Some(Renderbuffer(rb)) => unsafe { + rb.to_jsval(*cx, rval.handle_mut()); + }, + Some(Texture(texture)) => unsafe { + texture.to_jsval(*cx, rval.handle_mut()); + }, + _ => {}, + } + return Ok(rval.get()); + } + + 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(); + Ok(Int32Value(retval)) + } + + 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)); + } + + 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, // NB: The validator sets the correct error for us. + }; + + handle_potential_webgl_error!( + self.base, + texture.storage(target, levels, internal_format, width, height, depth), + return + ); + } +} + +impl WebGL2RenderingContextMethods for WebGL2RenderingContext { + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1 + fn Canvas(&self) -> DomRoot<HTMLCanvasElement> { + self.base.Canvas() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + fn Flush(&self) { + self.base.Flush() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + fn Finish(&self) { + self.base.Finish() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1 + fn DrawingBufferWidth(&self) -> i32 { + self.base.DrawingBufferWidth() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1 + fn DrawingBufferHeight(&self) -> i32 { + self.base.DrawingBufferHeight() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + fn GetBufferParameter(&self, _cx: JSContext, target: u32, parameter: u32) -> JSVal { + let buffer = + handle_potential_webgl_error!(self.base, self.bound_buffer(target), return NullValue()); + self.base.get_buffer_param(buffer, parameter) + } + + #[allow(unsafe_code)] + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn GetParameter(&self, cx: JSContext, parameter: u32) -> JSVal { + match parameter { + constants::VERSION => unsafe { + rooted!(in(*cx) let mut rval = UndefinedValue()); + "WebGL 2.0".to_jsval(*cx, rval.handle_mut()); + return rval.get(); + }, + constants::SHADING_LANGUAGE_VERSION => unsafe { + rooted!(in(*cx) let mut rval = UndefinedValue()); + "WebGL GLSL ES 3.00".to_jsval(*cx, rval.handle_mut()); + return rval.get(); + }, + constants::MAX_CLIENT_WAIT_TIMEOUT_WEBGL => { + return DoubleValue( + self.base.limits().max_client_wait_timeout_webgl.as_nanos() as f64 + ); + }, + constants::MAX_SERVER_WAIT_TIMEOUT => { + return DoubleValue(self.base.limits().max_server_wait_timeout.as_nanos() as f64); + }, + 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(); + return optional_root_object_to_js_or_null!(*cx, sampler); + }, + constants::COPY_READ_BUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_copy_read_buffer.get() + ); + }, + constants::COPY_WRITE_BUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_copy_write_buffer.get() + ); + }, + constants::PIXEL_PACK_BUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_pixel_pack_buffer.get() + ); + }, + constants::PIXEL_UNPACK_BUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_pixel_unpack_buffer.get() + ); + }, + constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_transform_feedback_buffer.get() + ); + }, + constants::UNIFORM_BUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!(*cx, &self.bound_uniform_buffer.get()); + }, + constants::TRANSFORM_FEEDBACK_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + self.current_transform_feedback.get() + ); + }, + constants::ELEMENT_ARRAY_BUFFER_BINDING => unsafe { + let buffer = self.current_vao().element_array_buffer().get(); + return optional_root_object_to_js_or_null!(*cx, buffer); + }, + constants::VERTEX_ARRAY_BINDING => unsafe { + let vao = self.current_vao(); + let vao = vao.id().map(|_| &*vao); + return optional_root_object_to_js_or_null!(*cx, vao); + }, + // NOTE: DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING, handled on the WebGL1 side + constants::READ_FRAMEBUFFER_BINDING => unsafe { + return optional_root_object_to_js_or_null!( + *cx, + &self.base.get_read_framebuffer_slot().get() + ); + }, + constants::READ_BUFFER => { + let buffer = match self.base.get_read_framebuffer_slot().get() { + Some(fb) => fb.read_buffer(), + None => self.default_fb_readbuffer.get(), + }; + return UInt32Value(buffer); + }, + 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, + }; + return UInt32Value(buffer); + }, + constants::MAX_TEXTURE_LOD_BIAS => { + return DoubleValue(self.base.limits().max_texture_lod_bias as f64) + }, + constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => { + return DoubleValue( + self.base.limits().max_combined_fragment_uniform_components as f64, + ) + }, + constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => { + return DoubleValue( + self.base.limits().max_combined_vertex_uniform_components as f64, + ) + }, + constants::MAX_ELEMENT_INDEX => { + return DoubleValue(self.base.limits().max_element_index as f64) + }, + constants::MAX_UNIFORM_BLOCK_SIZE => { + return DoubleValue(self.base.limits().max_uniform_block_size as f64) + }, + constants::MIN_PROGRAM_TEXEL_OFFSET => { + return Int32Value(self.base.limits().min_program_texel_offset) + }, + _ => {}, + } + + 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 { + return UInt32Value(limit); + } + + self.base.GetParameter(cx, parameter) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn GetTexParameter(&self, cx: JSContext, target: u32, pname: u32) -> JSVal { + self.base.GetTexParameter(cx, target, pname) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn GetError(&self) -> u32 { + self.base.GetError() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.2 + fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> { + self.base.GetContextAttributes() + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.13 + fn IsContextLost(&self) -> bool { + self.base.IsContextLost() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14 + fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> { + self.base.GetSupportedExtensions() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14 + fn GetExtension(&self, cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> { + self.base.GetExtension(cx, name) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + fn GetFramebufferAttachmentParameter( + &self, + cx: JSContext, + target: u32, + attachment: u32, + pname: u32, + ) -> JSVal { + 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 NullValue(); + }, + }; + + if let Some(fb) = fb_slot.get() { + // A selected framebuffer is bound to the target + handle_potential_webgl_error!(self.base, fb.validate_transparent(), return NullValue()); + handle_potential_webgl_error!( + self.base, + self.get_specific_fb_attachment_param(cx, &fb, target, attachment, pname), + return NullValue() + ) + } else { + // The default framebuffer is bound to the target + handle_potential_webgl_error!( + self.base, + self.get_default_fb_attachment_param(attachment, pname), + return NullValue() + ) + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 + fn GetRenderbufferParameter(&self, cx: JSContext, target: u32, pname: u32) -> JSVal { + self.base.GetRenderbufferParameter(cx, target, pname) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn ActiveTexture(&self, texture: u32) { + self.base.ActiveTexture(texture) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) { + self.base.BlendColor(r, g, b, a) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn BlendEquation(&self, mode: u32) { + self.base.BlendEquation(mode) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) { + self.base.BlendEquationSeparate(mode_rgb, mode_alpha) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn BlendFunc(&self, src_factor: u32, dest_factor: u32) { + self.base.BlendFunc(src_factor, dest_factor) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) { + self.base.AttachShader(program, shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) { + self.base.DetachShader(program, shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) { + self.base.BindAttribLocation(program, index, name) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 + 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); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + 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(), + ); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 + fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) { + self.base.BindRenderbuffer(target, renderbuffer) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) { + self.base.BindTexture(target, texture) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn GenerateMipmap(&self, target: u32) { + self.base.GenerateMipmap(target) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3 + #[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)); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3 + #[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(); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3 + 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() + .map_or(false, |t| t == constants::ELEMENT_ARRAY_BUFFER); + let dst_is_elemarray = write_buffer + .target() + .map_or(false, |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, + )); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3 + #[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; + + // TODO(mmatyas): Transform Feedback + + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + #[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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + #[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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + fn Clear(&self, mask: u32) { + self.base.Clear(mask) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) { + self.base.ClearColor(red, green, blue, alpha) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn ClearDepth(&self, depth: f32) { + self.base.ClearDepth(depth) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn ClearStencil(&self, stencil: i32) { + self.base.ClearStencil(stencil) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) { + self.base.ColorMask(r, g, b, a) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn CullFace(&self, mode: u32) { + self.base.CullFace(mode) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn FrontFace(&self, mode: u32) { + self.base.FrontFace(mode) + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn DepthFunc(&self, func: u32) { + self.base.DepthFunc(func) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn DepthMask(&self, flag: bool) { + self.base.DepthMask(flag) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn DepthRange(&self, near: f32, far: f32) { + self.base.DepthRange(near, far) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + 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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + 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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn CompileShader(&self, shader: &WebGLShader) { + self.base.CompileShader(shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> { + self.base.CreateBuffer() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> { + self.base.CreateFramebuffer() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 + fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> { + self.base.CreateRenderbuffer() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> { + self.base.CreateTexture() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> { + self.base.CreateProgram() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> { + self.base.CreateShader(shader_type) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn CreateVertexArray(&self) -> Option<DomRoot<WebGLVertexArrayObject>> { + self.base.create_vertex_array_webgl2() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + 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); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) { + self.base.DeleteFramebuffer(framebuffer) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 + fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) { + self.base.DeleteRenderbuffer(renderbuffer) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn DeleteTexture(&self, texture: Option<&WebGLTexture>) { + self.base.DeleteTexture(texture) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn DeleteProgram(&self, program: Option<&WebGLProgram>) { + self.base.DeleteProgram(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn DeleteShader(&self, shader: Option<&WebGLShader>) { + self.base.DeleteShader(shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn DeleteVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) { + self.base.delete_vertex_array_webgl2(vertex_array); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn EnableVertexAttribArray(&self, attrib_id: u32) { + self.base.EnableVertexAttribArray(attrib_id) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn DisableVertexAttribArray(&self, attrib_id: u32) { + self.base.DisableVertexAttribArray(attrib_id) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn GetActiveUniform( + &self, + program: &WebGLProgram, + index: u32, + ) -> Option<DomRoot<WebGLActiveInfo>> { + self.base.GetActiveUniform(program, index) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn GetActiveAttrib( + &self, + program: &WebGLProgram, + index: u32, + ) -> Option<DomRoot<WebGLActiveInfo>> { + self.base.GetActiveAttrib(program, index) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 { + self.base.GetAttribLocation(program, name) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.7 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> { + self.base.GetProgramInfoLog(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetProgramParameter(&self, cx: JSContext, program: &WebGLProgram, param_id: u32) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return NullValue() + ); + if program.is_deleted() { + self.base.webgl_error(InvalidOperation); + return NullValue(); + } + match param_id { + constants::TRANSFORM_FEEDBACK_VARYINGS => { + Int32Value(program.transform_feedback_varyings_length()) + }, + constants::TRANSFORM_FEEDBACK_BUFFER_MODE => { + Int32Value(program.transform_feedback_buffer_mode()) + }, + _ => self.base.GetProgramParameter(cx, program, param_id), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> { + self.base.GetShaderInfoLog(shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetShaderParameter(&self, cx: JSContext, shader: &WebGLShader, param_id: u32) -> JSVal { + self.base.GetShaderParameter(cx, shader, param_id) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetShaderPrecisionFormat( + &self, + shader_type: u32, + precision_type: u32, + ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> { + self.base + .GetShaderPrecisionFormat(shader_type, precision_type) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 + #[allow(unsafe_code)] + fn GetIndexedParameter(&self, cx: JSContext, target: u32, index: u32) -> JSVal { + 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 NullValue(); + }, + }; + + let binding = match bindings.get(index as usize) { + Some(binding) => binding, + None => { + self.base.webgl_error(InvalidValue); + return NullValue(); + }, + }; + + match target { + constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => unsafe { + optional_root_object_to_js_or_null!(*cx, binding.buffer.get()) + }, + constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => { + Int32Value(binding.start.get() as _) + }, + constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => { + Int32Value(binding.size.get() as _) + }, + _ => unreachable!(), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn GetUniformLocation( + &self, + program: &WebGLProgram, + name: DOMString, + ) -> Option<DomRoot<WebGLUniformLocation>> { + self.base.GetUniformLocation(program, name) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetVertexAttrib(&self, cx: JSContext, index: u32, pname: u32) -> JSVal { + self.base.GetVertexAttrib(cx, index, pname) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 { + self.base.GetVertexAttribOffset(index, pname) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn Hint(&self, target: u32, mode: u32) { + self.base.Hint(target, mode) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool { + self.base.IsBuffer(buffer) + } + + // TODO: We could write this without IPC, recording the calls to `enable` and `disable`. + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 + fn IsEnabled(&self, cap: u32) -> bool { + match cap { + constants::RASTERIZER_DISCARD => self.enable_rasterizer_discard.get(), + _ => self.base.IsEnabled(cap), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool { + self.base.IsFramebuffer(frame_buffer) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool { + self.base.IsProgram(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 + fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool { + self.base.IsRenderbuffer(render_buffer) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn IsShader(&self, shader: Option<&WebGLShader>) -> bool { + self.base.IsShader(shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool { + self.base.IsTexture(texture) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool { + self.base.is_vertex_array_webgl2(vertex_array) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn LineWidth(&self, width: f32) { + self.base.LineWidth(width) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 + 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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn PolygonOffset(&self, factor: f32, units: f32) { + self.base.PolygonOffset(factor, units) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.12 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.10 + 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, + )); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.10 + #[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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn SampleCoverage(&self, value: f32, invert: bool) { + self.base.SampleCoverage(value, invert) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 + fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) { + self.base.Scissor(x, y, width, height) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) { + self.base.StencilFunc(func, ref_, mask) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) { + self.base.StencilFuncSeparate(face, func, ref_, mask) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn StencilMask(&self, mask: u32) { + self.base.StencilMask(mask) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn StencilMaskSeparate(&self, face: u32, mask: u32) { + self.base.StencilMaskSeparate(face, mask) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) { + self.base.StencilOp(fail, zfail, zpass) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) { + self.base.StencilOpSeparate(face, fail, zfail, zpass) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn LinkProgram(&self, program: &WebGLProgram) { + self.base.LinkProgram(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) { + self.base.ShaderSource(shader, source) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> { + self.base.GetShaderSource(shader) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) { + self.base.Uniform1f(location, val) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) { + self.base.Uniform1i(location, val) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform1iv( + &self, + location: Option<&WebGLUniformLocation>, + v: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform1iv(location, v, src_offset, src_length) + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform1fv( + &self, + location: Option<&WebGLUniformLocation>, + v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform1fv(location, v, src_offset, src_length); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) { + self.base.Uniform2f(location, x, y) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform2fv( + &self, + location: Option<&WebGLUniformLocation>, + v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform2fv(location, v, src_offset, src_length); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) { + self.base.Uniform2i(location, x, y) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform2iv( + &self, + location: Option<&WebGLUniformLocation>, + v: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform2iv(location, v, src_offset, src_length) + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) { + self.base.Uniform3f(location, x, y, z) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform3fv( + &self, + location: Option<&WebGLUniformLocation>, + v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform3fv(location, v, src_offset, src_length); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) { + self.base.Uniform3i(location, x, y, z) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform3iv( + &self, + location: Option<&WebGLUniformLocation>, + v: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform3iv(location, v, src_offset, src_length) + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) { + self.base.Uniform4i(location, x, y, z, w) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform4iv( + &self, + location: Option<&WebGLUniformLocation>, + v: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform4iv(location, v, src_offset, src_length) + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) { + self.base.Uniform4f(location, x, y, z, w) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn Uniform4fv( + &self, + location: Option<&WebGLUniformLocation>, + v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.uniform4fv(location, v, src_offset, src_length); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + #[allow(unsafe_code)] + fn GetUniform( + &self, + cx: JSContext, + program: &WebGLProgram, + location: &WebGLUniformLocation, + ) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.uniform_check_program(program, location), + return NullValue() + ); + + let triple = (&*self.base, program.id(), location.id()); + + match location.type_() { + constants::UNSIGNED_INT => { + UInt32Value(uniform_get(triple, WebGLCommand::GetUniformUint)) + }, + constants::UNSIGNED_INT_VEC2 => unsafe { + uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint2)) + }, + constants::UNSIGNED_INT_VEC3 => unsafe { + uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint3)) + }, + constants::UNSIGNED_INT_VEC4 => unsafe { + uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint4)) + }, + constants::FLOAT_MAT2x3 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat2x3), + ) + }, + constants::FLOAT_MAT2x4 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat2x4), + ) + }, + constants::FLOAT_MAT3x2 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat3x2), + ) + }, + constants::FLOAT_MAT3x4 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat3x4), + ) + }, + constants::FLOAT_MAT4x2 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat4x2), + ) + }, + constants::FLOAT_MAT4x3 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat4x3), + ) + }, + constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => { + Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)) + }, + _ => self.base.GetUniform(cx, program, location), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn UseProgram(&self, program: Option<&WebGLProgram>) { + self.base.UseProgram(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn ValidateProgram(&self, program: &WebGLProgram) { + self.base.ValidateProgram(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib1f(&self, indx: u32, x: f32) { + self.base.VertexAttrib1f(indx, x) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) { + self.base.VertexAttrib1fv(indx, v) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) { + self.base.VertexAttrib2f(indx, x, y) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) { + self.base.VertexAttrib2fv(indx, v) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) { + self.base.VertexAttrib3f(indx, x, y, z) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) { + self.base.VertexAttrib3fv(indx, v) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) { + self.base.VertexAttrib4f(indx, x, y, z, w) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) { + self.base.VertexAttrib4fv(indx, v) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn VertexAttribI4i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) { + self.vertex_attrib_i(index, x, y, z, w) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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]); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn VertexAttribI4ui(&self, index: u32, x: u32, y: u32, z: u32, w: u32) { + self.vertex_attrib_u(index, x, y, z, w) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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]); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 + fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) { + self.base.Viewport(x, y, width, height) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + 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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + 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 => return Ok(self.base.webgl_error(InvalidOperation)), + }; + + if let Some(tf_buffer) = self.bound_transform_feedback_buffer.get() { + if pixel_unpack_buffer == tf_buffer { + return Ok(self.base.webgl_error(InvalidOperation)); + } + } + + if pbo_offset < 0 || pbo_offset as usize > pixel_unpack_buffer.capacity() { + return Ok(self.base.webgl_error(InvalidValue)); + } + + 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(()) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + 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() { + return Ok(self.base.webgl_error(InvalidOperation)); + } + + 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(()) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + #[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() { + return Ok(self.base.webgl_error(InvalidOperation)); + } + + if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV { + return Ok(self.base.webgl_error(InvalidOperation)); + } + + 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 { + return Ok(self.base.webgl_error(InvalidOperation)); + } + + 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() { + return Ok(self.base.webgl_error(InvalidOperation)); + } + + 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(()) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + 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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn TexParameterf(&self, target: u32, name: u32, value: f32) { + self.base.TexParameterf(target, name, value) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + fn TexParameteri(&self, target: u32, name: u32, value: i32) { + self.base.TexParameteri(target, name, value) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + 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, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 + fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) { + self.base + .RenderbufferStorage(target, internal_format, width, height) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + 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), + }; + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 + 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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> { + self.base.GetAttachedShaders(program) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9 + 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) + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9 + 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) + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9 + 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) + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9 + fn VertexAttribDivisor(&self, index: u32, divisor: u32) { + self.base.vertex_attrib_divisor(index, divisor); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + fn CreateQuery(&self) -> Option<DomRoot<WebGLQuery>> { + Some(WebGLQuery::new(&self.base)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + #[cfg_attr(rustfmt, 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + fn IsQuery(&self, query: Option<&WebGLQuery>) -> bool { + match query { + Some(query) => self.base.validate_ownership(query).is_ok() && query.is_valid(), + None => false, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + fn CreateSampler(&self) -> Option<DomRoot<WebGLSampler>> { + Some(WebGLSampler::new(&self.base)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + 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().map_or(false, |s| sampler == &*s) { + slot.set(None); + } + } + sampler.delete(Operation::Infallible); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + fn IsSampler(&self, sampler: Option<&WebGLSampler>) -> bool { + match sampler { + Some(sampler) => self.base.validate_ownership(sampler).is_ok() && sampler.is_valid(), + None => false, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + #[cfg_attr(rustfmt, 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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + #[cfg_attr(rustfmt, 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); + } + }, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + #[cfg_attr(rustfmt, 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 + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12 + #[cfg_attr(rustfmt, rustfmt_skip)] + fn GetQueryParameter(&self, _cx: JSContext, query: &WebGLQuery, pname: u32) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(query), + return NullValue() + ); + match query.get_parameter(&self.base, pname) { + Ok(value) => match pname { + constants::QUERY_RESULT => UInt32Value(value), + constants::QUERY_RESULT_AVAILABLE => BooleanValue(value != 0), + _ => unreachable!(), + }, + Err(error) => { + self.base.webgl_error(error); + NullValue() + }, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14 + 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)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14 + 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, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14 + 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, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14 + 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)); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14 + fn GetSyncParameter(&self, _cx: JSContext, sync: &WebGLSync, pname: u32) -> JSVal { + if !sync.is_valid() { + self.base.webgl_error(InvalidOperation); + return NullValue(); + } + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(sync), + return 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)); + UInt32Value(receiver.recv().unwrap()) + }, + constants::SYNC_STATUS => match sync.get_sync_status(pname, &self.base) { + Some(status) => UInt32Value(status), + None => UInt32Value(constants::UNSIGNALED), + }, + _ => { + self.base.webgl_error(InvalidEnum); + NullValue() + }, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + 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), + } + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) { + self.base.bind_vertex_array_webgl2(array); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 + fn GetSamplerParameter(&self, _cx: JSContext, sampler: &WebGLSampler, pname: u32) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(sampler), + return NullValue() + ); + match sampler.get_parameter(&self.base, pname) { + Ok(value) => match value { + WebGLSamplerValue::GLenum(value) => UInt32Value(value), + WebGLSamplerValue::Float(value) => DoubleValue(value as f64), + }, + Err(error) => { + self.base.webgl_error(error); + NullValue() + }, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> { + Some(WebGLTransformFeedback::new(&self.base)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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)), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + #[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); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + #[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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + 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), + )) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + 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); + + // for both the generic and the indexed bindings + 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); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + 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); + + // for both the generic and the indexed bindings + 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); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + #[allow(unsafe_code)] + fn GetActiveUniforms( + &self, + cx: JSContext, + program: &WebGLProgram, + indices: Vec<u32>, + pname: u32, + ) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return NullValue() + ); + let values = handle_potential_webgl_error!( + self.base, + program.get_active_uniforms(indices, pname), + return NullValue() + ); + + rooted!(in(*cx) let mut rval = UndefinedValue()); + 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.handle_mut()); + }, + constants::UNIFORM_IS_ROW_MAJOR => unsafe { + let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>(); + values.to_jsval(*cx, rval.handle_mut()); + }, + _ => unreachable!(), + } + rval.get() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn GetUniformBlockIndex(&self, program: &WebGLProgram, block_name: DOMString) -> u32 { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return constants::INVALID_INDEX + ); + let index = handle_potential_webgl_error!( + self.base, + program.get_uniform_block_index(block_name), + return constants::INVALID_INDEX + ); + index + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + #[allow(unsafe_code)] + fn GetActiveUniformBlockParameter( + &self, + cx: JSContext, + program: &WebGLProgram, + block_index: u32, + pname: u32, + ) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return NullValue() + ); + let values = handle_potential_webgl_error!( + self.base, + program.get_active_uniform_block_parameter(block_index, pname), + return NullValue() + ); + match pname { + constants::UNIFORM_BLOCK_BINDING | + constants::UNIFORM_BLOCK_DATA_SIZE | + constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => { + assert!(values.len() == 1); + 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>()); + let _ = Uint32Array::create(*cx, CreateWith::Slice(&values), result.handle_mut()) + .unwrap(); + ObjectValue(result.get()) + }, + constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER | + constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => { + assert!(values.len() == 1); + BooleanValue(values[0] != 0) + }, + _ => unreachable!(), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + 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)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + 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), + return + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11 + 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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11 + 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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11 + 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, + ) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11 + 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, + )); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + 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)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + 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, + )) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + 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), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.5 + #[allow(unsafe_code)] + fn GetInternalformatParameter( + &self, + cx: JSContext, + target: u32, + internal_format: u32, + pname: u32, + ) -> JSVal { + if target != constants::RENDERBUFFER { + self.base.webgl_error(InvalidEnum); + return NullValue(); + } + + match handle_potential_webgl_error!( + self.base, + InternalFormatParameter::from_u32(pname), + return 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>()); + let _ = Int32Array::create( + *cx, + CreateWith::Slice(&receiver.recv().unwrap()), + rval.handle_mut(), + ) + .unwrap(); + ObjectValue(rval.get()) + }, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.5 + fn RenderbufferStorageMultisample( + &self, + target: u32, + samples: i32, + internal_format: u32, + width: i32, + height: i32, + ) { + self.base + .renderbuffer_storage(target, samples, internal_format, width, height) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + 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), return) + } 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)); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11 + 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), return) + } 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)); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + 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) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + 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) + } +} + +impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGL2RenderingContext> { + #[allow(unsafe_code)] + unsafe fn canvas_data_source(self) -> HTMLCanvasDataSource { + let this = &*self.unsafe_get(); + (*this.base.to_layout().unsafe_get()).layout_handle() + } +} |