servo/components/script/dom/webgl/webgl2renderingcontext.rs
Andrei Volykhin 8a62984c2f
canvas: Use non rooted variant of HTMLCanvasElementOrOffscreenCanvas type (#38970)
Any RenderingContext/OffscreenRenderingContext type has readonly
"canvas" attribute
and associated native-code DOM context objects have reference to target
DOM canvas objects.
https://html.spec.whatwg.org/multipage/canvas.html#renderingcontext
https://html.spec.whatwg.org/multipage/canvas.html#offscreenrenderingcontext

And currently the reference to DOM canvas object is the rooting pointer
on the stack,
which leads to the circular reference problem.

The SpiderMonkey's (SM) garbage collector will not be able to free the
DOM canvas and context
objects (unreacheble from JS) because of the rooting pointer on stack
(see STACK_ROOTS).

And these objects will be stored until the associated script
runtime/thread will be terminated.

SM -> JS Roots -> DOM Canvas* (on heap) -> DOM Context (on heap)
SM -> Rust Roots -> Dom Canvas* (on stack) <- as "canvas" member field

Let's replace the rooting pointer to the traceble pointer (DomRoot ->
Dom)
in the "canvas" member field of DOM context object, which allows to
broke circular referencing problem.

Testing: No changes in existed tests

Signed-off-by: Andrei Volykhin <volykhin.andrei@huawei.com>
Co-authored-by: Andrei Volykhin <volykhin.andrei@huawei.com>
2025-09-03 19:24:48 +00:00

4924 lines
171 KiB
Rust

/* 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 std::cell::Cell;
use std::cmp;
use std::ptr::{self, NonNull};
#[cfg(feature = "webxr")]
use std::rc::Rc;
use bitflags::bitflags;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
AlphaTreatment, GLContextAttributes, InternalFormatParameter, TexDataType, TexFormat,
WebGLCommand, WebGLContextId, WebGLResult, WebGLVersion, YAxisTreatment, webgl_channel,
};
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D};
use ipc_channel::ipc::{self, IpcSharedMemory};
use js::jsapi::{JSObject, Type};
use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleValue};
use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array};
use pixels::{Alpha, Snapshot};
use script_bindings::conversions::SafeToJSValConvertible;
use script_bindings::interfaces::WebGL2RenderingContextHelpers;
use servo_config::pref;
use url::Host;
use webrender_api::ImageKey;
use super::validations::types::TexImageTarget;
use crate::canvas_context::{CanvasContext, LayoutCanvasRenderingContextHelpers};
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{
WebGL2RenderingContextConstants as constants, WebGL2RenderingContextMethods,
};
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{
TexImageSource, WebGLContextAttributes, WebGLRenderingContextMethods,
};
use crate::dom::bindings::codegen::UnionTypes::{
ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence,
HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas,
Int32ArrayOrLongSequence, Uint32ArrayOrUnsignedLongSequence,
};
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
#[cfg(feature = "webxr")]
use crate::dom::promise::Promise;
use crate::dom::webgl::validations::WebGLValidator;
use crate::dom::webgl::validations::tex_image_2d::{
TexImage2DValidator, TexImage2DValidatorResult, TexStorageValidator, TexStorageValidatorResult,
};
use crate::dom::webgl::validations::tex_image_3d::{
TexImage3DValidator, TexImage3DValidatorResult,
};
use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
use crate::dom::webgl::webglbuffer::WebGLBuffer;
use crate::dom::webgl::webglframebuffer::{WebGLFramebuffer, WebGLFramebufferAttachmentRoot};
use crate::dom::webgl::webglprogram::WebGLProgram;
use crate::dom::webgl::webglquery::WebGLQuery;
use crate::dom::webgl::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webgl::webglrenderingcontext::{
Operation, TexPixels, TexSource, VertexAttrib, WebGLRenderingContext, uniform_get,
uniform_typed,
};
use crate::dom::webgl::webglsampler::{WebGLSampler, WebGLSamplerValue};
use crate::dom::webgl::webglshader::WebGLShader;
use crate::dom::webgl::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
use crate::dom::webgl::webglsync::WebGLSync;
use crate::dom::webgl::webgltexture::WebGLTexture;
use crate::dom::webgl::webgltransformfeedback::WebGLTransformFeedback;
use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
use crate::dom::webgl::webglvertexarrayobject::WebGLVertexArrayObject;
use crate::dom::window::Window;
use crate::script_runtime::{CanGc, JSContext};
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(JSTraceable, MallocSizeOf)]
struct IndexedBinding {
buffer: MutNullableDom<WebGLBuffer>,
start: Cell<i64>,
size: Cell<i64>,
}
impl IndexedBinding {
fn new() -> IndexedBinding {
IndexedBinding {
buffer: MutNullableDom::new(None),
start: Cell::new(0),
size: Cell::new(0),
}
}
}
#[dom_struct]
pub(crate) 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>,
}
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: &RootedHTMLCanvasElementOrOffscreenCanvas,
size: Size2D<u32>,
attrs: GLContextAttributes,
can_gc: CanGc,
) -> Option<WebGL2RenderingContext> {
let base =
WebGLRenderingContext::new(window, canvas, WebGLVersion::WebGL2, size, attrs, can_gc)?;
let samplers = (0..base.limits().max_combined_texture_image_units)
.map(|_| Default::default())
.collect::<Vec<_>>()
.into();
let indexed_uniform_buffer_bindings = (0..base.limits().max_uniform_buffer_bindings)
.map(|_| IndexedBinding::new())
.collect::<Vec<_>>()
.into();
let indexed_transform_feedback_buffer_bindings =
(0..base.limits().max_transform_feedback_separate_attribs)
.map(|_| IndexedBinding::new())
.collect::<Vec<_>>()
.into();
Some(WebGL2RenderingContext {
reflector_: Reflector::new(),
base: Dom::from_ref(&*base),
occlusion_query: MutNullableDom::new(None),
primitives_query: MutNullableDom::new(None),
samplers,
bound_copy_read_buffer: MutNullableDom::new(None),
bound_copy_write_buffer: MutNullableDom::new(None),
bound_pixel_pack_buffer: MutNullableDom::new(None),
bound_pixel_unpack_buffer: MutNullableDom::new(None),
bound_transform_feedback_buffer: MutNullableDom::new(None),
bound_uniform_buffer: MutNullableDom::new(None),
indexed_uniform_buffer_bindings,
indexed_transform_feedback_buffer_bindings,
current_transform_feedback: MutNullableDom::new(None),
texture_pack_row_length: Cell::new(0),
texture_pack_skip_pixels: Cell::new(0),
texture_pack_skip_rows: Cell::new(0),
enable_rasterizer_discard: Cell::new(false),
default_fb_readbuffer: Cell::new(constants::BACK),
default_fb_drawbuffer: Cell::new(constants::BACK),
})
}
pub(crate) fn new(
window: &Window,
canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
size: Size2D<u32>,
attrs: GLContextAttributes,
can_gc: CanGc,
) -> Option<DomRoot<WebGL2RenderingContext>> {
WebGL2RenderingContext::new_inherited(window, canvas, size, attrs, can_gc)
.map(|ctx| reflect_dom_object(Box::new(ctx), window, can_gc))
}
#[allow(unsafe_code)]
pub(crate) 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(crate) fn current_vao(&self) -> DomRoot<WebGLVertexArrayObject> {
self.base.current_vao_webgl2()
}
pub(crate) 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,
// TODO(#34300): remove unwrap
vao.get_vertex_attrib(prog_attrib.location.unwrap_or(u32::MAX))
.ok_or(InvalidOperation),
return
);
// TODO(#34300): remove unwrap
let current_vertex_attrib = self.base.current_vertex_attribs()[prog_attrib
.location
.map(|l| l as usize)
.unwrap_or(usize::MAX)];
let attrib_data_base_type = if !attrib.enabled_as_array {
match current_vertex_attrib {
VertexAttrib::Int(_, _, _, _) => constants::INT,
VertexAttrib::Uint(_, _, _, _) => constants::UNSIGNED_INT,
VertexAttrib::Float(_, _, _, _) => constants::FLOAT,
}
} else {
attrib.type_
};
let contains = groups
.iter()
.find(|g| g.contains(&attrib_data_base_type) && g.contains(&prog_attrib.type_));
if contains.is_none() {
self.base.webgl_error(InvalidOperation);
return;
}
}
}
pub(crate) 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(crate) fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> {
match usage {
constants::STATIC_READ |
constants::DYNAMIC_READ |
constants::STREAM_READ |
constants::STATIC_COPY |
constants::DYNAMIC_COPY |
constants::STREAM_COPY => Ok(usage),
_ => self.base.buffer_usage(usage),
}
}
fn unbind_from(&self, slot: &MutNullableDom<WebGLBuffer>, buffer: &WebGLBuffer) {
if slot.get().is_some_and(|b| buffer == &*b) {
buffer.decrement_attached_counter(Operation::Infallible);
slot.set(None);
}
}
fn calc_read_pixel_formats(
&self,
pixel_type: u32,
format: u32,
) -> WebGLResult<ReadPixelsAllowedFormats<'_>> {
let array_types = match pixel_type {
constants::BYTE => &[Type::Int8][..],
constants::SHORT => &[Type::Int16][..],
constants::INT => &[Type::Int32][..],
constants::UNSIGNED_BYTE => &[Type::Uint8, Type::Uint8Clamped][..],
constants::UNSIGNED_SHORT |
constants::UNSIGNED_SHORT_4_4_4_4 |
constants::UNSIGNED_SHORT_5_5_5_1 |
constants::UNSIGNED_SHORT_5_6_5 => &[Type::Uint16][..],
constants::UNSIGNED_INT |
constants::UNSIGNED_INT_2_10_10_10_REV |
constants::UNSIGNED_INT_10F_11F_11F_REV |
constants::UNSIGNED_INT_5_9_9_9_REV => &[Type::Uint32][..],
constants::FLOAT => &[Type::Float32][..],
constants::HALF_FLOAT => &[Type::Uint16][..],
_ => return Err(InvalidEnum),
};
let channels = match format {
constants::ALPHA | constants::RED | constants::RED_INTEGER => 1,
constants::RG | constants::RG_INTEGER => 2,
constants::RGB | constants::RGB_INTEGER => 3,
constants::RGBA | constants::RGBA_INTEGER => 4,
_ => return Err(InvalidEnum),
};
Ok(ReadPixelsAllowedFormats {
array_types,
channels,
})
}
fn calc_read_pixel_sizes(
&self,
width: i32,
height: i32,
bytes_per_pixel: usize,
) -> WebGLResult<ReadPixelsSizes> {
if width < 0 || height < 0 {
return Err(InvalidValue);
}
// 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)?;
full_row_bytes
.checked_add(last_row_bytes)
.ok_or(InvalidOperation)?
};
let skipped_bytes = {
let skipped_row_bytes = self
.texture_pack_skip_rows
.get()
.checked_mul(row_stride)
.ok_or(InvalidOperation)?;
let skipped_pixel_bytes = self
.texture_pack_skip_pixels
.get()
.checked_mul(bytes_per_pixel)
.ok_or(InvalidOperation)?;
skipped_row_bytes
.checked_add(skipped_pixel_bytes)
.ok_or(InvalidOperation)?
};
Ok(ReadPixelsSizes {
row_stride,
skipped_bytes,
size,
})
}
#[allow(unsafe_code, clippy::too_many_arguments)]
fn read_pixels_into(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
format: u32,
pixel_type: u32,
dst: &mut ArrayBufferView,
dst_elem_offset: u32,
) {
handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
if self.bound_pixel_pack_buffer.get().is_some() {
return self.base.webgl_error(InvalidOperation);
}
let fb_slot = self.base.get_draw_framebuffer_slot();
let fb_readbuffer_valid = match fb_slot.get() {
Some(fb) => fb.attachment(fb.read_buffer()).is_some(),
None => self.default_fb_readbuffer.get() != constants::NONE,
};
if !fb_readbuffer_valid {
return self.base.webgl_error(InvalidOperation);
}
let dst_byte_offset = {
let dst_elem_size = dst.get_array_type().byte_size().unwrap();
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 = dst_array_type.byte_size().unwrap() * 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_u32(), fb_size.to_u32()) {
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::channel().unwrap();
self.base.send_command(WebGLCommand::ReadPixels(
src_rect, format, pixel_type, sender,
));
let (src, _) = receiver.recv().unwrap();
for i in 0..src_rect.size.height as usize {
let src_start = i * src_row_bytes as usize;
let dst_start = dst_byte_offset + i * row_stride;
dst_pixels[dst_start..dst_start + src_row_bytes as usize]
.copy_from_slice(&src[src_start..src_start + src_row_bytes as usize]);
}
}
fn uniform_vec_section_uint(
&self,
vec: Uint32ArrayOrUnsignedLongSequence,
offset: u32,
length: u32,
uniform_size: usize,
uniform_location: &WebGLUniformLocation,
) -> WebGLResult<Vec<u32>> {
let vec = match vec {
Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
};
self.base
.uniform_vec_section::<u32>(vec, offset, length, uniform_size, uniform_location)
}
#[allow(unsafe_code)]
fn get_default_fb_attachment_param(
&self,
attachment: u32,
pname: u32,
mut retval: MutableHandleValue,
) -> WebGLResult<()> {
match attachment {
constants::BACK | constants::DEPTH | constants::STENCIL => {},
_ => return Err(InvalidEnum),
}
if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
retval.set(NullValue());
return Ok(());
}
let attrs = self
.GetContextAttributes()
.unwrap_or_else(WebGLContextAttributes::empty);
let intval = match pname {
constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => match attachment {
constants::DEPTH if !attrs.depth => constants::NONE as _,
constants::STENCIL if !attrs.stencil => constants::NONE as _,
_ => constants::FRAMEBUFFER_DEFAULT as _,
},
constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE => match attachment {
constants::BACK => 8,
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE => match attachment {
constants::BACK if attrs.alpha => 8,
constants::BACK => return Err(InvalidOperation),
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE => match attachment {
constants::DEPTH if attrs.depth => 24,
constants::DEPTH => return Err(InvalidOperation),
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE => match attachment {
constants::STENCIL if attrs.stencil => 8,
constants::STENCIL => return Err(InvalidOperation),
_ => 0,
},
constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE => match attachment {
constants::DEPTH if attrs.depth => constants::UNSIGNED_NORMALIZED as _,
constants::STENCIL if attrs.stencil => constants::UNSIGNED_INT as _,
constants::DEPTH => return Err(InvalidOperation),
constants::STENCIL => return Err(InvalidOperation),
_ => constants::UNSIGNED_NORMALIZED as _,
},
constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => match attachment {
constants::DEPTH if !attrs.depth => return Err(InvalidOperation),
constants::STENCIL if !attrs.stencil => return Err(InvalidOperation),
_ => constants::LINEAR as _,
},
_ => return Err(InvalidEnum),
};
retval.set(Int32Value(intval));
Ok(())
}
fn get_specific_fb_attachment_param(
&self,
cx: JSContext,
fb: &WebGLFramebuffer,
target: u32,
attachment: u32,
pname: u32,
mut rval: MutableHandleValue,
) -> WebGLResult<()> {
use crate::dom::webgl::webglframebuffer::WebGLFramebufferAttachmentRoot::{
Renderbuffer, Texture,
};
match attachment {
constants::DEPTH_ATTACHMENT | constants::STENCIL_ATTACHMENT => {},
constants::DEPTH_STENCIL_ATTACHMENT => {
if pname == constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE {
return Err(InvalidOperation);
}
let a = fb.attachment(constants::DEPTH_ATTACHMENT);
let b = fb.attachment(constants::STENCIL_ATTACHMENT);
match (a, b) {
(Some(Renderbuffer(ref a)), Some(Renderbuffer(ref b))) if a.id() == b.id() => {
},
(Some(Texture(ref a)), Some(Texture(ref b))) if a.id() == b.id() => {},
_ => return Err(InvalidOperation),
}
},
constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
let last_slot =
constants::COLOR_ATTACHMENT0 + self.base.limits().max_color_attachments - 1;
if last_slot < attachment {
return Err(InvalidEnum);
}
},
_ => return Err(InvalidEnum),
}
let attachment = match attachment {
constants::DEPTH_STENCIL_ATTACHMENT => constants::DEPTH_ATTACHMENT,
_ => attachment,
};
if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
match fb.attachment(attachment) {
Some(Renderbuffer(rb)) => {
rb.safe_to_jsval(cx, rval);
},
Some(Texture(texture)) => {
texture.safe_to_jsval(cx, rval);
},
_ => rval.set(NullValue()),
}
return Ok(());
}
match pname {
constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => {},
_ => match fb.attachment(attachment) {
Some(webgl_attachment) => match pname {
constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE |
constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE |
constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => {},
_ => match webgl_attachment {
WebGLFramebufferAttachmentRoot::Renderbuffer(_) => return Err(InvalidEnum),
WebGLFramebufferAttachmentRoot::Texture(_) => match pname {
constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER => {},
_ => return Err(InvalidEnum),
},
},
},
None => return Err(InvalidOperation),
},
}
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetFramebufferAttachmentParameter(
target, attachment, pname, sender,
));
let retval = receiver.recv().unwrap();
rval.set(Int32Value(retval));
Ok(())
}
fn clearbuffer_array_size(&self, buffer: u32, draw_buffer: i32) -> WebGLResult<usize> {
match buffer {
constants::COLOR => {
if draw_buffer < 0 || draw_buffer as u32 >= self.base.limits().max_draw_buffers {
return Err(InvalidValue);
}
Ok(4)
},
constants::DEPTH | constants::STENCIL | constants::DEPTH_STENCIL => {
if draw_buffer != 0 {
return Err(InvalidValue);
}
Ok(1)
},
_ => unreachable!(),
}
}
fn clear_buffer<T: Clone>(
&self,
buffer: u32,
draw_buffer: i32,
valid_buffers: &[u32],
src_offset: u32,
array: Vec<T>,
msg: fn(u32, i32, Vec<T>) -> WebGLCommand,
) {
if !valid_buffers.contains(&buffer) {
return self.base.webgl_error(InvalidEnum);
}
let array_size = handle_potential_webgl_error!(
self.base,
self.clearbuffer_array_size(buffer, draw_buffer),
return
);
let src_offset = src_offset as usize;
if array.len() < src_offset + array_size {
return self.base.webgl_error(InvalidValue);
}
let array = array[src_offset..src_offset + array_size].to_vec();
self.base.send_command(msg(buffer, draw_buffer, array));
}
fn valid_fb_attachment_values(&self, target: u32, attachments: &[u32]) -> bool {
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => {
self.base.webgl_error(InvalidEnum);
return false;
},
};
if let Some(fb) = fb_slot.get() {
if fb.check_status() != constants::FRAMEBUFFER_COMPLETE {
return false;
}
for &attachment in attachments {
match attachment {
constants::DEPTH_ATTACHMENT |
constants::STENCIL_ATTACHMENT |
constants::DEPTH_STENCIL_ATTACHMENT => {},
constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
let last_slot = constants::COLOR_ATTACHMENT0 +
self.base.limits().max_color_attachments -
1;
if last_slot < attachment {
return false;
}
},
_ => return false,
}
}
} else {
for &attachment in attachments {
match attachment {
constants::COLOR | constants::DEPTH | constants::STENCIL => {},
_ => return false,
}
}
}
true
}
fn vertex_attrib_i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) {
if index >= self.base.limits().max_vertex_attribs {
return self.base.webgl_error(InvalidValue);
}
self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Int(x, y, z, w);
self.current_vao()
.set_vertex_attrib_type(index, constants::INT);
self.base
.send_command(WebGLCommand::VertexAttribI(index, x, y, z, w));
}
fn vertex_attrib_u(&self, index: u32, x: u32, y: u32, z: u32, w: u32) {
if index >= self.base.limits().max_vertex_attribs {
return self.base.webgl_error(InvalidValue);
}
self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Uint(x, y, z, w);
self.current_vao()
.set_vertex_attrib_type(index, constants::UNSIGNED_INT);
self.base
.send_command(WebGLCommand::VertexAttribU(index, x, y, z, w));
}
#[allow(clippy::too_many_arguments)]
fn tex_storage(
&self,
dimensions: u8,
target: u32,
levels: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
) {
let expected_dimensions = match target {
constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP => 2,
constants::TEXTURE_3D | constants::TEXTURE_2D_ARRAY => 3,
_ => return self.base.webgl_error(InvalidEnum),
};
if dimensions != expected_dimensions {
return self.base.webgl_error(InvalidEnum);
}
let validator = TexStorageValidator::new(
&self.base,
dimensions,
target,
levels,
internal_format,
width,
height,
depth,
);
let TexStorageValidatorResult {
texture,
target,
levels,
internal_format,
width,
height,
depth,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return, // NB: The validator sets the correct error for us.
};
handle_potential_webgl_error!(
self.base,
texture.storage(target, levels, internal_format, width, height, depth)
);
}
#[allow(clippy::too_many_arguments)]
fn tex_image_3d(
&self,
texture: &WebGLTexture,
target: TexImageTarget,
data_type: TexDataType,
internal_format: TexFormat,
format: TexFormat,
level: u32,
width: u32,
height: u32,
depth: u32,
_border: u32,
unpacking_alignment: u32,
data: TexPixels,
) {
handle_potential_webgl_error!(
self.base,
texture.initialize(
target,
width,
height,
depth,
internal_format,
level,
Some(data_type)
)
);
let internal_format = self
.base
.extension_manager()
.get_effective_tex_internal_format(internal_format, data_type.as_gl_constant());
let effective_data_type = self
.base
.extension_manager()
.effective_type(data_type.as_gl_constant());
self.base.send_command(WebGLCommand::TexImage3D {
target: target.as_gl_constant(),
level,
internal_format,
size: data.size(),
depth,
format,
data_type,
effective_data_type,
unpacking_alignment,
alpha_treatment: data.alpha_treatment(),
y_axis_treatment: data.y_axis_treatment(),
pixel_format: data.pixel_format(),
data: data.into_shared_memory().into(),
});
// TODO: Hint/tex_parameter
if let Some(fb) = self.base.bound_draw_framebuffer() {
fb.invalidate_texture(texture);
}
}
}
impl CanvasContext for WebGL2RenderingContext {
type ID = WebGLContextId;
fn context_id(&self) -> Self::ID {
self.base.context_id()
}
fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
self.base.canvas()
}
fn resize(&self) {
self.base.resize();
}
fn reset_bitmap(&self) {
self.base.reset_bitmap();
}
fn get_image_data(&self) -> Option<Snapshot> {
self.base.get_image_data()
}
fn mark_as_dirty(&self) {
self.base.mark_as_dirty()
}
fn image_key(&self) -> Option<ImageKey> {
self.base.image_key()
}
}
impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingContext {
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
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,
mut retval: MutableHandleValue,
) {
let buffer = handle_potential_webgl_error!(
self.base,
self.bound_buffer(target),
return retval.set(NullValue())
);
self.base.get_buffer_param(buffer, parameter, retval)
}
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
fn GetParameter(&self, cx: JSContext, parameter: u32, mut rval: MutableHandleValue) {
match parameter {
constants::VERSION => {
"WebGL 2.0".safe_to_jsval(cx, rval);
return;
},
constants::SHADING_LANGUAGE_VERSION => {
"WebGL GLSL ES 3.00".safe_to_jsval(cx, rval);
return;
},
constants::MAX_CLIENT_WAIT_TIMEOUT_WEBGL => {
rval.set(DoubleValue(
self.base.limits().max_client_wait_timeout_webgl.as_nanos() as f64,
));
return;
},
constants::MAX_SERVER_WAIT_TIMEOUT => {
rval.set(DoubleValue(
self.base.limits().max_server_wait_timeout.as_nanos() as f64,
));
return;
},
constants::SAMPLER_BINDING => {
let idx = (self.base.textures().active_unit_enum() - constants::TEXTURE0) as usize;
assert!(idx < self.samplers.len());
let sampler = self.samplers[idx].get();
sampler.safe_to_jsval(cx, rval);
return;
},
constants::COPY_READ_BUFFER_BINDING => {
self.bound_copy_read_buffer.get().safe_to_jsval(cx, rval);
return;
},
constants::COPY_WRITE_BUFFER_BINDING => {
self.bound_copy_write_buffer.get().safe_to_jsval(cx, rval);
return;
},
constants::PIXEL_PACK_BUFFER_BINDING => {
self.bound_pixel_pack_buffer.get().safe_to_jsval(cx, rval);
return;
},
constants::PIXEL_UNPACK_BUFFER_BINDING => {
self.bound_pixel_unpack_buffer.get().safe_to_jsval(cx, rval);
return;
},
constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => {
self.bound_transform_feedback_buffer
.get()
.safe_to_jsval(cx, rval);
return;
},
constants::UNIFORM_BUFFER_BINDING => {
self.bound_uniform_buffer.get().safe_to_jsval(cx, rval);
return;
},
constants::TRANSFORM_FEEDBACK_BINDING => {
self.current_transform_feedback
.get()
.safe_to_jsval(cx, rval);
return;
},
constants::ELEMENT_ARRAY_BUFFER_BINDING => {
let buffer = self.current_vao().element_array_buffer().get();
buffer.safe_to_jsval(cx, rval);
return;
},
constants::VERTEX_ARRAY_BINDING => {
let vao = self.current_vao();
let vao = vao.id().map(|_| &*vao);
vao.safe_to_jsval(cx, rval);
return;
},
// NOTE: DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING, handled on the WebGL1 side
constants::READ_FRAMEBUFFER_BINDING => {
self.base
.get_read_framebuffer_slot()
.get()
.safe_to_jsval(cx, rval);
return;
},
constants::READ_BUFFER => {
let buffer = match self.base.get_read_framebuffer_slot().get() {
Some(fb) => fb.read_buffer(),
None => self.default_fb_readbuffer.get(),
};
rval.set(UInt32Value(buffer));
return;
},
constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => {
let buffer = match self.base.get_read_framebuffer_slot().get() {
Some(fb) => {
let idx = parameter - constants::DRAW_BUFFER0;
fb.draw_buffer_i(idx as usize)
},
None if parameter == constants::DRAW_BUFFER0 => {
self.default_fb_readbuffer.get()
},
None => constants::NONE,
};
rval.set(UInt32Value(buffer));
return;
},
constants::MAX_TEXTURE_LOD_BIAS => {
rval.set(DoubleValue(self.base.limits().max_texture_lod_bias as f64));
return;
},
constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => {
rval.set(DoubleValue(
self.base.limits().max_combined_fragment_uniform_components as f64,
));
return;
},
constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => {
rval.set(DoubleValue(
self.base.limits().max_combined_vertex_uniform_components as f64,
));
return;
},
constants::MAX_ELEMENT_INDEX => {
rval.set(DoubleValue(self.base.limits().max_element_index as f64));
return;
},
constants::MAX_UNIFORM_BLOCK_SIZE => {
rval.set(DoubleValue(
self.base.limits().max_uniform_block_size as f64,
));
return;
},
constants::MIN_PROGRAM_TEXEL_OFFSET => {
rval.set(Int32Value(self.base.limits().min_program_texel_offset));
return;
},
_ => {},
}
let limit = match parameter {
constants::MAX_3D_TEXTURE_SIZE => Some(self.base.limits().max_3d_texture_size),
constants::MAX_ARRAY_TEXTURE_LAYERS => {
Some(self.base.limits().max_array_texture_layers)
},
constants::MAX_COLOR_ATTACHMENTS => Some(self.base.limits().max_color_attachments),
constants::MAX_COMBINED_UNIFORM_BLOCKS => {
Some(self.base.limits().max_combined_uniform_blocks)
},
constants::MAX_DRAW_BUFFERS => Some(self.base.limits().max_draw_buffers),
constants::MAX_ELEMENTS_INDICES => Some(self.base.limits().max_elements_indices),
constants::MAX_ELEMENTS_VERTICES => Some(self.base.limits().max_elements_vertices),
constants::MAX_FRAGMENT_INPUT_COMPONENTS => {
Some(self.base.limits().max_fragment_input_components)
},
constants::MAX_FRAGMENT_UNIFORM_BLOCKS => {
Some(self.base.limits().max_fragment_uniform_blocks)
},
constants::MAX_FRAGMENT_UNIFORM_COMPONENTS => {
Some(self.base.limits().max_fragment_uniform_components)
},
constants::MAX_PROGRAM_TEXEL_OFFSET => {
Some(self.base.limits().max_program_texel_offset)
},
constants::MAX_SAMPLES => Some(self.base.limits().max_samples),
constants::MAX_UNIFORM_BUFFER_BINDINGS => {
Some(self.base.limits().max_uniform_buffer_bindings)
},
constants::MAX_VARYING_COMPONENTS => Some(self.base.limits().max_varying_components),
constants::MAX_VERTEX_OUTPUT_COMPONENTS => {
Some(self.base.limits().max_vertex_output_components)
},
constants::MAX_VERTEX_UNIFORM_BLOCKS => {
Some(self.base.limits().max_vertex_uniform_blocks)
},
constants::MAX_VERTEX_UNIFORM_COMPONENTS => {
Some(self.base.limits().max_vertex_uniform_components)
},
constants::UNIFORM_BUFFER_OFFSET_ALIGNMENT => {
Some(self.base.limits().uniform_buffer_offset_alignment)
},
_ => None,
};
if let Some(limit) = limit {
rval.set(UInt32Value(limit));
return;
}
self.base.GetParameter(cx, parameter, rval)
}
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
fn GetTexParameter(&self, cx: JSContext, target: u32, pname: u32, retval: MutableHandleValue) {
self.base.GetTexParameter(cx, target, pname, retval)
}
/// <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,
mut rval: MutableHandleValue,
) {
let fb_slot = match target {
constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
self.base.get_draw_framebuffer_slot()
},
constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
_ => {
self.base.webgl_error(InvalidEnum);
rval.set(NullValue());
return;
},
};
if let Some(fb) = fb_slot.get() {
// A selected framebuffer is bound to the target
handle_potential_webgl_error!(
self.base,
fb.validate_transparent(),
return rval.set(NullValue())
);
handle_potential_webgl_error!(
self.base,
self.get_specific_fb_attachment_param(
cx,
&fb,
target,
attachment,
pname,
rval.reborrow()
),
rval.set(NullValue())
)
} else {
// The default framebuffer is bound to the target
handle_potential_webgl_error!(
self.base,
self.get_default_fb_attachment_param(attachment, pname, rval.reborrow()),
rval.set(NullValue())
)
}
}
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
fn GetRenderbufferParameter(
&self,
cx: JSContext,
target: u32,
pname: u32,
retval: MutableHandleValue,
) {
self.base
.GetRenderbufferParameter(cx, target, pname, retval)
}
/// <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 = data.get_array_type().byte_size().unwrap();
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 = src_data.get_array_type().byte_size().unwrap();
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()
.is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
let dst_is_elemarray = write_buffer
.target()
.is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
if src_is_elemarray != dst_is_elemarray {
return self.base.webgl_error(InvalidOperation);
}
self.base.send_command(WebGLCommand::CopyBufferSubData(
read_target,
write_target,
read_offset,
write_offset,
size,
));
}
/// <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 = dst_buffer.get_array_type().byte_size().unwrap();
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,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return retval.set(NullValue())
);
if program.is_deleted() {
self.base.webgl_error(InvalidOperation);
return retval.set(NullValue());
}
match param_id {
constants::TRANSFORM_FEEDBACK_VARYINGS => {
retval.set(Int32Value(program.transform_feedback_varyings_length()))
},
constants::TRANSFORM_FEEDBACK_BUFFER_MODE => {
retval.set(Int32Value(program.transform_feedback_buffer_mode()))
},
_ => self.base.GetProgramParameter(cx, program, param_id, retval),
}
}
/// <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,
retval: MutableHandleValue,
) {
self.base.GetShaderParameter(cx, shader, param_id, retval)
}
/// <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>
fn GetIndexedParameter(
&self,
cx: JSContext,
target: u32,
index: u32,
mut retval: MutableHandleValue,
) {
let bindings = match target {
constants::TRANSFORM_FEEDBACK_BUFFER_BINDING |
constants::TRANSFORM_FEEDBACK_BUFFER_SIZE |
constants::TRANSFORM_FEEDBACK_BUFFER_START => {
&self.indexed_transform_feedback_buffer_bindings
},
constants::UNIFORM_BUFFER_BINDING |
constants::UNIFORM_BUFFER_SIZE |
constants::UNIFORM_BUFFER_START => &self.indexed_uniform_buffer_bindings,
_ => {
self.base.webgl_error(InvalidEnum);
return retval.set(NullValue());
},
};
let binding = match bindings.get(index as usize) {
Some(binding) => binding,
None => {
self.base.webgl_error(InvalidValue);
return retval.set(NullValue());
},
};
match target {
constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => {
binding.buffer.get().safe_to_jsval(cx, retval)
},
constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => {
retval.set(Int32Value(binding.start.get() as _))
},
constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => {
retval.set(Int32Value(binding.size.get() as _))
},
_ => unreachable!(),
}
}
/// <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, retval: MutableHandleValue) {
self.base.GetVertexAttrib(cx, index, pname, retval)
}
/// <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_u32(), fb_size.to_u32()).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_2D_ARRAY |
constants::SAMPLER_3D |
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,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.uniform_check_program(program, location),
return retval.set(NullValue())
);
let triple = (&*self.base, program.id(), location.id());
match location.type_() {
constants::UNSIGNED_INT => retval.set(UInt32Value(uniform_get(
triple,
WebGLCommand::GetUniformUint,
))),
constants::UNSIGNED_INT_VEC2 => unsafe {
uniform_typed::<Uint32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformUint2),
retval,
)
},
constants::UNSIGNED_INT_VEC3 => unsafe {
uniform_typed::<Uint32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformUint3),
retval,
)
},
constants::UNSIGNED_INT_VEC4 => unsafe {
uniform_typed::<Uint32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformUint4),
retval,
)
},
constants::FLOAT_MAT2x3 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat2x3),
retval,
)
},
constants::FLOAT_MAT2x4 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat2x4),
retval,
)
},
constants::FLOAT_MAT3x2 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat3x2),
retval,
)
},
constants::FLOAT_MAT3x4 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat3x4),
retval,
)
},
constants::FLOAT_MAT4x2 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat4x2),
retval,
)
},
constants::FLOAT_MAT4x3 => unsafe {
uniform_typed::<Float32>(
*cx,
&uniform_get(triple, WebGLCommand::GetUniformFloat4x3),
retval,
)
},
constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => {
retval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
},
_ => self.base.GetUniform(cx, program, location, retval),
}
}
/// <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/2.0/#3.7.6>
///
/// Allocates and initializes the specified mipmap level of a three-dimensional or
/// two-dimensional array texture.
#[allow(unsafe_code)]
fn TexImage3D(
&self,
target: u32,
level: i32,
internal_format: i32,
width: i32,
height: i32,
depth: i32,
border: i32,
format: u32,
type_: u32,
src_data: CustomAutoRooterGuard<Option<ArrayBufferView>>,
) -> Fallible<()> {
// If a WebGLBuffer is bound to the PIXEL_UNPACK_BUFFER target,
// generates an INVALID_OPERATION error.
if self.bound_pixel_unpack_buffer.get().is_some() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
// If type is specified as FLOAT_32_UNSIGNED_INT_24_8_REV, srcData must be null;
// otherwise, generates an INVALID_OPERATION error.
if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV && src_data.is_some() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
if border != 0 {
self.base.webgl_error(InvalidValue);
return Ok(());
}
let Ok(TexImage3DValidatorResult {
width,
height,
depth,
level,
border,
texture,
target,
internal_format,
format,
data_type,
}) = TexImage3DValidator::new(
&self.base,
target,
level,
internal_format as u32,
width,
height,
depth,
border,
format,
type_,
&src_data,
)
.validate()
else {
return Ok(());
};
// TODO: If pixel store parameter constraints are not met, generates an INVALID_OPERATION error.
// If srcData is null, a buffer of sufficient size initialized to 0 is passed.
let unpacking_alignment = self.base.texture_unpacking_alignment();
let buff = match *src_data {
Some(ref data) => IpcSharedMemory::from_bytes(unsafe { data.as_slice() }),
None => {
let element_size = data_type.element_size();
let components = format.components();
let components_per_element = format.components();
// FIXME: This is copied from tex_image_2d which is apparently incorrect
// NOTE: width and height are positive or zero due to validate()
let expected_byte_len = if height == 0 {
0
} else {
// We need to be careful here to not count unpack
// alignment at the end of the image, otherwise (for
// example) passing a single byte for uploading a 1x1
// GL_ALPHA/GL_UNSIGNED_BYTE texture would throw an error.
let cpp = element_size * components / components_per_element;
let stride =
(width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
stride * (height - 1) + width * cpp
};
IpcSharedMemory::from_bytes(&vec![0u8; expected_byte_len as usize])
},
};
let (alpha_treatment, y_axis_treatment) =
self.base.get_current_unpack_state(Alpha::NotPremultiplied);
// If UNPACK_FLIP_Y_WEBGL or UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to true, texImage3D and texSubImage3D
// generate an INVALID_OPERATION error if they upload data from a PIXEL_UNPACK_BUFFER or a non-null client
// side ArrayBufferView.
if let (Some(AlphaTreatment::Premultiply), YAxisTreatment::Flipped) =
(alpha_treatment, y_axis_treatment)
{
if src_data.is_some() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
}
let tex_source = TexPixels::from_array(
buff,
Size2D::new(width, height),
alpha_treatment,
y_axis_treatment,
);
self.tex_image_3d(
&texture,
target,
data_type,
internal_format,
format,
level,
width,
height,
depth,
border,
unpacking_alignment,
tex_source,
);
Ok(())
}
/// <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: TexImageSource,
) -> 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 => {
self.base.webgl_error(InvalidOperation);
return Ok(());
},
};
if let Some(tf_buffer) = self.bound_transform_feedback_buffer.get() {
if pixel_unpack_buffer == tf_buffer {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
}
if pbo_offset < 0 || pbo_offset as usize > pixel_unpack_buffer.capacity() {
self.base.webgl_error(InvalidValue);
return Ok(());
}
let unpacking_alignment = self.base.texture_unpacking_alignment();
let validator = TexImage2DValidator::new(
&self.base,
target,
level,
internalformat as u32,
width,
height,
border,
format,
type_,
);
let TexImage2DValidatorResult {
texture,
target,
width,
height,
level,
border,
internal_format,
format,
data_type,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()),
};
self.base.tex_image_2d(
&texture,
target,
data_type,
internal_format,
format,
level,
border,
unpacking_alignment,
Size2D::new(width, height),
TexSource::BufferOffset(pbo_offset),
);
Ok(())
}
/// <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: TexImageSource,
) -> Fallible<()> {
if self.bound_pixel_unpack_buffer.get().is_some() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let validator = TexImage2DValidator::new(
&self.base,
target,
level,
internalformat as u32,
width,
height,
border,
format,
type_,
);
let TexImage2DValidatorResult {
texture,
target,
width: _,
height: _,
level,
border,
internal_format,
format,
data_type,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()),
};
let unpacking_alignment = self.base.texture_unpacking_alignment();
let pixels = match self.base.get_image_pixels(source)? {
Some(pixels) => pixels,
None => return Ok(()),
};
self.base.tex_image_2d(
&texture,
target,
data_type,
internal_format,
format,
level,
border,
unpacking_alignment,
pixels.size(),
TexSource::Pixels(pixels),
);
Ok(())
}
/// <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() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let validator = TexImage2DValidator::new(
&self.base,
target,
level,
internalformat as u32,
width,
height,
border,
format,
type_,
);
let TexImage2DValidatorResult {
texture,
target,
width,
height,
level,
border,
internal_format,
format,
data_type,
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()),
};
let unpacking_alignment = self.base.texture_unpacking_alignment();
let src_elem_size = src_data.get_array_type().byte_size().unwrap();
let src_byte_offset = src_offset as usize * src_elem_size;
if src_data.len() <= src_byte_offset {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let buff = IpcSharedMemory::from_bytes(unsafe { &src_data.as_slice()[src_byte_offset..] });
let expected_byte_length = match self.base.validate_tex_image_2d_data(
width,
height,
format,
data_type,
unpacking_alignment,
Some(&*src_data),
) {
Ok(byte_length) => byte_length,
Err(()) => return Ok(()),
};
if expected_byte_length as usize > buff.len() {
self.base.webgl_error(InvalidOperation);
return Ok(());
}
let size = Size2D::new(width, height);
let (alpha_treatment, y_axis_treatment) =
self.base.get_current_unpack_state(Alpha::NotPremultiplied);
self.base.tex_image_2d(
&texture,
target,
data_type,
internal_format,
format,
level,
border,
unpacking_alignment,
size,
TexSource::Pixels(TexPixels::from_array(
buff,
size,
alpha_treatment,
y_axis_treatment,
)),
);
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: TexImageSource,
) -> 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/2.0/#3.7.4S>
fn BlitFramebuffer(
&self,
src_x0: i32,
src_y0: i32,
src_x1: i32,
src_y1: i32,
dst_x0: i32,
dst_y0: i32,
dst_x1: i32,
dst_y1: i32,
mask: u32,
filter: u32,
) {
bitflags! {
struct BlitFrameBufferFlags: u32 {
const DEPTH = constants::DEPTH_BUFFER_BIT;
const COLOR = constants::COLOR_BUFFER_BIT;
const STENCIL = constants::STENCIL_BUFFER_BIT;
const DEPTH_STENCIL = constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT;
}
};
let Some(bits) = BlitFrameBufferFlags::from_bits(mask) else {
return self.base.webgl_error(InvalidValue);
};
let attributes = self.base.GetContextAttributes().unwrap();
if bits.intersects(BlitFrameBufferFlags::DEPTH_STENCIL) {
match filter {
constants::LINEAR => return self.base.webgl_error(InvalidOperation),
constants::NEAREST => {},
_ => return self.base.webgl_error(InvalidOperation),
}
}
let src_fb = self.base.get_read_framebuffer_slot().get();
let dst_fb = self.base.get_draw_framebuffer_slot().get();
let get_default_formats = || -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
// All attempts to blit to an antialiased back buffer should fail.
if attributes.antialias {
return Err(InvalidOperation);
};
let color = if attributes.alpha {
Some(constants::RGBA8)
} else {
Some(constants::RGB8)
};
let (depth, stencil) = match (attributes.depth, attributes.stencil) {
(true, true) => (
Some(constants::DEPTH24_STENCIL8),
Some(constants::DEPTH24_STENCIL8),
),
(true, false) => (Some(constants::DEPTH_COMPONENT16), None),
(false, true) => (None, Some(constants::STENCIL_INDEX8)),
_ => (None, None),
};
Ok((color, depth, stencil))
};
let (src_color, src_depth, src_stencil) = match src_fb {
Some(fb) => {
handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
},
None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
};
let (dst_color, dst_depth, dst_stencil) = match dst_fb {
Some(fb) => {
handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
},
None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
};
if bits.intersects(BlitFrameBufferFlags::COLOR) && src_color != dst_color {
return self.base.webgl_error(InvalidOperation);
}
if bits.intersects(BlitFrameBufferFlags::DEPTH) && src_depth != dst_depth {
return self.base.webgl_error(InvalidOperation);
}
if bits.intersects(BlitFrameBufferFlags::STENCIL) && src_stencil != dst_stencil {
return self.base.webgl_error(InvalidOperation);
}
let src_width = src_x1.checked_sub(src_x0);
let dst_width = dst_x1.checked_sub(dst_x0);
let src_height = src_y1.checked_sub(src_y0);
let dst_height = dst_y1.checked_sub(dst_y0);
if src_width.is_none() ||
dst_width.is_none() ||
src_height.is_none() ||
dst_height.is_none()
{
return self.base.webgl_error(InvalidOperation);
}
self.base.send_command(WebGLCommand::BlitFrameBuffer(
src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
));
}
/// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
fn FramebufferRenderbuffer(
&self,
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, CanGc::note()))
}
/// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
#[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, CanGc::note()))
}
/// <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().is_some_and(|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>
#[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>
#[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>
#[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>
#[rustfmt::skip]
fn GetQueryParameter(&self, _cx: JSContext, query: &WebGLQuery, pname: u32, mut retval: MutableHandleValue) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(query),
return retval.set(NullValue())
);
match query.get_parameter(&self.base, pname) {
Ok(value) => match pname {
constants::QUERY_RESULT => retval.set(UInt32Value(value)),
constants::QUERY_RESULT_AVAILABLE => retval.set(BooleanValue(value != 0)),
_ => unreachable!(),
},
Err(error) => {
self.base.webgl_error(error);
retval.set(NullValue())
},
}
}
/// <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, CanGc::note()))
}
/// <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,
mut retval: MutableHandleValue,
) {
if !sync.is_valid() {
self.base.webgl_error(InvalidOperation);
return retval.set(NullValue());
}
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(sync),
return retval.set(NullValue())
);
match pname {
constants::OBJECT_TYPE | constants::SYNC_CONDITION | constants::SYNC_FLAGS => {
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetSyncParameter(sync.id(), pname, sender));
retval.set(UInt32Value(receiver.recv().unwrap()))
},
constants::SYNC_STATUS => match sync.get_sync_status(pname, &self.base) {
Some(status) => retval.set(UInt32Value(status)),
None => retval.set(UInt32Value(constants::UNSIGNALED)),
},
_ => {
self.base.webgl_error(InvalidEnum);
retval.set(NullValue())
},
}
}
/// <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,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(sampler),
return retval.set(NullValue())
);
match sampler.get_parameter(&self.base, pname) {
Ok(value) => match value {
WebGLSamplerValue::GLenum(value) => retval.set(UInt32Value(value)),
WebGLSamplerValue::Float(value) => retval.set(DoubleValue(value as f64)),
},
Err(error) => {
self.base.webgl_error(error);
retval.set(NullValue())
},
}
}
/// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> {
Some(WebGLTransformFeedback::new(&self.base, CanGc::note()))
}
/// <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),
CanGc::note(),
))
}
/// <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>
fn GetActiveUniforms(
&self,
cx: JSContext,
program: &WebGLProgram,
indices: Vec<u32>,
pname: u32,
mut rval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return rval.set(NullValue())
);
let values = handle_potential_webgl_error!(
self.base,
program.get_active_uniforms(indices, pname),
return rval.set(NullValue())
);
match pname {
constants::UNIFORM_SIZE |
constants::UNIFORM_TYPE |
constants::UNIFORM_BLOCK_INDEX |
constants::UNIFORM_OFFSET |
constants::UNIFORM_ARRAY_STRIDE |
constants::UNIFORM_MATRIX_STRIDE => {
values.safe_to_jsval(cx, rval);
},
constants::UNIFORM_IS_ROW_MAJOR => {
let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>();
values.safe_to_jsval(cx, rval);
},
_ => unreachable!(),
}
}
/// <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
);
handle_potential_webgl_error!(
self.base,
program.get_uniform_block_index(block_name),
constants::INVALID_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,
mut retval: MutableHandleValue,
) {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return retval.set(NullValue())
);
let values = handle_potential_webgl_error!(
self.base,
program.get_active_uniform_block_parameter(block_index, pname),
return retval.set(NullValue())
);
match pname {
constants::UNIFORM_BLOCK_BINDING |
constants::UNIFORM_BLOCK_DATA_SIZE |
constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => {
assert!(values.len() == 1);
retval.set(UInt32Value(values[0] as u32))
},
constants::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
let values = values.iter().map(|&v| v as u32).collect::<Vec<_>>();
rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
Uint32Array::create(*cx, CreateWith::Slice(&values), result.handle_mut()).unwrap();
retval.set(ObjectValue(result.get()))
},
constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {
assert!(values.len() == 1);
retval.set(BooleanValue(values[0] != 0))
},
_ => unreachable!(),
}
}
/// <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)
)
}
/// <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,
mut retval: MutableHandleValue,
) {
if target != constants::RENDERBUFFER {
self.base.webgl_error(InvalidEnum);
return retval.set(NullValue());
}
match handle_potential_webgl_error!(
self.base,
InternalFormatParameter::from_u32(pname),
return retval.set(NullValue())
) {
InternalFormatParameter::IntVec(param) => unsafe {
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetInternalFormatIntVec(
target,
internal_format,
param,
sender,
));
rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
Int32Array::create(
*cx,
CreateWith::Slice(&receiver.recv().unwrap()),
rval.handle_mut(),
)
.unwrap();
retval.set(ObjectValue(rval.get()))
},
}
}
/// <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))
} 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))
} 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)
}
/// <https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible>
#[cfg(feature = "webxr")]
fn MakeXRCompatible(&self, can_gc: CanGc) -> Rc<Promise> {
// XXXManishearth Fill in with compatibility checks when rust-webxr supports this
let p = Promise::new(&self.global(), can_gc);
p.resolve_native(&(), can_gc);
p
}
}
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGL2RenderingContext> {
#[allow(unsafe_code)]
fn canvas_data_source(self) -> Option<ImageKey> {
let this = self.unsafe_get();
unsafe { (*this.base.to_layout().unsafe_get()).layout_handle() }
}
}
impl WebGL2RenderingContextHelpers for WebGL2RenderingContext {
fn is_webgl2_enabled(cx: JSContext, global: HandleObject) -> bool {
Self::is_webgl2_enabled(cx, global)
}
}