1use std::cell::Cell;
6use std::cmp;
7use std::ptr::{self, NonNull};
8#[cfg(feature = "webxr")]
9use std::rc::Rc;
10
11use base::generic_channel::{self, GenericSharedMemory};
12use bitflags::bitflags;
13use canvas_traits::webgl::WebGLError::*;
14use canvas_traits::webgl::{
15 AlphaTreatment, GLContextAttributes, InternalFormatParameter, TexDataType, TexFormat,
16 WebGLCommand, WebGLContextId, WebGLResult, WebGLVersion, YAxisTreatment, webgl_channel,
17};
18use dom_struct::dom_struct;
19use euclid::default::{Point2D, Rect, Size2D};
20use js::jsapi::{JSObject, Type};
21use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
22use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleValue};
23use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array};
24use pixels::{Alpha, Snapshot};
25use script_bindings::conversions::SafeToJSValConvertible;
26use script_bindings::interfaces::WebGL2RenderingContextHelpers;
27use servo_config::pref;
28use url::Host;
29use webrender_api::ImageKey;
30
31use super::validations::types::TexImageTarget;
32use crate::canvas_context::CanvasContext;
33use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{
34 WebGL2RenderingContextConstants as constants, WebGL2RenderingContextMethods,
35};
36use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{
37 TexImageSource, WebGLContextAttributes, WebGLRenderingContextMethods,
38};
39use crate::dom::bindings::codegen::UnionTypes::{
40 ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence,
41 HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas,
42 Int32ArrayOrLongSequence, Uint32ArrayOrUnsignedLongSequence,
43};
44use crate::dom::bindings::error::{ErrorResult, Fallible};
45use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
46use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
47use crate::dom::bindings::str::DOMString;
48use crate::dom::globalscope::GlobalScope;
49#[cfg(feature = "webxr")]
50use crate::dom::promise::Promise;
51use crate::dom::webgl::validations::WebGLValidator;
52use crate::dom::webgl::validations::tex_image_2d::{
53 TexImage2DValidator, TexImage2DValidatorResult, TexStorageValidator, TexStorageValidatorResult,
54};
55use crate::dom::webgl::validations::tex_image_3d::{
56 TexImage3DValidator, TexImage3DValidatorResult,
57};
58use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
59use crate::dom::webgl::webglbuffer::WebGLBuffer;
60use crate::dom::webgl::webglframebuffer::{WebGLFramebuffer, WebGLFramebufferAttachmentRoot};
61use crate::dom::webgl::webglprogram::WebGLProgram;
62use crate::dom::webgl::webglquery::WebGLQuery;
63use crate::dom::webgl::webglrenderbuffer::WebGLRenderbuffer;
64use crate::dom::webgl::webglrenderingcontext::{
65 Operation, TexPixels, TexSource, VertexAttrib, WebGLRenderingContext, uniform_get,
66 uniform_typed,
67};
68use crate::dom::webgl::webglsampler::{WebGLSampler, WebGLSamplerValue};
69use crate::dom::webgl::webglshader::WebGLShader;
70use crate::dom::webgl::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
71use crate::dom::webgl::webglsync::WebGLSync;
72use crate::dom::webgl::webgltexture::WebGLTexture;
73use crate::dom::webgl::webgltransformfeedback::WebGLTransformFeedback;
74use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
75use crate::dom::webgl::webglvertexarrayobject::WebGLVertexArrayObject;
76use crate::dom::window::Window;
77use crate::script_runtime::{CanGc, JSContext};
78
79#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
80#[derive(JSTraceable, MallocSizeOf)]
81struct IndexedBinding {
82 buffer: MutNullableDom<WebGLBuffer>,
83 start: Cell<i64>,
84 size: Cell<i64>,
85}
86
87impl IndexedBinding {
88 fn new() -> IndexedBinding {
89 IndexedBinding {
90 buffer: MutNullableDom::new(None),
91 start: Cell::new(0),
92 size: Cell::new(0),
93 }
94 }
95}
96
97#[dom_struct]
98pub(crate) struct WebGL2RenderingContext {
99 reflector_: Reflector,
100 base: Dom<WebGLRenderingContext>,
101 occlusion_query: MutNullableDom<WebGLQuery>,
102 primitives_query: MutNullableDom<WebGLQuery>,
103 samplers: Box<[MutNullableDom<WebGLSampler>]>,
104 bound_copy_read_buffer: MutNullableDom<WebGLBuffer>,
105 bound_copy_write_buffer: MutNullableDom<WebGLBuffer>,
106 bound_pixel_pack_buffer: MutNullableDom<WebGLBuffer>,
107 bound_pixel_unpack_buffer: MutNullableDom<WebGLBuffer>,
108 bound_transform_feedback_buffer: MutNullableDom<WebGLBuffer>,
109 bound_uniform_buffer: MutNullableDom<WebGLBuffer>,
110 indexed_uniform_buffer_bindings: Box<[IndexedBinding]>,
111 indexed_transform_feedback_buffer_bindings: Box<[IndexedBinding]>,
112 current_transform_feedback: MutNullableDom<WebGLTransformFeedback>,
113 texture_pack_row_length: Cell<usize>,
114 texture_pack_skip_pixels: Cell<usize>,
115 texture_pack_skip_rows: Cell<usize>,
116 enable_rasterizer_discard: Cell<bool>,
117 default_fb_readbuffer: Cell<u32>,
118 default_fb_drawbuffer: Cell<u32>,
119}
120
121struct ReadPixelsAllowedFormats<'a> {
122 array_types: &'a [Type],
123 channels: usize,
124}
125
126struct ReadPixelsSizes {
127 row_stride: usize,
128 skipped_bytes: usize,
129 size: usize,
130}
131
132impl WebGL2RenderingContext {
133 fn new_inherited(
134 window: &Window,
135 canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
136 size: Size2D<u32>,
137 attrs: GLContextAttributes,
138 can_gc: CanGc,
139 ) -> Option<WebGL2RenderingContext> {
140 let base =
141 WebGLRenderingContext::new(window, canvas, WebGLVersion::WebGL2, size, attrs, can_gc)?;
142
143 let samplers = (0..base.limits().max_combined_texture_image_units)
144 .map(|_| Default::default())
145 .collect::<Vec<_>>()
146 .into();
147 let indexed_uniform_buffer_bindings = (0..base.limits().max_uniform_buffer_bindings)
148 .map(|_| IndexedBinding::new())
149 .collect::<Vec<_>>()
150 .into();
151 let indexed_transform_feedback_buffer_bindings =
152 (0..base.limits().max_transform_feedback_separate_attribs)
153 .map(|_| IndexedBinding::new())
154 .collect::<Vec<_>>()
155 .into();
156
157 Some(WebGL2RenderingContext {
158 reflector_: Reflector::new(),
159 base: Dom::from_ref(&*base),
160 occlusion_query: MutNullableDom::new(None),
161 primitives_query: MutNullableDom::new(None),
162 samplers,
163 bound_copy_read_buffer: MutNullableDom::new(None),
164 bound_copy_write_buffer: MutNullableDom::new(None),
165 bound_pixel_pack_buffer: MutNullableDom::new(None),
166 bound_pixel_unpack_buffer: MutNullableDom::new(None),
167 bound_transform_feedback_buffer: MutNullableDom::new(None),
168 bound_uniform_buffer: MutNullableDom::new(None),
169 indexed_uniform_buffer_bindings,
170 indexed_transform_feedback_buffer_bindings,
171 current_transform_feedback: MutNullableDom::new(None),
172 texture_pack_row_length: Cell::new(0),
173 texture_pack_skip_pixels: Cell::new(0),
174 texture_pack_skip_rows: Cell::new(0),
175 enable_rasterizer_discard: Cell::new(false),
176 default_fb_readbuffer: Cell::new(constants::BACK),
177 default_fb_drawbuffer: Cell::new(constants::BACK),
178 })
179 }
180
181 pub(crate) fn new(
182 window: &Window,
183 canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
184 size: Size2D<u32>,
185 attrs: GLContextAttributes,
186 can_gc: CanGc,
187 ) -> Option<DomRoot<WebGL2RenderingContext>> {
188 WebGL2RenderingContext::new_inherited(window, canvas, size, attrs, can_gc)
189 .map(|ctx| reflect_dom_object(Box::new(ctx), window, can_gc))
190 }
191
192 pub(crate) fn set_image_key(&self, image_key: ImageKey) {
193 self.base.set_image_key(image_key);
194 }
195
196 #[expect(unsafe_code)]
197 pub(crate) fn is_webgl2_enabled(_cx: JSContext, global: HandleObject) -> bool {
198 if pref!(dom_webgl2_enabled) {
199 return true;
200 }
201
202 let global = unsafe { GlobalScope::from_object(global.get()) };
203 let origin = global.origin();
204 let host = origin.host();
205 WEBGL2_ORIGINS
206 .iter()
207 .any(|origin| host == Host::parse(origin).ok().as_ref())
208 }
209}
210
211static WEBGL2_ORIGINS: &[&str] = &["www.servoexperiments.com"];
214
215impl WebGL2RenderingContext {
216 pub(crate) fn current_vao(&self) -> DomRoot<WebGLVertexArrayObject> {
217 self.base.current_vao_webgl2()
218 }
219
220 pub(crate) fn validate_uniform_block_for_draw(&self) {
221 let program = match self.base.current_program() {
222 Some(program) => program,
223 None => return,
224 };
225 for uniform_block in program.active_uniform_blocks().iter() {
226 let data_size = uniform_block.size as usize;
227 for block in program.active_uniforms().iter() {
228 let index = match block.bind_index {
229 Some(index) => index,
230 None => continue,
231 };
232 let indexed = &self.indexed_uniform_buffer_bindings[index as usize];
233 let buffer = match indexed.buffer.get() {
234 Some(buffer) => buffer,
235 None => {
236 self.base.webgl_error(InvalidOperation);
237 return;
238 },
239 };
240 if indexed.size.get() == 0 {
241 if data_size > buffer.capacity() {
242 self.base.webgl_error(InvalidOperation);
243 return;
244 }
245 } else {
246 let start = indexed.start.get() as usize;
247 let mut size = indexed.size.get() as usize;
248 if start >= size {
249 self.base.webgl_error(InvalidOperation);
250 return;
251 }
252 size -= start;
253 if data_size > size {
254 self.base.webgl_error(InvalidOperation);
255 return;
256 }
257 }
258 }
259 }
260 }
261
262 fn validate_vertex_attribs_for_draw(&self) {
263 let program = match self.base.current_program() {
264 Some(program) => program,
265 None => return,
266 };
267 let groups = [
268 [
269 constants::INT,
270 constants::INT_VEC2,
271 constants::INT_VEC3,
272 constants::INT_VEC4,
273 ],
274 [
275 constants::UNSIGNED_INT,
276 constants::UNSIGNED_INT_VEC2,
277 constants::UNSIGNED_INT_VEC3,
278 constants::UNSIGNED_INT_VEC4,
279 ],
280 [
281 constants::FLOAT,
282 constants::FLOAT_VEC2,
283 constants::FLOAT_VEC3,
284 constants::FLOAT_VEC4,
285 ],
286 ];
287 let vao = self.current_vao();
288 for prog_attrib in program.active_attribs().iter() {
289 let attrib = handle_potential_webgl_error!(
290 self.base,
291 vao.get_vertex_attrib(prog_attrib.location.unwrap_or(u32::MAX))
293 .ok_or(InvalidOperation),
294 return
295 );
296
297 let current_vertex_attrib = self.base.current_vertex_attribs()[prog_attrib
299 .location
300 .map(|l| l as usize)
301 .unwrap_or(usize::MAX)];
302 let attrib_data_base_type = if !attrib.enabled_as_array {
303 match current_vertex_attrib {
304 VertexAttrib::Int(_, _, _, _) => constants::INT,
305 VertexAttrib::Uint(_, _, _, _) => constants::UNSIGNED_INT,
306 VertexAttrib::Float(_, _, _, _) => constants::FLOAT,
307 }
308 } else {
309 attrib.type_
310 };
311
312 let contains = groups
313 .iter()
314 .find(|g| g.contains(&attrib_data_base_type) && g.contains(&prog_attrib.type_));
315 if contains.is_none() {
316 self.base.webgl_error(InvalidOperation);
317 return;
318 }
319 }
320 }
321
322 pub(crate) fn base_context(&self) -> DomRoot<WebGLRenderingContext> {
323 DomRoot::from_ref(&*self.base)
324 }
325
326 fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
327 match target {
328 constants::COPY_READ_BUFFER => Ok(self.bound_copy_read_buffer.get()),
329 constants::COPY_WRITE_BUFFER => Ok(self.bound_copy_write_buffer.get()),
330 constants::PIXEL_PACK_BUFFER => Ok(self.bound_pixel_pack_buffer.get()),
331 constants::PIXEL_UNPACK_BUFFER => Ok(self.bound_pixel_unpack_buffer.get()),
332 constants::TRANSFORM_FEEDBACK_BUFFER => Ok(self.bound_transform_feedback_buffer.get()),
333 constants::UNIFORM_BUFFER => Ok(self.bound_uniform_buffer.get()),
334 constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()),
335 _ => self.base.bound_buffer(target),
336 }
337 }
338
339 pub(crate) fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> {
340 match usage {
341 constants::STATIC_READ |
342 constants::DYNAMIC_READ |
343 constants::STREAM_READ |
344 constants::STATIC_COPY |
345 constants::DYNAMIC_COPY |
346 constants::STREAM_COPY => Ok(usage),
347 _ => self.base.buffer_usage(usage),
348 }
349 }
350
351 fn unbind_from(&self, slot: &MutNullableDom<WebGLBuffer>, buffer: &WebGLBuffer) {
352 if slot.get().is_some_and(|b| buffer == &*b) {
353 buffer.decrement_attached_counter(Operation::Infallible);
354 slot.set(None);
355 }
356 }
357
358 fn calc_read_pixel_formats(
359 &self,
360 pixel_type: u32,
361 format: u32,
362 ) -> WebGLResult<ReadPixelsAllowedFormats<'_>> {
363 let array_types = match pixel_type {
364 constants::BYTE => &[Type::Int8][..],
365 constants::SHORT => &[Type::Int16][..],
366 constants::INT => &[Type::Int32][..],
367 constants::UNSIGNED_BYTE => &[Type::Uint8, Type::Uint8Clamped][..],
368 constants::UNSIGNED_SHORT |
369 constants::UNSIGNED_SHORT_4_4_4_4 |
370 constants::UNSIGNED_SHORT_5_5_5_1 |
371 constants::UNSIGNED_SHORT_5_6_5 => &[Type::Uint16][..],
372 constants::UNSIGNED_INT |
373 constants::UNSIGNED_INT_2_10_10_10_REV |
374 constants::UNSIGNED_INT_10F_11F_11F_REV |
375 constants::UNSIGNED_INT_5_9_9_9_REV => &[Type::Uint32][..],
376 constants::FLOAT => &[Type::Float32][..],
377 constants::HALF_FLOAT => &[Type::Uint16][..],
378 _ => return Err(InvalidEnum),
379 };
380 let channels = match format {
381 constants::ALPHA | constants::RED | constants::RED_INTEGER => 1,
382 constants::RG | constants::RG_INTEGER => 2,
383 constants::RGB | constants::RGB_INTEGER => 3,
384 constants::RGBA | constants::RGBA_INTEGER => 4,
385 _ => return Err(InvalidEnum),
386 };
387 Ok(ReadPixelsAllowedFormats {
388 array_types,
389 channels,
390 })
391 }
392
393 fn calc_read_pixel_sizes(
394 &self,
395 width: i32,
396 height: i32,
397 bytes_per_pixel: usize,
398 ) -> WebGLResult<ReadPixelsSizes> {
399 if width < 0 || height < 0 {
400 return Err(InvalidValue);
401 }
402
403 let pixels_per_row = if self.texture_pack_row_length.get() > 0 {
405 self.texture_pack_row_length.get()
406 } else {
407 width as usize
408 };
409 if self.texture_pack_skip_pixels.get() + width as usize > pixels_per_row {
410 return Err(InvalidOperation);
411 }
412
413 let bytes_per_row = pixels_per_row
414 .checked_mul(bytes_per_pixel)
415 .ok_or(InvalidOperation)?;
416 let row_padding_bytes = {
417 let pack_alignment = self.base.get_texture_packing_alignment() as usize;
418 match bytes_per_row % pack_alignment {
419 0 => 0,
420 remainder => pack_alignment - remainder,
421 }
422 };
423 let row_stride = bytes_per_row + row_padding_bytes;
424 let size = if width == 0 || height == 0 {
425 0
426 } else {
427 let full_row_bytes = row_stride
428 .checked_mul(height as usize - 1)
429 .ok_or(InvalidOperation)?;
430 let last_row_bytes = bytes_per_pixel
431 .checked_mul(width as usize)
432 .ok_or(InvalidOperation)?;
433 full_row_bytes
434 .checked_add(last_row_bytes)
435 .ok_or(InvalidOperation)?
436 };
437 let skipped_bytes = {
438 let skipped_row_bytes = self
439 .texture_pack_skip_rows
440 .get()
441 .checked_mul(row_stride)
442 .ok_or(InvalidOperation)?;
443 let skipped_pixel_bytes = self
444 .texture_pack_skip_pixels
445 .get()
446 .checked_mul(bytes_per_pixel)
447 .ok_or(InvalidOperation)?;
448 skipped_row_bytes
449 .checked_add(skipped_pixel_bytes)
450 .ok_or(InvalidOperation)?
451 };
452 Ok(ReadPixelsSizes {
453 row_stride,
454 skipped_bytes,
455 size,
456 })
457 }
458
459 #[expect(unsafe_code)]
460 #[expect(clippy::too_many_arguments)]
461 fn read_pixels_into(
462 &self,
463 x: i32,
464 y: i32,
465 width: i32,
466 height: i32,
467 format: u32,
468 pixel_type: u32,
469 dst: &mut ArrayBufferView,
470 dst_elem_offset: u32,
471 ) {
472 handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
473
474 if self.bound_pixel_pack_buffer.get().is_some() {
475 return self.base.webgl_error(InvalidOperation);
476 }
477
478 let fb_slot = self.base.get_draw_framebuffer_slot();
479 let fb_readbuffer_valid = match fb_slot.get() {
480 Some(fb) => fb.attachment(fb.read_buffer()).is_some(),
481 None => self.default_fb_readbuffer.get() != constants::NONE,
482 };
483 if !fb_readbuffer_valid {
484 return self.base.webgl_error(InvalidOperation);
485 }
486
487 let dst_byte_offset = {
488 let dst_elem_size = dst.get_array_type().byte_size().unwrap();
489 dst_elem_offset as usize * dst_elem_size
490 };
491 if dst_byte_offset > dst.len() {
492 return self.base.webgl_error(InvalidValue);
493 }
494
495 let dst_array_type = dst.get_array_type();
496 let ReadPixelsAllowedFormats {
497 array_types: allowed_array_types,
498 channels,
499 } = match self.calc_read_pixel_formats(pixel_type, format) {
500 Ok(result) => result,
501 Err(error) => return self.base.webgl_error(error),
502 };
503 if !allowed_array_types.contains(&dst_array_type) {
504 return self.base.webgl_error(InvalidOperation);
505 }
506 if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
507 return self.base.webgl_error(InvalidOperation);
508 }
509
510 let bytes_per_pixel = dst_array_type.byte_size().unwrap() * channels;
511 let ReadPixelsSizes {
512 row_stride,
513 skipped_bytes,
514 size,
515 } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
516 Ok(result) => result,
517 Err(error) => return self.base.webgl_error(error),
518 };
519 let dst_end = dst_byte_offset + skipped_bytes + size;
520 let dst_pixels = unsafe { dst.as_mut_slice() };
521 if dst_pixels.len() < dst_end {
522 return self.base.webgl_error(InvalidOperation);
523 }
524
525 let dst_byte_offset = {
526 let margin_left = cmp::max(0, -x) as usize;
527 let margin_top = cmp::max(0, -y) as usize;
528 dst_byte_offset +
529 skipped_bytes +
530 margin_left * bytes_per_pixel +
531 margin_top * row_stride
532 };
533 let src_rect = {
534 let (fb_width, fb_height) = handle_potential_webgl_error!(
535 self.base,
536 self.base
537 .get_current_framebuffer_size()
538 .ok_or(InvalidOperation),
539 return
540 );
541 let src_origin = Point2D::new(x, y);
542 let src_size = Size2D::new(width as u32, height as u32);
543 let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
544 match pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()) {
545 Some(rect) => rect.to_u32(),
546 None => return,
547 }
548 };
549 let src_row_bytes = handle_potential_webgl_error!(
550 self.base,
551 src_rect
552 .size
553 .width
554 .checked_mul(bytes_per_pixel as u32)
555 .ok_or(InvalidOperation),
556 return
557 );
558
559 let (sender, receiver) = generic_channel::channel().unwrap();
560 self.base.send_command(WebGLCommand::ReadPixels(
561 src_rect, format, pixel_type, sender,
562 ));
563 let (src, _) = receiver.recv().unwrap();
564
565 for i in 0..src_rect.size.height as usize {
566 let src_start = i * src_row_bytes as usize;
567 let dst_start = dst_byte_offset + i * row_stride;
568 dst_pixels[dst_start..dst_start + src_row_bytes as usize]
569 .copy_from_slice(&src[src_start..src_start + src_row_bytes as usize]);
570 }
571 }
572
573 fn uniform_vec_section_uint(
574 &self,
575 vec: Uint32ArrayOrUnsignedLongSequence,
576 offset: u32,
577 length: u32,
578 uniform_size: usize,
579 uniform_location: &WebGLUniformLocation,
580 ) -> WebGLResult<Vec<u32>> {
581 let vec = match vec {
582 Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
583 Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
584 };
585 self.base
586 .uniform_vec_section::<u32>(vec, offset, length, uniform_size, uniform_location)
587 }
588
589 fn get_default_fb_attachment_param(
590 &self,
591 attachment: u32,
592 pname: u32,
593 mut retval: MutableHandleValue,
594 ) -> WebGLResult<()> {
595 match attachment {
596 constants::BACK | constants::DEPTH | constants::STENCIL => {},
597 _ => return Err(InvalidEnum),
598 }
599
600 if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
601 retval.set(NullValue());
602 return Ok(());
603 }
604
605 let attrs = self
606 .GetContextAttributes()
607 .unwrap_or_else(WebGLContextAttributes::empty);
608
609 let intval = match pname {
610 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => match attachment {
611 constants::DEPTH if !attrs.depth => constants::NONE as _,
612 constants::STENCIL if !attrs.stencil => constants::NONE as _,
613 _ => constants::FRAMEBUFFER_DEFAULT as _,
614 },
615 constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
616 constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
617 constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE => match attachment {
618 constants::BACK => 8,
619 _ => 0,
620 },
621 constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE => match attachment {
622 constants::BACK if attrs.alpha => 8,
623 constants::BACK => return Err(InvalidOperation),
624 _ => 0,
625 },
626 constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE => match attachment {
627 constants::DEPTH if attrs.depth => 24,
628 constants::DEPTH => return Err(InvalidOperation),
629 _ => 0,
630 },
631 constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE => match attachment {
632 constants::STENCIL if attrs.stencil => 8,
633 constants::STENCIL => return Err(InvalidOperation),
634 _ => 0,
635 },
636 constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE => match attachment {
637 constants::DEPTH if attrs.depth => constants::UNSIGNED_NORMALIZED as _,
638 constants::STENCIL if attrs.stencil => constants::UNSIGNED_INT as _,
639 constants::DEPTH => return Err(InvalidOperation),
640 constants::STENCIL => return Err(InvalidOperation),
641 _ => constants::UNSIGNED_NORMALIZED as _,
642 },
643 constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => match attachment {
644 constants::DEPTH if !attrs.depth => return Err(InvalidOperation),
645 constants::STENCIL if !attrs.stencil => return Err(InvalidOperation),
646 _ => constants::LINEAR as _,
647 },
648 _ => return Err(InvalidEnum),
649 };
650 retval.set(Int32Value(intval));
651 Ok(())
652 }
653
654 fn get_specific_fb_attachment_param(
655 &self,
656 cx: JSContext,
657 fb: &WebGLFramebuffer,
658 target: u32,
659 attachment: u32,
660 pname: u32,
661 mut rval: MutableHandleValue,
662 ) -> WebGLResult<()> {
663 use crate::dom::webgl::webglframebuffer::WebGLFramebufferAttachmentRoot::{
664 Renderbuffer, Texture,
665 };
666
667 match attachment {
668 constants::DEPTH_ATTACHMENT | constants::STENCIL_ATTACHMENT => {},
669 constants::DEPTH_STENCIL_ATTACHMENT => {
670 if pname == constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE {
671 return Err(InvalidOperation);
672 }
673
674 let a = fb.attachment(constants::DEPTH_ATTACHMENT);
675 let b = fb.attachment(constants::STENCIL_ATTACHMENT);
676 match (a, b) {
677 (Some(Renderbuffer(ref a)), Some(Renderbuffer(ref b))) if a.id() == b.id() => {
678 },
679 (Some(Texture(ref a)), Some(Texture(ref b))) if a.id() == b.id() => {},
680 _ => return Err(InvalidOperation),
681 }
682 },
683 constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
684 let last_slot =
685 constants::COLOR_ATTACHMENT0 + self.base.limits().max_color_attachments - 1;
686 if last_slot < attachment {
687 return Err(InvalidEnum);
688 }
689 },
690 _ => return Err(InvalidEnum),
691 }
692
693 let attachment = match attachment {
694 constants::DEPTH_STENCIL_ATTACHMENT => constants::DEPTH_ATTACHMENT,
695 _ => attachment,
696 };
697
698 if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
699 match fb.attachment(attachment) {
700 Some(Renderbuffer(rb)) => {
701 rb.safe_to_jsval(cx, rval, CanGc::note());
702 },
703 Some(Texture(texture)) => {
704 texture.safe_to_jsval(cx, rval, CanGc::note());
705 },
706 _ => rval.set(NullValue()),
707 }
708 return Ok(());
709 }
710
711 match pname {
712 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => {},
713 _ => match fb.attachment(attachment) {
714 Some(webgl_attachment) => match pname {
715 constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
716 constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
717 constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE |
718 constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE |
719 constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE |
720 constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE |
721 constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE |
722 constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => {},
723 _ => match webgl_attachment {
724 WebGLFramebufferAttachmentRoot::Renderbuffer(_) => return Err(InvalidEnum),
725 WebGLFramebufferAttachmentRoot::Texture(_) => match pname {
726 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
727 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
728 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER => {},
729 _ => return Err(InvalidEnum),
730 },
731 },
732 },
733 None => return Err(InvalidOperation),
734 },
735 }
736
737 let (sender, receiver) = webgl_channel().unwrap();
738 self.base
739 .send_command(WebGLCommand::GetFramebufferAttachmentParameter(
740 target, attachment, pname, sender,
741 ));
742
743 let retval = receiver.recv().unwrap();
744 rval.set(Int32Value(retval));
745 Ok(())
746 }
747
748 fn clearbuffer_array_size(&self, buffer: u32, draw_buffer: i32) -> WebGLResult<usize> {
749 match buffer {
750 constants::COLOR => {
751 if draw_buffer < 0 || draw_buffer as u32 >= self.base.limits().max_draw_buffers {
752 return Err(InvalidValue);
753 }
754 Ok(4)
755 },
756 constants::DEPTH | constants::STENCIL | constants::DEPTH_STENCIL => {
757 if draw_buffer != 0 {
758 return Err(InvalidValue);
759 }
760 Ok(1)
761 },
762 _ => unreachable!(),
763 }
764 }
765
766 fn clear_buffer<T: Clone>(
767 &self,
768 buffer: u32,
769 draw_buffer: i32,
770 valid_buffers: &[u32],
771 src_offset: u32,
772 array: Vec<T>,
773 msg: fn(u32, i32, Vec<T>) -> WebGLCommand,
774 ) {
775 if !valid_buffers.contains(&buffer) {
776 return self.base.webgl_error(InvalidEnum);
777 }
778
779 let array_size = handle_potential_webgl_error!(
780 self.base,
781 self.clearbuffer_array_size(buffer, draw_buffer),
782 return
783 );
784 let src_offset = src_offset as usize;
785
786 if array.len() < src_offset + array_size {
787 return self.base.webgl_error(InvalidValue);
788 }
789 let array = array[src_offset..src_offset + array_size].to_vec();
790
791 self.base.send_command(msg(buffer, draw_buffer, array));
792 }
793
794 fn valid_fb_attachment_values(&self, target: u32, attachments: &[u32]) -> bool {
795 let fb_slot = match target {
796 constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
797 self.base.get_draw_framebuffer_slot()
798 },
799 constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
800 _ => {
801 self.base.webgl_error(InvalidEnum);
802 return false;
803 },
804 };
805
806 if let Some(fb) = fb_slot.get() {
807 if fb.check_status() != constants::FRAMEBUFFER_COMPLETE {
808 return false;
809 }
810
811 for &attachment in attachments {
812 match attachment {
813 constants::DEPTH_ATTACHMENT |
814 constants::STENCIL_ATTACHMENT |
815 constants::DEPTH_STENCIL_ATTACHMENT => {},
816 constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
817 let last_slot = constants::COLOR_ATTACHMENT0 +
818 self.base.limits().max_color_attachments -
819 1;
820 if last_slot < attachment {
821 return false;
822 }
823 },
824 _ => return false,
825 }
826 }
827 } else {
828 for &attachment in attachments {
829 match attachment {
830 constants::COLOR | constants::DEPTH | constants::STENCIL => {},
831 _ => return false,
832 }
833 }
834 }
835
836 true
837 }
838
839 fn vertex_attrib_i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) {
840 if index >= self.base.limits().max_vertex_attribs {
841 return self.base.webgl_error(InvalidValue);
842 }
843 self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Int(x, y, z, w);
844 self.current_vao()
845 .set_vertex_attrib_type(index, constants::INT);
846 self.base
847 .send_command(WebGLCommand::VertexAttribI(index, x, y, z, w));
848 }
849
850 fn vertex_attrib_u(&self, index: u32, x: u32, y: u32, z: u32, w: u32) {
851 if index >= self.base.limits().max_vertex_attribs {
852 return self.base.webgl_error(InvalidValue);
853 }
854 self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Uint(x, y, z, w);
855 self.current_vao()
856 .set_vertex_attrib_type(index, constants::UNSIGNED_INT);
857 self.base
858 .send_command(WebGLCommand::VertexAttribU(index, x, y, z, w));
859 }
860
861 #[expect(clippy::too_many_arguments)]
862 fn tex_storage(
863 &self,
864 dimensions: u8,
865 target: u32,
866 levels: i32,
867 internal_format: u32,
868 width: i32,
869 height: i32,
870 depth: i32,
871 ) {
872 let expected_dimensions = match target {
873 constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP => 2,
874 constants::TEXTURE_3D | constants::TEXTURE_2D_ARRAY => 3,
875 _ => return self.base.webgl_error(InvalidEnum),
876 };
877 if dimensions != expected_dimensions {
878 return self.base.webgl_error(InvalidEnum);
879 }
880
881 let validator = TexStorageValidator::new(
882 &self.base,
883 dimensions,
884 target,
885 levels,
886 internal_format,
887 width,
888 height,
889 depth,
890 );
891 let TexStorageValidatorResult {
892 texture,
893 target,
894 levels,
895 internal_format,
896 width,
897 height,
898 depth,
899 } = match validator.validate() {
900 Ok(result) => result,
901 Err(_) => return, };
903
904 handle_potential_webgl_error!(
905 self.base,
906 texture.storage(target, levels, internal_format, width, height, depth)
907 );
908 }
909
910 #[expect(clippy::too_many_arguments)]
911 fn tex_image_3d(
912 &self,
913 texture: &WebGLTexture,
914 target: TexImageTarget,
915 data_type: TexDataType,
916 internal_format: TexFormat,
917 format: TexFormat,
918 level: u32,
919 width: u32,
920 height: u32,
921 depth: u32,
922 _border: u32,
923 unpacking_alignment: u32,
924 data: TexPixels,
925 ) {
926 handle_potential_webgl_error!(
927 self.base,
928 texture.initialize(
929 target,
930 width,
931 height,
932 depth,
933 internal_format,
934 level,
935 Some(data_type)
936 )
937 );
938
939 let internal_format = self
940 .base
941 .extension_manager()
942 .get_effective_tex_internal_format(internal_format, data_type.as_gl_constant());
943 let effective_data_type = self
944 .base
945 .extension_manager()
946 .effective_type(data_type.as_gl_constant());
947
948 self.base.send_command(WebGLCommand::TexImage3D {
949 target: target.as_gl_constant(),
950 level,
951 internal_format,
952 size: data.size(),
953 depth,
954 format,
955 data_type,
956 effective_data_type,
957 unpacking_alignment,
958 alpha_treatment: data.alpha_treatment(),
959 y_axis_treatment: data.y_axis_treatment(),
960 pixel_format: data.pixel_format(),
961 data: data.into_shared_memory().into(),
962 });
963 if let Some(fb) = self.base.bound_draw_framebuffer() {
966 fb.invalidate_texture(texture);
967 }
968 }
969}
970
971impl CanvasContext for WebGL2RenderingContext {
972 type ID = WebGLContextId;
973
974 fn context_id(&self) -> Self::ID {
975 self.base.context_id()
976 }
977
978 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
979 self.base.canvas()
980 }
981
982 fn resize(&self) {
983 self.base.resize();
984 }
985
986 fn reset_bitmap(&self) {
987 self.base.reset_bitmap();
988 }
989
990 fn get_image_data(&self) -> Option<Snapshot> {
991 self.base.get_image_data()
992 }
993
994 fn mark_as_dirty(&self) {
995 self.base.mark_as_dirty()
996 }
997}
998
999impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingContext {
1000 fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
1002 self.base.Canvas()
1003 }
1004
1005 fn Flush(&self) {
1007 self.base.Flush()
1008 }
1009
1010 fn Finish(&self) {
1012 self.base.Finish()
1013 }
1014
1015 fn DrawingBufferWidth(&self) -> i32 {
1017 self.base.DrawingBufferWidth()
1018 }
1019
1020 fn DrawingBufferHeight(&self) -> i32 {
1022 self.base.DrawingBufferHeight()
1023 }
1024
1025 fn GetBufferParameter(
1027 &self,
1028 _cx: JSContext,
1029 target: u32,
1030 parameter: u32,
1031 mut retval: MutableHandleValue,
1032 ) {
1033 let buffer = handle_potential_webgl_error!(
1034 self.base,
1035 self.bound_buffer(target),
1036 return retval.set(NullValue())
1037 );
1038 self.base.get_buffer_param(buffer, parameter, retval)
1039 }
1040
1041 fn GetParameter(&self, cx: JSContext, parameter: u32, mut rval: MutableHandleValue) {
1043 match parameter {
1044 constants::VERSION => {
1045 "WebGL 2.0".safe_to_jsval(cx, rval, CanGc::note());
1046 return;
1047 },
1048 constants::SHADING_LANGUAGE_VERSION => {
1049 "WebGL GLSL ES 3.00".safe_to_jsval(cx, rval, CanGc::note());
1050 return;
1051 },
1052 constants::MAX_CLIENT_WAIT_TIMEOUT_WEBGL => {
1053 rval.set(DoubleValue(
1054 self.base.limits().max_client_wait_timeout_webgl.as_nanos() as f64,
1055 ));
1056 return;
1057 },
1058 constants::MAX_SERVER_WAIT_TIMEOUT => {
1059 rval.set(DoubleValue(
1060 self.base.limits().max_server_wait_timeout.as_nanos() as f64,
1061 ));
1062 return;
1063 },
1064 constants::SAMPLER_BINDING => {
1065 let idx = (self.base.textures().active_unit_enum() - constants::TEXTURE0) as usize;
1066 assert!(idx < self.samplers.len());
1067 let sampler = self.samplers[idx].get();
1068 sampler.safe_to_jsval(cx, rval, CanGc::note());
1069 return;
1070 },
1071 constants::COPY_READ_BUFFER_BINDING => {
1072 self.bound_copy_read_buffer
1073 .get()
1074 .safe_to_jsval(cx, rval, CanGc::note());
1075 return;
1076 },
1077 constants::COPY_WRITE_BUFFER_BINDING => {
1078 self.bound_copy_write_buffer
1079 .get()
1080 .safe_to_jsval(cx, rval, CanGc::note());
1081 return;
1082 },
1083 constants::PIXEL_PACK_BUFFER_BINDING => {
1084 self.bound_pixel_pack_buffer
1085 .get()
1086 .safe_to_jsval(cx, rval, CanGc::note());
1087 return;
1088 },
1089 constants::PIXEL_UNPACK_BUFFER_BINDING => {
1090 self.bound_pixel_unpack_buffer
1091 .get()
1092 .safe_to_jsval(cx, rval, CanGc::note());
1093 return;
1094 },
1095 constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => {
1096 self.bound_transform_feedback_buffer
1097 .get()
1098 .safe_to_jsval(cx, rval, CanGc::note());
1099 return;
1100 },
1101 constants::UNIFORM_BUFFER_BINDING => {
1102 self.bound_uniform_buffer
1103 .get()
1104 .safe_to_jsval(cx, rval, CanGc::note());
1105 return;
1106 },
1107 constants::TRANSFORM_FEEDBACK_BINDING => {
1108 self.current_transform_feedback
1109 .get()
1110 .safe_to_jsval(cx, rval, CanGc::note());
1111 return;
1112 },
1113 constants::ELEMENT_ARRAY_BUFFER_BINDING => {
1114 let buffer = self.current_vao().element_array_buffer().get();
1115 buffer.safe_to_jsval(cx, rval, CanGc::note());
1116 return;
1117 },
1118 constants::VERTEX_ARRAY_BINDING => {
1119 let vao = self.current_vao();
1120 let vao = vao.id().map(|_| &*vao);
1121 vao.safe_to_jsval(cx, rval, CanGc::note());
1122 return;
1123 },
1124 constants::READ_FRAMEBUFFER_BINDING => {
1126 self.base
1127 .get_read_framebuffer_slot()
1128 .get()
1129 .safe_to_jsval(cx, rval, CanGc::note());
1130 return;
1131 },
1132 constants::READ_BUFFER => {
1133 let buffer = match self.base.get_read_framebuffer_slot().get() {
1134 Some(fb) => fb.read_buffer(),
1135 None => self.default_fb_readbuffer.get(),
1136 };
1137 rval.set(UInt32Value(buffer));
1138 return;
1139 },
1140 constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => {
1141 let buffer = match self.base.get_read_framebuffer_slot().get() {
1142 Some(fb) => {
1143 let idx = parameter - constants::DRAW_BUFFER0;
1144 fb.draw_buffer_i(idx as usize)
1145 },
1146 None if parameter == constants::DRAW_BUFFER0 => {
1147 self.default_fb_readbuffer.get()
1148 },
1149 None => constants::NONE,
1150 };
1151 rval.set(UInt32Value(buffer));
1152 return;
1153 },
1154 constants::MAX_TEXTURE_LOD_BIAS => {
1155 rval.set(DoubleValue(self.base.limits().max_texture_lod_bias as f64));
1156 return;
1157 },
1158 constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => {
1159 rval.set(DoubleValue(
1160 self.base.limits().max_combined_fragment_uniform_components as f64,
1161 ));
1162 return;
1163 },
1164 constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => {
1165 rval.set(DoubleValue(
1166 self.base.limits().max_combined_vertex_uniform_components as f64,
1167 ));
1168 return;
1169 },
1170 constants::MAX_ELEMENT_INDEX => {
1171 rval.set(DoubleValue(self.base.limits().max_element_index as f64));
1172 return;
1173 },
1174 constants::MAX_UNIFORM_BLOCK_SIZE => {
1175 rval.set(DoubleValue(
1176 self.base.limits().max_uniform_block_size as f64,
1177 ));
1178 return;
1179 },
1180 constants::MIN_PROGRAM_TEXEL_OFFSET => {
1181 rval.set(Int32Value(self.base.limits().min_program_texel_offset));
1182 return;
1183 },
1184 _ => {},
1185 }
1186
1187 let limit = match parameter {
1188 constants::MAX_3D_TEXTURE_SIZE => Some(self.base.limits().max_3d_texture_size),
1189 constants::MAX_ARRAY_TEXTURE_LAYERS => {
1190 Some(self.base.limits().max_array_texture_layers)
1191 },
1192 constants::MAX_COLOR_ATTACHMENTS => Some(self.base.limits().max_color_attachments),
1193 constants::MAX_COMBINED_UNIFORM_BLOCKS => {
1194 Some(self.base.limits().max_combined_uniform_blocks)
1195 },
1196 constants::MAX_DRAW_BUFFERS => Some(self.base.limits().max_draw_buffers),
1197 constants::MAX_ELEMENTS_INDICES => Some(self.base.limits().max_elements_indices),
1198 constants::MAX_ELEMENTS_VERTICES => Some(self.base.limits().max_elements_vertices),
1199 constants::MAX_FRAGMENT_INPUT_COMPONENTS => {
1200 Some(self.base.limits().max_fragment_input_components)
1201 },
1202 constants::MAX_FRAGMENT_UNIFORM_BLOCKS => {
1203 Some(self.base.limits().max_fragment_uniform_blocks)
1204 },
1205 constants::MAX_FRAGMENT_UNIFORM_COMPONENTS => {
1206 Some(self.base.limits().max_fragment_uniform_components)
1207 },
1208 constants::MAX_PROGRAM_TEXEL_OFFSET => {
1209 Some(self.base.limits().max_program_texel_offset)
1210 },
1211 constants::MAX_SAMPLES => Some(self.base.limits().max_samples),
1212 constants::MAX_UNIFORM_BUFFER_BINDINGS => {
1213 Some(self.base.limits().max_uniform_buffer_bindings)
1214 },
1215 constants::MAX_VARYING_COMPONENTS => Some(self.base.limits().max_varying_components),
1216 constants::MAX_VERTEX_OUTPUT_COMPONENTS => {
1217 Some(self.base.limits().max_vertex_output_components)
1218 },
1219 constants::MAX_VERTEX_UNIFORM_BLOCKS => {
1220 Some(self.base.limits().max_vertex_uniform_blocks)
1221 },
1222 constants::MAX_VERTEX_UNIFORM_COMPONENTS => {
1223 Some(self.base.limits().max_vertex_uniform_components)
1224 },
1225 constants::UNIFORM_BUFFER_OFFSET_ALIGNMENT => {
1226 Some(self.base.limits().uniform_buffer_offset_alignment)
1227 },
1228 _ => None,
1229 };
1230 if let Some(limit) = limit {
1231 rval.set(UInt32Value(limit));
1232 return;
1233 }
1234
1235 self.base.GetParameter(cx, parameter, rval)
1236 }
1237
1238 fn GetTexParameter(&self, cx: JSContext, target: u32, pname: u32, retval: MutableHandleValue) {
1240 self.base.GetTexParameter(cx, target, pname, retval)
1241 }
1242
1243 fn GetError(&self) -> u32 {
1245 self.base.GetError()
1246 }
1247
1248 fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
1250 self.base.GetContextAttributes()
1251 }
1252
1253 fn IsContextLost(&self) -> bool {
1255 self.base.IsContextLost()
1256 }
1257
1258 fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
1260 self.base.GetSupportedExtensions()
1261 }
1262
1263 fn GetExtension(&self, cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> {
1265 self.base.GetExtension(cx, name)
1266 }
1267
1268 fn GetFramebufferAttachmentParameter(
1270 &self,
1271 cx: JSContext,
1272 target: u32,
1273 attachment: u32,
1274 pname: u32,
1275 mut rval: MutableHandleValue,
1276 ) {
1277 let fb_slot = match target {
1278 constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
1279 self.base.get_draw_framebuffer_slot()
1280 },
1281 constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
1282 _ => {
1283 self.base.webgl_error(InvalidEnum);
1284 rval.set(NullValue());
1285 return;
1286 },
1287 };
1288
1289 if let Some(fb) = fb_slot.get() {
1290 handle_potential_webgl_error!(
1292 self.base,
1293 fb.validate_transparent(),
1294 return rval.set(NullValue())
1295 );
1296 handle_potential_webgl_error!(
1297 self.base,
1298 self.get_specific_fb_attachment_param(
1299 cx,
1300 &fb,
1301 target,
1302 attachment,
1303 pname,
1304 rval.reborrow()
1305 ),
1306 rval.set(NullValue())
1307 )
1308 } else {
1309 handle_potential_webgl_error!(
1311 self.base,
1312 self.get_default_fb_attachment_param(attachment, pname, rval.reborrow()),
1313 rval.set(NullValue())
1314 )
1315 }
1316 }
1317
1318 fn GetRenderbufferParameter(
1320 &self,
1321 cx: JSContext,
1322 target: u32,
1323 pname: u32,
1324 retval: MutableHandleValue,
1325 ) {
1326 self.base
1327 .GetRenderbufferParameter(cx, target, pname, retval)
1328 }
1329
1330 fn ActiveTexture(&self, texture: u32) {
1332 self.base.ActiveTexture(texture)
1333 }
1334
1335 fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
1337 self.base.BlendColor(r, g, b, a)
1338 }
1339
1340 fn BlendEquation(&self, mode: u32) {
1342 self.base.BlendEquation(mode)
1343 }
1344
1345 fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
1347 self.base.BlendEquationSeparate(mode_rgb, mode_alpha)
1348 }
1349
1350 fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
1352 self.base.BlendFunc(src_factor, dest_factor)
1353 }
1354
1355 fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
1357 self.base
1358 .BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha)
1359 }
1360
1361 fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
1363 self.base.AttachShader(program, shader)
1364 }
1365
1366 fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
1368 self.base.DetachShader(program, shader)
1369 }
1370
1371 fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
1373 self.base.BindAttribLocation(program, index, name)
1374 }
1375
1376 fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
1378 let current_vao;
1379 let slot = match target {
1380 constants::COPY_READ_BUFFER => &self.bound_copy_read_buffer,
1381 constants::COPY_WRITE_BUFFER => &self.bound_copy_write_buffer,
1382 constants::PIXEL_PACK_BUFFER => &self.bound_pixel_pack_buffer,
1383 constants::PIXEL_UNPACK_BUFFER => &self.bound_pixel_unpack_buffer,
1384 constants::TRANSFORM_FEEDBACK_BUFFER => &self.bound_transform_feedback_buffer,
1385 constants::UNIFORM_BUFFER => &self.bound_uniform_buffer,
1386 constants::ELEMENT_ARRAY_BUFFER => {
1387 current_vao = self.current_vao();
1388 current_vao.element_array_buffer()
1389 },
1390 _ => return self.base.BindBuffer(target, buffer),
1391 };
1392 self.base.bind_buffer_maybe(slot, target, buffer);
1393 }
1394
1395 fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
1397 handle_potential_webgl_error!(
1398 self.base,
1399 self.base.validate_new_framebuffer_binding(framebuffer),
1400 return
1401 );
1402
1403 let (bind_read, bind_draw) = match target {
1404 constants::FRAMEBUFFER => (true, true),
1405 constants::READ_FRAMEBUFFER => (true, false),
1406 constants::DRAW_FRAMEBUFFER => (false, true),
1407 _ => return self.base.webgl_error(InvalidEnum),
1408 };
1409 if bind_read {
1410 self.base.bind_framebuffer_to(
1411 target,
1412 framebuffer,
1413 self.base.get_read_framebuffer_slot(),
1414 );
1415 }
1416 if bind_draw {
1417 self.base.bind_framebuffer_to(
1418 target,
1419 framebuffer,
1420 self.base.get_draw_framebuffer_slot(),
1421 );
1422 }
1423 }
1424
1425 fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
1427 self.base.BindRenderbuffer(target, renderbuffer)
1428 }
1429
1430 fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
1432 self.base.BindTexture(target, texture)
1433 }
1434
1435 fn GenerateMipmap(&self, target: u32) {
1437 self.base.GenerateMipmap(target)
1438 }
1439
1440 fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) {
1442 let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1443 let bound_buffer =
1444 handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1445 self.base.buffer_data(target, data, usage, bound_buffer)
1446 }
1447
1448 fn BufferData(&self, target: u32, size: i64, usage: u32) {
1450 let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1451 let bound_buffer =
1452 handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1453 self.base.buffer_data_(target, size, usage, bound_buffer)
1454 }
1455
1456 #[expect(unsafe_code)]
1458 fn BufferData__(
1459 &self,
1460 target: u32,
1461 data: CustomAutoRooterGuard<ArrayBufferView>,
1462 usage: u32,
1463 elem_offset: u32,
1464 length: u32,
1465 ) {
1466 let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1467 let bound_buffer =
1468 handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1469 let bound_buffer =
1470 handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1471
1472 let elem_size = data.get_array_type().byte_size().unwrap();
1473 let elem_count = data.len() / elem_size;
1474 let elem_offset = elem_offset as usize;
1475 let byte_offset = elem_offset * elem_size;
1476
1477 if byte_offset > data.len() {
1478 return self.base.webgl_error(InvalidValue);
1479 }
1480
1481 let copy_count = if length == 0 {
1482 elem_count - elem_offset
1483 } else {
1484 length as usize
1485 };
1486 if copy_count == 0 {
1487 return;
1488 }
1489 let copy_bytes = copy_count * elem_size;
1490
1491 if byte_offset + copy_bytes > data.len() {
1492 return self.base.webgl_error(InvalidValue);
1493 }
1494
1495 let data_end = byte_offset + copy_bytes;
1496 let data: &[u8] = unsafe { &data.as_slice()[byte_offset..data_end] };
1497 handle_potential_webgl_error!(self.base, bound_buffer.buffer_data(target, data, usage));
1498 }
1499
1500 fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) {
1502 let bound_buffer =
1503 handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1504 self.base
1505 .buffer_sub_data(target, offset, data, bound_buffer)
1506 }
1507
1508 #[expect(unsafe_code)]
1510 fn BufferSubData_(
1511 &self,
1512 target: u32,
1513 dst_byte_offset: i64,
1514 src_data: CustomAutoRooterGuard<ArrayBufferView>,
1515 src_elem_offset: u32,
1516 length: u32,
1517 ) {
1518 let bound_buffer =
1519 handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1520 let bound_buffer =
1521 handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1522
1523 let src_elem_size = src_data.get_array_type().byte_size().unwrap();
1524 let src_elem_count = src_data.len() / src_elem_size;
1525 let src_elem_offset = src_elem_offset as usize;
1526 let src_byte_offset = src_elem_offset * src_elem_size;
1527
1528 if dst_byte_offset < 0 || src_byte_offset > src_data.len() {
1529 return self.base.webgl_error(InvalidValue);
1530 }
1531
1532 let copy_count = if length == 0 {
1533 src_elem_count - src_elem_offset
1534 } else {
1535 length as usize
1536 };
1537 if copy_count == 0 {
1538 return;
1539 }
1540 let copy_bytes = copy_count * src_elem_size;
1541
1542 let dst_byte_offset = dst_byte_offset as usize;
1543 if dst_byte_offset + copy_bytes > bound_buffer.capacity() ||
1544 src_byte_offset + copy_bytes > src_data.len()
1545 {
1546 return self.base.webgl_error(InvalidValue);
1547 }
1548
1549 let (sender, receiver) = generic_channel::channel().unwrap();
1550 self.base.send_command(WebGLCommand::BufferSubData(
1551 target,
1552 dst_byte_offset as isize,
1553 receiver,
1554 ));
1555 let src_end = src_byte_offset + copy_bytes;
1556 let data: &[u8] = unsafe { &src_data.as_slice()[src_byte_offset..src_end] };
1557 let buffer = GenericSharedMemory::from_bytes(data);
1558 sender.send(buffer).unwrap();
1559 }
1560
1561 fn CopyBufferSubData(
1563 &self,
1564 read_target: u32,
1565 write_target: u32,
1566 read_offset: i64,
1567 write_offset: i64,
1568 size: i64,
1569 ) {
1570 if read_offset < 0 || write_offset < 0 || size < 0 {
1571 return self.base.webgl_error(InvalidValue);
1572 }
1573
1574 let read_buffer =
1575 handle_potential_webgl_error!(self.base, self.bound_buffer(read_target), return);
1576 let read_buffer =
1577 handle_potential_webgl_error!(self.base, read_buffer.ok_or(InvalidOperation), return);
1578
1579 let write_buffer =
1580 handle_potential_webgl_error!(self.base, self.bound_buffer(write_target), return);
1581 let write_buffer =
1582 handle_potential_webgl_error!(self.base, write_buffer.ok_or(InvalidOperation), return);
1583
1584 let read_until = read_offset + size;
1585 let write_until = write_offset + size;
1586 if read_until as usize > read_buffer.capacity() ||
1587 write_until as usize > write_buffer.capacity()
1588 {
1589 return self.base.webgl_error(InvalidValue);
1590 }
1591
1592 if read_target == write_target {
1593 let is_separate = read_until <= write_offset || write_until <= read_offset;
1594 if !is_separate {
1595 return self.base.webgl_error(InvalidValue);
1596 }
1597 }
1598 let src_is_elemarray = read_buffer
1599 .target()
1600 .is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
1601 let dst_is_elemarray = write_buffer
1602 .target()
1603 .is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
1604 if src_is_elemarray != dst_is_elemarray {
1605 return self.base.webgl_error(InvalidOperation);
1606 }
1607
1608 self.base.send_command(WebGLCommand::CopyBufferSubData(
1609 read_target,
1610 write_target,
1611 read_offset,
1612 write_offset,
1613 size,
1614 ));
1615 }
1616
1617 #[expect(unsafe_code)]
1619 fn GetBufferSubData(
1620 &self,
1621 target: u32,
1622 src_byte_offset: i64,
1623 mut dst_buffer: CustomAutoRooterGuard<ArrayBufferView>,
1624 dst_elem_offset: u32,
1625 length: u32,
1626 ) {
1627 let bound_buffer =
1628 handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1629 let bound_buffer =
1630 handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1631
1632 let dst_elem_size = dst_buffer.get_array_type().byte_size().unwrap();
1633 let dst_elem_count = dst_buffer.len() / dst_elem_size;
1634 let dst_elem_offset = dst_elem_offset as usize;
1635 let dst_byte_offset = dst_elem_offset * dst_elem_size;
1636
1637 if src_byte_offset < 0 || dst_byte_offset > dst_buffer.len() {
1638 return self.base.webgl_error(InvalidValue);
1639 }
1640
1641 let copy_count = if length == 0 {
1642 dst_elem_count - dst_elem_offset
1643 } else {
1644 length as usize
1645 };
1646 if copy_count == 0 {
1647 return;
1648 }
1649 let copy_bytes = copy_count * dst_elem_size;
1650
1651 let src_byte_offset = src_byte_offset as usize;
1654 if src_byte_offset + copy_bytes > bound_buffer.capacity() ||
1655 dst_byte_offset + copy_bytes > dst_buffer.len()
1656 {
1657 return self.base.webgl_error(InvalidValue);
1658 }
1659
1660 let (sender, receiver) = generic_channel::channel().unwrap();
1661 self.base.send_command(WebGLCommand::GetBufferSubData(
1662 target,
1663 src_byte_offset,
1664 copy_bytes,
1665 sender,
1666 ));
1667 let data = receiver.recv().unwrap();
1668 let dst_end = dst_byte_offset + copy_bytes;
1669 unsafe {
1670 dst_buffer.as_mut_slice()[dst_byte_offset..dst_end].copy_from_slice(&data);
1671 }
1672 }
1673
1674 #[expect(unsafe_code)]
1676 fn CompressedTexImage2D(
1677 &self,
1678 target: u32,
1679 level: i32,
1680 internal_format: u32,
1681 width: i32,
1682 height: i32,
1683 border: i32,
1684 pixels: CustomAutoRooterGuard<ArrayBufferView>,
1685 src_offset: u32,
1686 src_length_override: u32,
1687 ) {
1688 let mut data = unsafe { pixels.as_slice() };
1689 let start = src_offset as usize;
1690 let end = (src_offset + src_length_override) as usize;
1691 if start > data.len() || end > data.len() {
1692 self.base.webgl_error(InvalidValue);
1693 return;
1694 }
1695 if src_length_override != 0 {
1696 data = &data[start..end];
1697 }
1698 self.base.compressed_tex_image_2d(
1699 target,
1700 level,
1701 internal_format,
1702 width,
1703 height,
1704 border,
1705 data,
1706 )
1707 }
1708
1709 #[expect(unsafe_code)]
1711 fn CompressedTexSubImage2D(
1712 &self,
1713 target: u32,
1714 level: i32,
1715 xoffset: i32,
1716 yoffset: i32,
1717 width: i32,
1718 height: i32,
1719 format: u32,
1720 pixels: CustomAutoRooterGuard<ArrayBufferView>,
1721 src_offset: u32,
1722 src_length_override: u32,
1723 ) {
1724 let mut data = unsafe { pixels.as_slice() };
1725 let start = src_offset as usize;
1726 let end = (src_offset + src_length_override) as usize;
1727 if start > data.len() || end > data.len() {
1728 self.base.webgl_error(InvalidValue);
1729 return;
1730 }
1731 if src_length_override != 0 {
1732 data = &data[start..end];
1733 }
1734 self.base.compressed_tex_sub_image_2d(
1735 target, level, xoffset, yoffset, width, height, format, data,
1736 )
1737 }
1738
1739 fn CopyTexImage2D(
1741 &self,
1742 target: u32,
1743 level: i32,
1744 internal_format: u32,
1745 x: i32,
1746 y: i32,
1747 width: i32,
1748 height: i32,
1749 border: i32,
1750 ) {
1751 self.base
1752 .CopyTexImage2D(target, level, internal_format, x, y, width, height, border)
1753 }
1754
1755 fn CopyTexSubImage2D(
1757 &self,
1758 target: u32,
1759 level: i32,
1760 xoffset: i32,
1761 yoffset: i32,
1762 x: i32,
1763 y: i32,
1764 width: i32,
1765 height: i32,
1766 ) {
1767 self.base
1768 .CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height)
1769 }
1770
1771 fn Clear(&self, mask: u32) {
1773 self.base.Clear(mask)
1774 }
1775
1776 fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
1778 self.base.ClearColor(red, green, blue, alpha)
1779 }
1780
1781 fn ClearDepth(&self, depth: f32) {
1783 self.base.ClearDepth(depth)
1784 }
1785
1786 fn ClearStencil(&self, stencil: i32) {
1788 self.base.ClearStencil(stencil)
1789 }
1790
1791 fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
1793 self.base.ColorMask(r, g, b, a)
1794 }
1795
1796 fn CullFace(&self, mode: u32) {
1798 self.base.CullFace(mode)
1799 }
1800
1801 fn FrontFace(&self, mode: u32) {
1803 self.base.FrontFace(mode)
1804 }
1805 fn DepthFunc(&self, func: u32) {
1807 self.base.DepthFunc(func)
1808 }
1809
1810 fn DepthMask(&self, flag: bool) {
1812 self.base.DepthMask(flag)
1813 }
1814
1815 fn DepthRange(&self, near: f32, far: f32) {
1817 self.base.DepthRange(near, far)
1818 }
1819
1820 fn Enable(&self, cap: u32) {
1822 match cap {
1823 constants::RASTERIZER_DISCARD => {
1824 self.enable_rasterizer_discard.set(true);
1825 self.base.send_command(WebGLCommand::Enable(cap));
1826 },
1827 _ => self.base.Enable(cap),
1828 }
1829 }
1830
1831 fn Disable(&self, cap: u32) {
1833 match cap {
1834 constants::RASTERIZER_DISCARD => {
1835 self.enable_rasterizer_discard.set(false);
1836 self.base.send_command(WebGLCommand::Disable(cap));
1837 },
1838 _ => self.base.Disable(cap),
1839 }
1840 }
1841
1842 fn CompileShader(&self, shader: &WebGLShader) {
1844 self.base.CompileShader(shader)
1845 }
1846
1847 fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> {
1849 self.base.CreateBuffer()
1850 }
1851
1852 fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
1854 self.base.CreateFramebuffer()
1855 }
1856
1857 fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
1859 self.base.CreateRenderbuffer()
1860 }
1861
1862 fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> {
1864 self.base.CreateTexture()
1865 }
1866
1867 fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> {
1869 self.base.CreateProgram()
1870 }
1871
1872 fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
1874 self.base.CreateShader(shader_type)
1875 }
1876
1877 fn CreateVertexArray(&self) -> Option<DomRoot<WebGLVertexArrayObject>> {
1879 self.base.create_vertex_array_webgl2()
1880 }
1881
1882 fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
1884 let buffer = match buffer {
1885 Some(buffer) => buffer,
1886 None => return,
1887 };
1888 handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
1889 if buffer.is_marked_for_deletion() {
1890 return;
1891 }
1892 self.current_vao().unbind_buffer(buffer);
1893 self.unbind_from(self.base.array_buffer_slot(), buffer);
1894 self.unbind_from(&self.bound_copy_read_buffer, buffer);
1895 self.unbind_from(&self.bound_copy_write_buffer, buffer);
1896 self.unbind_from(&self.bound_pixel_pack_buffer, buffer);
1897 self.unbind_from(&self.bound_pixel_unpack_buffer, buffer);
1898 self.unbind_from(&self.bound_transform_feedback_buffer, buffer);
1899 self.unbind_from(&self.bound_uniform_buffer, buffer);
1900
1901 for binding in self.indexed_uniform_buffer_bindings.iter() {
1902 self.unbind_from(&binding.buffer, buffer);
1903 }
1904 for binding in self.indexed_transform_feedback_buffer_bindings.iter() {
1905 self.unbind_from(&binding.buffer, buffer);
1906 }
1907
1908 buffer.mark_for_deletion(Operation::Infallible);
1909 }
1910
1911 fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
1913 self.base.DeleteFramebuffer(framebuffer)
1914 }
1915
1916 fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
1918 self.base.DeleteRenderbuffer(renderbuffer)
1919 }
1920
1921 fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
1923 self.base.DeleteTexture(texture)
1924 }
1925
1926 fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
1928 self.base.DeleteProgram(program)
1929 }
1930
1931 fn DeleteShader(&self, shader: Option<&WebGLShader>) {
1933 self.base.DeleteShader(shader)
1934 }
1935
1936 fn DeleteVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) {
1938 self.base.delete_vertex_array_webgl2(vertex_array);
1939 }
1940
1941 fn DrawArrays(&self, mode: u32, first: i32, count: i32) {
1943 self.validate_uniform_block_for_draw();
1944 self.validate_vertex_attribs_for_draw();
1945 self.base.DrawArrays(mode, first, count)
1946 }
1947
1948 fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
1950 self.validate_uniform_block_for_draw();
1951 self.validate_vertex_attribs_for_draw();
1952 self.base.DrawElements(mode, count, type_, offset)
1953 }
1954
1955 fn EnableVertexAttribArray(&self, attrib_id: u32) {
1957 self.base.EnableVertexAttribArray(attrib_id)
1958 }
1959
1960 fn DisableVertexAttribArray(&self, attrib_id: u32) {
1962 self.base.DisableVertexAttribArray(attrib_id)
1963 }
1964
1965 fn GetActiveUniform(
1967 &self,
1968 program: &WebGLProgram,
1969 index: u32,
1970 ) -> Option<DomRoot<WebGLActiveInfo>> {
1971 self.base.GetActiveUniform(program, index)
1972 }
1973
1974 fn GetActiveAttrib(
1976 &self,
1977 program: &WebGLProgram,
1978 index: u32,
1979 ) -> Option<DomRoot<WebGLActiveInfo>> {
1980 self.base.GetActiveAttrib(program, index)
1981 }
1982
1983 fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
1985 self.base.GetAttribLocation(program, name)
1986 }
1987
1988 fn GetFragDataLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
1990 handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return -1);
1991 handle_potential_webgl_error!(self.base, program.get_frag_data_location(name), -1)
1992 }
1993
1994 fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
1996 self.base.GetProgramInfoLog(program)
1997 }
1998
1999 fn GetProgramParameter(
2001 &self,
2002 cx: JSContext,
2003 program: &WebGLProgram,
2004 param_id: u32,
2005 mut retval: MutableHandleValue,
2006 ) {
2007 handle_potential_webgl_error!(
2008 self.base,
2009 self.base.validate_ownership(program),
2010 return retval.set(NullValue())
2011 );
2012 if program.is_deleted() {
2013 self.base.webgl_error(InvalidOperation);
2014 return retval.set(NullValue());
2015 }
2016 match param_id {
2017 constants::TRANSFORM_FEEDBACK_VARYINGS => {
2018 retval.set(Int32Value(program.transform_feedback_varyings_length()))
2019 },
2020 constants::TRANSFORM_FEEDBACK_BUFFER_MODE => {
2021 retval.set(Int32Value(program.transform_feedback_buffer_mode()))
2022 },
2023 _ => self.base.GetProgramParameter(cx, program, param_id, retval),
2024 }
2025 }
2026
2027 fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
2029 self.base.GetShaderInfoLog(shader)
2030 }
2031
2032 fn GetShaderParameter(
2034 &self,
2035 cx: JSContext,
2036 shader: &WebGLShader,
2037 param_id: u32,
2038 retval: MutableHandleValue,
2039 ) {
2040 self.base.GetShaderParameter(cx, shader, param_id, retval)
2041 }
2042
2043 fn GetShaderPrecisionFormat(
2045 &self,
2046 shader_type: u32,
2047 precision_type: u32,
2048 ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
2049 self.base
2050 .GetShaderPrecisionFormat(shader_type, precision_type)
2051 }
2052
2053 fn GetIndexedParameter(
2055 &self,
2056 cx: JSContext,
2057 target: u32,
2058 index: u32,
2059 mut retval: MutableHandleValue,
2060 ) {
2061 let bindings = match target {
2062 constants::TRANSFORM_FEEDBACK_BUFFER_BINDING |
2063 constants::TRANSFORM_FEEDBACK_BUFFER_SIZE |
2064 constants::TRANSFORM_FEEDBACK_BUFFER_START => {
2065 &self.indexed_transform_feedback_buffer_bindings
2066 },
2067 constants::UNIFORM_BUFFER_BINDING |
2068 constants::UNIFORM_BUFFER_SIZE |
2069 constants::UNIFORM_BUFFER_START => &self.indexed_uniform_buffer_bindings,
2070 _ => {
2071 self.base.webgl_error(InvalidEnum);
2072 return retval.set(NullValue());
2073 },
2074 };
2075
2076 let binding = match bindings.get(index as usize) {
2077 Some(binding) => binding,
2078 None => {
2079 self.base.webgl_error(InvalidValue);
2080 return retval.set(NullValue());
2081 },
2082 };
2083
2084 match target {
2085 constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => {
2086 binding
2087 .buffer
2088 .get()
2089 .safe_to_jsval(cx, retval, CanGc::note())
2090 },
2091 constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => {
2092 retval.set(Int32Value(binding.start.get() as _))
2093 },
2094 constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => {
2095 retval.set(Int32Value(binding.size.get() as _))
2096 },
2097 _ => unreachable!(),
2098 }
2099 }
2100
2101 fn GetUniformLocation(
2103 &self,
2104 program: &WebGLProgram,
2105 name: DOMString,
2106 ) -> Option<DomRoot<WebGLUniformLocation>> {
2107 self.base.GetUniformLocation(program, name)
2108 }
2109
2110 fn GetVertexAttrib(&self, cx: JSContext, index: u32, pname: u32, retval: MutableHandleValue) {
2112 self.base.GetVertexAttrib(cx, index, pname, retval)
2113 }
2114
2115 fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
2117 self.base.GetVertexAttribOffset(index, pname)
2118 }
2119
2120 fn Hint(&self, target: u32, mode: u32) {
2122 self.base.Hint(target, mode)
2123 }
2124
2125 fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
2127 self.base.IsBuffer(buffer)
2128 }
2129
2130 fn IsEnabled(&self, cap: u32) -> bool {
2133 match cap {
2134 constants::RASTERIZER_DISCARD => self.enable_rasterizer_discard.get(),
2135 _ => self.base.IsEnabled(cap),
2136 }
2137 }
2138
2139 fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
2141 self.base.IsFramebuffer(frame_buffer)
2142 }
2143
2144 fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
2146 self.base.IsProgram(program)
2147 }
2148
2149 fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
2151 self.base.IsRenderbuffer(render_buffer)
2152 }
2153
2154 fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
2156 self.base.IsShader(shader)
2157 }
2158
2159 fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
2161 self.base.IsTexture(texture)
2162 }
2163
2164 fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool {
2166 self.base.is_vertex_array_webgl2(vertex_array)
2167 }
2168
2169 fn LineWidth(&self, width: f32) {
2171 self.base.LineWidth(width)
2172 }
2173
2174 fn PixelStorei(&self, param_name: u32, param_value: i32) {
2176 if param_value < 0 {
2177 return self.base.webgl_error(InvalidValue);
2178 }
2179
2180 match param_name {
2181 constants::PACK_ROW_LENGTH => self.texture_pack_row_length.set(param_value as _),
2182 constants::PACK_SKIP_PIXELS => self.texture_pack_skip_pixels.set(param_value as _),
2183 constants::PACK_SKIP_ROWS => self.texture_pack_skip_rows.set(param_value as _),
2184 _ => self.base.PixelStorei(param_name, param_value),
2185 }
2186 }
2187
2188 fn PolygonOffset(&self, factor: f32, units: f32) {
2190 self.base.PolygonOffset(factor, units)
2191 }
2192
2193 fn ReadPixels(
2195 &self,
2196 x: i32,
2197 y: i32,
2198 width: i32,
2199 height: i32,
2200 format: u32,
2201 pixel_type: u32,
2202 mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
2203 ) {
2204 let pixels =
2205 handle_potential_webgl_error!(self.base, pixels.as_mut().ok_or(InvalidValue), return);
2206
2207 self.read_pixels_into(x, y, width, height, format, pixel_type, pixels, 0)
2208 }
2209
2210 fn ReadPixels_(
2212 &self,
2213 x: i32,
2214 y: i32,
2215 width: i32,
2216 height: i32,
2217 format: u32,
2218 pixel_type: u32,
2219 dst_byte_offset: i64,
2220 ) {
2221 handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
2222
2223 let dst = match self.bound_pixel_pack_buffer.get() {
2224 Some(buffer) => buffer,
2225 None => return self.base.webgl_error(InvalidOperation),
2226 };
2227
2228 if dst_byte_offset < 0 {
2229 return self.base.webgl_error(InvalidValue);
2230 }
2231 let dst_byte_offset = dst_byte_offset as usize;
2232 if dst_byte_offset > dst.capacity() {
2233 return self.base.webgl_error(InvalidOperation);
2234 }
2235
2236 let ReadPixelsAllowedFormats {
2237 array_types: _,
2238 channels: bytes_per_pixel,
2239 } = match self.calc_read_pixel_formats(pixel_type, format) {
2240 Ok(result) => result,
2241 Err(error) => return self.base.webgl_error(error),
2242 };
2243 if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
2244 return self.base.webgl_error(InvalidOperation);
2245 }
2246
2247 let ReadPixelsSizes {
2248 row_stride: _,
2249 skipped_bytes,
2250 size,
2251 } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
2252 Ok(result) => result,
2253 Err(error) => return self.base.webgl_error(error),
2254 };
2255 let dst_end = dst_byte_offset + skipped_bytes + size;
2256 if dst.capacity() < dst_end {
2257 return self.base.webgl_error(InvalidOperation);
2258 }
2259
2260 {
2261 let (fb_width, fb_height) = handle_potential_webgl_error!(
2262 self.base,
2263 self.base
2264 .get_current_framebuffer_size()
2265 .ok_or(InvalidOperation),
2266 return
2267 );
2268 let src_origin = Point2D::new(x, y);
2269 let src_size = Size2D::new(width as u32, height as u32);
2270 let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
2271 if pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()).is_none() {
2272 return;
2273 }
2274 }
2275 let src_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
2276
2277 self.base.send_command(WebGLCommand::PixelStorei(
2278 constants::PACK_ALIGNMENT,
2279 self.base.get_texture_packing_alignment() as _,
2280 ));
2281 self.base.send_command(WebGLCommand::PixelStorei(
2282 constants::PACK_ROW_LENGTH,
2283 self.texture_pack_row_length.get() as _,
2284 ));
2285 self.base.send_command(WebGLCommand::PixelStorei(
2286 constants::PACK_SKIP_ROWS,
2287 self.texture_pack_skip_rows.get() as _,
2288 ));
2289 self.base.send_command(WebGLCommand::PixelStorei(
2290 constants::PACK_SKIP_PIXELS,
2291 self.texture_pack_skip_pixels.get() as _,
2292 ));
2293 self.base.send_command(WebGLCommand::ReadPixelsPP(
2294 src_rect,
2295 format,
2296 pixel_type,
2297 dst_byte_offset,
2298 ));
2299 }
2300
2301 fn ReadPixels__(
2303 &self,
2304 x: i32,
2305 y: i32,
2306 width: i32,
2307 height: i32,
2308 format: u32,
2309 pixel_type: u32,
2310 mut dst: CustomAutoRooterGuard<ArrayBufferView>,
2311 dst_elem_offset: u32,
2312 ) {
2313 self.read_pixels_into(
2314 x,
2315 y,
2316 width,
2317 height,
2318 format,
2319 pixel_type,
2320 &mut dst,
2321 dst_elem_offset,
2322 )
2323 }
2324
2325 fn SampleCoverage(&self, value: f32, invert: bool) {
2327 self.base.SampleCoverage(value, invert)
2328 }
2329
2330 fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
2332 self.base.Scissor(x, y, width, height)
2333 }
2334
2335 fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
2337 self.base.StencilFunc(func, ref_, mask)
2338 }
2339
2340 fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
2342 self.base.StencilFuncSeparate(face, func, ref_, mask)
2343 }
2344
2345 fn StencilMask(&self, mask: u32) {
2347 self.base.StencilMask(mask)
2348 }
2349
2350 fn StencilMaskSeparate(&self, face: u32, mask: u32) {
2352 self.base.StencilMaskSeparate(face, mask)
2353 }
2354
2355 fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
2357 self.base.StencilOp(fail, zfail, zpass)
2358 }
2359
2360 fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
2362 self.base.StencilOpSeparate(face, fail, zfail, zpass)
2363 }
2364
2365 fn LinkProgram(&self, program: &WebGLProgram) {
2367 self.base.LinkProgram(program)
2368 }
2369
2370 fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
2372 self.base.ShaderSource(shader, source)
2373 }
2374
2375 fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
2377 self.base.GetShaderSource(shader)
2378 }
2379
2380 fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
2382 self.base.Uniform1f(location, val)
2383 }
2384
2385 fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
2387 self.base.Uniform1i(location, val)
2388 }
2389
2390 fn Uniform1iv(
2392 &self,
2393 location: Option<&WebGLUniformLocation>,
2394 v: Int32ArrayOrLongSequence,
2395 src_offset: u32,
2396 src_length: u32,
2397 ) {
2398 self.base.uniform1iv(location, v, src_offset, src_length)
2399 }
2400
2401 fn Uniform1ui(&self, location: Option<&WebGLUniformLocation>, val: u32) {
2403 self.base.with_location(location, |location| {
2404 match location.type_() {
2405 constants::BOOL | constants::UNSIGNED_INT => (),
2406 _ => return Err(InvalidOperation),
2407 }
2408 self.base
2409 .send_command(WebGLCommand::Uniform1ui(location.id(), val));
2410 Ok(())
2411 });
2412 }
2413
2414 fn Uniform1uiv(
2416 &self,
2417 location: Option<&WebGLUniformLocation>,
2418 val: Uint32ArrayOrUnsignedLongSequence,
2419 src_offset: u32,
2420 src_length: u32,
2421 ) {
2422 self.base.with_location(location, |location| {
2423 match location.type_() {
2424 constants::BOOL |
2425 constants::UNSIGNED_INT |
2426 constants::SAMPLER_2D |
2427 constants::SAMPLER_2D_ARRAY |
2428 constants::SAMPLER_3D |
2429 constants::SAMPLER_CUBE => {},
2430 _ => return Err(InvalidOperation),
2431 }
2432
2433 let val = self.uniform_vec_section_uint(val, src_offset, src_length, 1, location)?;
2434
2435 match location.type_() {
2436 constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
2437 for &v in val
2438 .iter()
2439 .take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
2440 {
2441 if v >= self.base.limits().max_combined_texture_image_units {
2442 return Err(InvalidValue);
2443 }
2444 }
2445 },
2446 _ => {},
2447 }
2448 self.base
2449 .send_command(WebGLCommand::Uniform1uiv(location.id(), val));
2450 Ok(())
2451 });
2452 }
2453
2454 fn Uniform1fv(
2456 &self,
2457 location: Option<&WebGLUniformLocation>,
2458 v: Float32ArrayOrUnrestrictedFloatSequence,
2459 src_offset: u32,
2460 src_length: u32,
2461 ) {
2462 self.base.uniform1fv(location, v, src_offset, src_length);
2463 }
2464
2465 fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
2467 self.base.Uniform2f(location, x, y)
2468 }
2469
2470 fn Uniform2fv(
2472 &self,
2473 location: Option<&WebGLUniformLocation>,
2474 v: Float32ArrayOrUnrestrictedFloatSequence,
2475 src_offset: u32,
2476 src_length: u32,
2477 ) {
2478 self.base.uniform2fv(location, v, src_offset, src_length);
2479 }
2480
2481 fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
2483 self.base.Uniform2i(location, x, y)
2484 }
2485
2486 fn Uniform2iv(
2488 &self,
2489 location: Option<&WebGLUniformLocation>,
2490 v: Int32ArrayOrLongSequence,
2491 src_offset: u32,
2492 src_length: u32,
2493 ) {
2494 self.base.uniform2iv(location, v, src_offset, src_length)
2495 }
2496
2497 fn Uniform2ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32) {
2499 self.base.with_location(location, |location| {
2500 match location.type_() {
2501 constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
2502 _ => return Err(InvalidOperation),
2503 }
2504 self.base
2505 .send_command(WebGLCommand::Uniform2ui(location.id(), x, y));
2506 Ok(())
2507 });
2508 }
2509
2510 fn Uniform2uiv(
2512 &self,
2513 location: Option<&WebGLUniformLocation>,
2514 val: Uint32ArrayOrUnsignedLongSequence,
2515 src_offset: u32,
2516 src_length: u32,
2517 ) {
2518 self.base.with_location(location, |location| {
2519 match location.type_() {
2520 constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
2521 _ => return Err(InvalidOperation),
2522 }
2523 let val = self.uniform_vec_section_uint(val, src_offset, src_length, 2, location)?;
2524 self.base
2525 .send_command(WebGLCommand::Uniform2uiv(location.id(), val));
2526 Ok(())
2527 });
2528 }
2529
2530 fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
2532 self.base.Uniform3f(location, x, y, z)
2533 }
2534
2535 fn Uniform3fv(
2537 &self,
2538 location: Option<&WebGLUniformLocation>,
2539 v: Float32ArrayOrUnrestrictedFloatSequence,
2540 src_offset: u32,
2541 src_length: u32,
2542 ) {
2543 self.base.uniform3fv(location, v, src_offset, src_length);
2544 }
2545
2546 fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
2548 self.base.Uniform3i(location, x, y, z)
2549 }
2550
2551 fn Uniform3iv(
2553 &self,
2554 location: Option<&WebGLUniformLocation>,
2555 v: Int32ArrayOrLongSequence,
2556 src_offset: u32,
2557 src_length: u32,
2558 ) {
2559 self.base.uniform3iv(location, v, src_offset, src_length)
2560 }
2561
2562 fn Uniform3ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32) {
2564 self.base.with_location(location, |location| {
2565 match location.type_() {
2566 constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
2567 _ => return Err(InvalidOperation),
2568 }
2569 self.base
2570 .send_command(WebGLCommand::Uniform3ui(location.id(), x, y, z));
2571 Ok(())
2572 });
2573 }
2574
2575 fn Uniform3uiv(
2577 &self,
2578 location: Option<&WebGLUniformLocation>,
2579 val: Uint32ArrayOrUnsignedLongSequence,
2580 src_offset: u32,
2581 src_length: u32,
2582 ) {
2583 self.base.with_location(location, |location| {
2584 match location.type_() {
2585 constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
2586 _ => return Err(InvalidOperation),
2587 }
2588 let val = self.uniform_vec_section_uint(val, src_offset, src_length, 3, location)?;
2589 self.base
2590 .send_command(WebGLCommand::Uniform3uiv(location.id(), val));
2591 Ok(())
2592 });
2593 }
2594
2595 fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
2597 self.base.Uniform4i(location, x, y, z, w)
2598 }
2599
2600 fn Uniform4iv(
2602 &self,
2603 location: Option<&WebGLUniformLocation>,
2604 v: Int32ArrayOrLongSequence,
2605 src_offset: u32,
2606 src_length: u32,
2607 ) {
2608 self.base.uniform4iv(location, v, src_offset, src_length)
2609 }
2610
2611 fn Uniform4ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32, w: u32) {
2613 self.base.with_location(location, |location| {
2614 match location.type_() {
2615 constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
2616 _ => return Err(InvalidOperation),
2617 }
2618 self.base
2619 .send_command(WebGLCommand::Uniform4ui(location.id(), x, y, z, w));
2620 Ok(())
2621 });
2622 }
2623
2624 fn Uniform4uiv(
2626 &self,
2627 location: Option<&WebGLUniformLocation>,
2628 val: Uint32ArrayOrUnsignedLongSequence,
2629 src_offset: u32,
2630 src_length: u32,
2631 ) {
2632 self.base.with_location(location, |location| {
2633 match location.type_() {
2634 constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
2635 _ => return Err(InvalidOperation),
2636 }
2637 let val = self.uniform_vec_section_uint(val, src_offset, src_length, 4, location)?;
2638 self.base
2639 .send_command(WebGLCommand::Uniform4uiv(location.id(), val));
2640 Ok(())
2641 });
2642 }
2643
2644 fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
2646 self.base.Uniform4f(location, x, y, z, w)
2647 }
2648
2649 fn Uniform4fv(
2651 &self,
2652 location: Option<&WebGLUniformLocation>,
2653 v: Float32ArrayOrUnrestrictedFloatSequence,
2654 src_offset: u32,
2655 src_length: u32,
2656 ) {
2657 self.base.uniform4fv(location, v, src_offset, src_length);
2658 }
2659
2660 fn UniformMatrix2fv(
2662 &self,
2663 location: Option<&WebGLUniformLocation>,
2664 transpose: bool,
2665 v: Float32ArrayOrUnrestrictedFloatSequence,
2666 src_offset: u32,
2667 src_length: u32,
2668 ) {
2669 self.base
2670 .uniform_matrix_2fv(location, transpose, v, src_offset, src_length)
2671 }
2672
2673 fn UniformMatrix3fv(
2675 &self,
2676 location: Option<&WebGLUniformLocation>,
2677 transpose: bool,
2678 v: Float32ArrayOrUnrestrictedFloatSequence,
2679 src_offset: u32,
2680 src_length: u32,
2681 ) {
2682 self.base
2683 .uniform_matrix_3fv(location, transpose, v, src_offset, src_length)
2684 }
2685
2686 fn UniformMatrix4fv(
2688 &self,
2689 location: Option<&WebGLUniformLocation>,
2690 transpose: bool,
2691 v: Float32ArrayOrUnrestrictedFloatSequence,
2692 src_offset: u32,
2693 src_length: u32,
2694 ) {
2695 self.base
2696 .uniform_matrix_4fv(location, transpose, v, src_offset, src_length)
2697 }
2698
2699 fn UniformMatrix3x2fv(
2701 &self,
2702 location: Option<&WebGLUniformLocation>,
2703 transpose: bool,
2704 val: Float32ArrayOrUnrestrictedFloatSequence,
2705 src_offset: u32,
2706 src_length: u32,
2707 ) {
2708 self.base.with_location(location, |location| {
2709 match location.type_() {
2710 constants::FLOAT_MAT3x2 => {},
2711 _ => return Err(InvalidOperation),
2712 }
2713 let val = self.base.uniform_matrix_section(
2714 val,
2715 src_offset,
2716 src_length,
2717 transpose,
2718 3 * 2,
2719 location,
2720 )?;
2721 self.base
2722 .send_command(WebGLCommand::UniformMatrix3x2fv(location.id(), val));
2723 Ok(())
2724 });
2725 }
2726
2727 fn UniformMatrix4x2fv(
2729 &self,
2730 location: Option<&WebGLUniformLocation>,
2731 transpose: bool,
2732 val: Float32ArrayOrUnrestrictedFloatSequence,
2733 src_offset: u32,
2734 src_length: u32,
2735 ) {
2736 self.base.with_location(location, |location| {
2737 match location.type_() {
2738 constants::FLOAT_MAT4x2 => {},
2739 _ => return Err(InvalidOperation),
2740 }
2741 let val = self.base.uniform_matrix_section(
2742 val,
2743 src_offset,
2744 src_length,
2745 transpose,
2746 4 * 2,
2747 location,
2748 )?;
2749 self.base
2750 .send_command(WebGLCommand::UniformMatrix4x2fv(location.id(), val));
2751 Ok(())
2752 });
2753 }
2754
2755 fn UniformMatrix2x3fv(
2757 &self,
2758 location: Option<&WebGLUniformLocation>,
2759 transpose: bool,
2760 val: Float32ArrayOrUnrestrictedFloatSequence,
2761 src_offset: u32,
2762 src_length: u32,
2763 ) {
2764 self.base.with_location(location, |location| {
2765 match location.type_() {
2766 constants::FLOAT_MAT2x3 => {},
2767 _ => return Err(InvalidOperation),
2768 }
2769 let val = self.base.uniform_matrix_section(
2770 val,
2771 src_offset,
2772 src_length,
2773 transpose,
2774 2 * 3,
2775 location,
2776 )?;
2777 self.base
2778 .send_command(WebGLCommand::UniformMatrix2x3fv(location.id(), val));
2779 Ok(())
2780 });
2781 }
2782
2783 fn UniformMatrix4x3fv(
2785 &self,
2786 location: Option<&WebGLUniformLocation>,
2787 transpose: bool,
2788 val: Float32ArrayOrUnrestrictedFloatSequence,
2789 src_offset: u32,
2790 src_length: u32,
2791 ) {
2792 self.base.with_location(location, |location| {
2793 match location.type_() {
2794 constants::FLOAT_MAT4x3 => {},
2795 _ => return Err(InvalidOperation),
2796 }
2797 let val = self.base.uniform_matrix_section(
2798 val,
2799 src_offset,
2800 src_length,
2801 transpose,
2802 4 * 3,
2803 location,
2804 )?;
2805 self.base
2806 .send_command(WebGLCommand::UniformMatrix4x3fv(location.id(), val));
2807 Ok(())
2808 });
2809 }
2810
2811 fn UniformMatrix2x4fv(
2813 &self,
2814 location: Option<&WebGLUniformLocation>,
2815 transpose: bool,
2816 val: Float32ArrayOrUnrestrictedFloatSequence,
2817 src_offset: u32,
2818 src_length: u32,
2819 ) {
2820 self.base.with_location(location, |location| {
2821 match location.type_() {
2822 constants::FLOAT_MAT2x4 => {},
2823 _ => return Err(InvalidOperation),
2824 }
2825 let val = self.base.uniform_matrix_section(
2826 val,
2827 src_offset,
2828 src_length,
2829 transpose,
2830 2 * 4,
2831 location,
2832 )?;
2833 self.base
2834 .send_command(WebGLCommand::UniformMatrix2x4fv(location.id(), val));
2835 Ok(())
2836 });
2837 }
2838
2839 fn UniformMatrix3x4fv(
2841 &self,
2842 location: Option<&WebGLUniformLocation>,
2843 transpose: bool,
2844 val: Float32ArrayOrUnrestrictedFloatSequence,
2845 src_offset: u32,
2846 src_length: u32,
2847 ) {
2848 self.base.with_location(location, |location| {
2849 match location.type_() {
2850 constants::FLOAT_MAT3x4 => {},
2851 _ => return Err(InvalidOperation),
2852 }
2853 let val = self.base.uniform_matrix_section(
2854 val,
2855 src_offset,
2856 src_length,
2857 transpose,
2858 3 * 4,
2859 location,
2860 )?;
2861 self.base
2862 .send_command(WebGLCommand::UniformMatrix3x4fv(location.id(), val));
2863 Ok(())
2864 });
2865 }
2866
2867 #[expect(unsafe_code)]
2869 fn GetUniform(
2870 &self,
2871 cx: JSContext,
2872 program: &WebGLProgram,
2873 location: &WebGLUniformLocation,
2874 mut retval: MutableHandleValue,
2875 ) {
2876 handle_potential_webgl_error!(
2877 self.base,
2878 self.base.uniform_check_program(program, location),
2879 return retval.set(NullValue())
2880 );
2881
2882 let triple = (&*self.base, program.id(), location.id());
2883
2884 match location.type_() {
2885 constants::UNSIGNED_INT => retval.set(UInt32Value(uniform_get(
2886 triple,
2887 WebGLCommand::GetUniformUint,
2888 ))),
2889 constants::UNSIGNED_INT_VEC2 => unsafe {
2890 uniform_typed::<Uint32>(
2891 *cx,
2892 &uniform_get(triple, WebGLCommand::GetUniformUint2),
2893 retval,
2894 )
2895 },
2896 constants::UNSIGNED_INT_VEC3 => unsafe {
2897 uniform_typed::<Uint32>(
2898 *cx,
2899 &uniform_get(triple, WebGLCommand::GetUniformUint3),
2900 retval,
2901 )
2902 },
2903 constants::UNSIGNED_INT_VEC4 => unsafe {
2904 uniform_typed::<Uint32>(
2905 *cx,
2906 &uniform_get(triple, WebGLCommand::GetUniformUint4),
2907 retval,
2908 )
2909 },
2910 constants::FLOAT_MAT2x3 => unsafe {
2911 uniform_typed::<Float32>(
2912 *cx,
2913 &uniform_get(triple, WebGLCommand::GetUniformFloat2x3),
2914 retval,
2915 )
2916 },
2917 constants::FLOAT_MAT2x4 => unsafe {
2918 uniform_typed::<Float32>(
2919 *cx,
2920 &uniform_get(triple, WebGLCommand::GetUniformFloat2x4),
2921 retval,
2922 )
2923 },
2924 constants::FLOAT_MAT3x2 => unsafe {
2925 uniform_typed::<Float32>(
2926 *cx,
2927 &uniform_get(triple, WebGLCommand::GetUniformFloat3x2),
2928 retval,
2929 )
2930 },
2931 constants::FLOAT_MAT3x4 => unsafe {
2932 uniform_typed::<Float32>(
2933 *cx,
2934 &uniform_get(triple, WebGLCommand::GetUniformFloat3x4),
2935 retval,
2936 )
2937 },
2938 constants::FLOAT_MAT4x2 => unsafe {
2939 uniform_typed::<Float32>(
2940 *cx,
2941 &uniform_get(triple, WebGLCommand::GetUniformFloat4x2),
2942 retval,
2943 )
2944 },
2945 constants::FLOAT_MAT4x3 => unsafe {
2946 uniform_typed::<Float32>(
2947 *cx,
2948 &uniform_get(triple, WebGLCommand::GetUniformFloat4x3),
2949 retval,
2950 )
2951 },
2952 constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => {
2953 retval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
2954 },
2955 _ => self.base.GetUniform(cx, program, location, retval),
2956 }
2957 }
2958
2959 fn UseProgram(&self, program: Option<&WebGLProgram>) {
2961 self.base.UseProgram(program)
2962 }
2963
2964 fn ValidateProgram(&self, program: &WebGLProgram) {
2966 self.base.ValidateProgram(program)
2967 }
2968
2969 fn VertexAttrib1f(&self, indx: u32, x: f32) {
2971 self.base.VertexAttrib1f(indx, x)
2972 }
2973
2974 fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
2976 self.base.VertexAttrib1fv(indx, v)
2977 }
2978
2979 fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) {
2981 self.base.VertexAttrib2f(indx, x, y)
2982 }
2983
2984 fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
2986 self.base.VertexAttrib2fv(indx, v)
2987 }
2988
2989 fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) {
2991 self.base.VertexAttrib3f(indx, x, y, z)
2992 }
2993
2994 fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
2996 self.base.VertexAttrib3fv(indx, v)
2997 }
2998
2999 fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
3001 self.base.VertexAttrib4f(indx, x, y, z, w)
3002 }
3003
3004 fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
3006 self.base.VertexAttrib4fv(indx, v)
3007 }
3008
3009 fn VertexAttribI4i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) {
3011 self.vertex_attrib_i(index, x, y, z, w)
3012 }
3013
3014 fn VertexAttribI4iv(&self, index: u32, v: Int32ArrayOrLongSequence) {
3016 let values = match v {
3017 Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
3018 Int32ArrayOrLongSequence::LongSequence(v) => v,
3019 };
3020 if values.len() < 4 {
3021 return self.base.webgl_error(InvalidValue);
3022 }
3023 self.vertex_attrib_i(index, values[0], values[1], values[2], values[3]);
3024 }
3025
3026 fn VertexAttribI4ui(&self, index: u32, x: u32, y: u32, z: u32, w: u32) {
3028 self.vertex_attrib_u(index, x, y, z, w)
3029 }
3030
3031 fn VertexAttribI4uiv(&self, index: u32, v: Uint32ArrayOrUnsignedLongSequence) {
3033 let values = match v {
3034 Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
3035 Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
3036 };
3037 if values.len() < 4 {
3038 return self.base.webgl_error(InvalidValue);
3039 }
3040 self.vertex_attrib_u(index, values[0], values[1], values[2], values[3]);
3041 }
3042
3043 fn VertexAttribPointer(
3045 &self,
3046 attrib_id: u32,
3047 size: i32,
3048 data_type: u32,
3049 normalized: bool,
3050 stride: i32,
3051 offset: i64,
3052 ) {
3053 self.base
3054 .VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset)
3055 }
3056
3057 fn VertexAttribIPointer(&self, index: u32, size: i32, type_: u32, stride: i32, offset: i64) {
3059 match type_ {
3060 constants::BYTE |
3061 constants::UNSIGNED_BYTE |
3062 constants::SHORT |
3063 constants::UNSIGNED_SHORT |
3064 constants::INT |
3065 constants::UNSIGNED_INT => {},
3066 _ => return self.base.webgl_error(InvalidEnum),
3067 };
3068 self.base
3069 .VertexAttribPointer(index, size, type_, false, stride, offset)
3070 }
3071
3072 fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
3074 self.base.Viewport(x, y, width, height)
3075 }
3076
3077 #[expect(unsafe_code)]
3082 fn TexImage3D(
3083 &self,
3084 target: u32,
3085 level: i32,
3086 internal_format: i32,
3087 width: i32,
3088 height: i32,
3089 depth: i32,
3090 border: i32,
3091 format: u32,
3092 type_: u32,
3093 src_data: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3094 ) -> Fallible<()> {
3095 if self.bound_pixel_unpack_buffer.get().is_some() {
3098 self.base.webgl_error(InvalidOperation);
3099 return Ok(());
3100 }
3101
3102 if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV && src_data.is_some() {
3105 self.base.webgl_error(InvalidOperation);
3106 return Ok(());
3107 }
3108
3109 if border != 0 {
3110 self.base.webgl_error(InvalidValue);
3111 return Ok(());
3112 }
3113
3114 let Ok(TexImage3DValidatorResult {
3115 width,
3116 height,
3117 depth,
3118 level,
3119 border,
3120 texture,
3121 target,
3122 internal_format,
3123 format,
3124 data_type,
3125 }) = TexImage3DValidator::new(
3126 &self.base,
3127 target,
3128 level,
3129 internal_format as u32,
3130 width,
3131 height,
3132 depth,
3133 border,
3134 format,
3135 type_,
3136 &src_data,
3137 )
3138 .validate()
3139 else {
3140 return Ok(());
3141 };
3142
3143 let unpacking_alignment = self.base.texture_unpacking_alignment();
3147 let buff = match *src_data {
3148 Some(ref data) => GenericSharedMemory::from_bytes(unsafe { data.as_slice() }),
3149 None => {
3150 let element_size = data_type.element_size();
3151 let components = format.components();
3152 let components_per_element = format.components();
3153 let expected_byte_len = if height == 0 {
3156 0
3157 } else {
3158 let cpp = element_size * components / components_per_element;
3163 let stride =
3164 (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
3165 stride * (height - 1) + width * cpp
3166 };
3167 GenericSharedMemory::from_bytes(&vec![0u8; expected_byte_len as usize])
3168 },
3169 };
3170 let (alpha_treatment, y_axis_treatment) =
3171 self.base.get_current_unpack_state(Alpha::NotPremultiplied);
3172 if let (Some(AlphaTreatment::Premultiply), YAxisTreatment::Flipped) =
3176 (alpha_treatment, y_axis_treatment)
3177 {
3178 if src_data.is_some() {
3179 self.base.webgl_error(InvalidOperation);
3180 return Ok(());
3181 }
3182 }
3183 let tex_source = TexPixels::from_array(
3184 buff,
3185 Size2D::new(width, height),
3186 alpha_treatment,
3187 y_axis_treatment,
3188 );
3189
3190 self.tex_image_3d(
3191 &texture,
3192 target,
3193 data_type,
3194 internal_format,
3195 format,
3196 level,
3197 width,
3198 height,
3199 depth,
3200 border,
3201 unpacking_alignment,
3202 tex_source,
3203 );
3204 Ok(())
3205 }
3206
3207 fn TexImage2D(
3209 &self,
3210 target: u32,
3211 level: i32,
3212 internal_format: i32,
3213 width: i32,
3214 height: i32,
3215 border: i32,
3216 format: u32,
3217 data_type: u32,
3218 pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3219 ) -> Fallible<()> {
3220 self.base.TexImage2D(
3221 target,
3222 level,
3223 internal_format,
3224 width,
3225 height,
3226 border,
3227 format,
3228 data_type,
3229 pixels,
3230 )
3231 }
3232
3233 fn TexImage2D_(
3235 &self,
3236 target: u32,
3237 level: i32,
3238 internal_format: i32,
3239 format: u32,
3240 data_type: u32,
3241 source: TexImageSource,
3242 ) -> ErrorResult {
3243 self.base
3244 .TexImage2D_(target, level, internal_format, format, data_type, source)
3245 }
3246
3247 fn TexImage2D__(
3249 &self,
3250 target: u32,
3251 level: i32,
3252 internalformat: i32,
3253 width: i32,
3254 height: i32,
3255 border: i32,
3256 format: u32,
3257 type_: u32,
3258 pbo_offset: i64,
3259 ) -> Fallible<()> {
3260 let pixel_unpack_buffer = match self.bound_pixel_unpack_buffer.get() {
3261 Some(pixel_unpack_buffer) => pixel_unpack_buffer,
3262 None => {
3263 self.base.webgl_error(InvalidOperation);
3264 return Ok(());
3265 },
3266 };
3267
3268 if let Some(tf_buffer) = self.bound_transform_feedback_buffer.get() {
3269 if pixel_unpack_buffer == tf_buffer {
3270 self.base.webgl_error(InvalidOperation);
3271 return Ok(());
3272 }
3273 }
3274
3275 if pbo_offset < 0 || pbo_offset as usize > pixel_unpack_buffer.capacity() {
3276 self.base.webgl_error(InvalidValue);
3277 return Ok(());
3278 }
3279
3280 let unpacking_alignment = self.base.texture_unpacking_alignment();
3281
3282 let validator = TexImage2DValidator::new(
3283 &self.base,
3284 target,
3285 level,
3286 internalformat as u32,
3287 width,
3288 height,
3289 border,
3290 format,
3291 type_,
3292 );
3293
3294 let TexImage2DValidatorResult {
3295 texture,
3296 target,
3297 width,
3298 height,
3299 level,
3300 border,
3301 internal_format,
3302 format,
3303 data_type,
3304 } = match validator.validate() {
3305 Ok(result) => result,
3306 Err(_) => return Ok(()),
3307 };
3308
3309 self.base.tex_image_2d(
3310 &texture,
3311 target,
3312 data_type,
3313 internal_format,
3314 format,
3315 level,
3316 border,
3317 unpacking_alignment,
3318 Size2D::new(width, height),
3319 TexSource::BufferOffset(pbo_offset),
3320 );
3321
3322 Ok(())
3323 }
3324
3325 fn TexImage2D___(
3327 &self,
3328 target: u32,
3329 level: i32,
3330 internalformat: i32,
3331 width: i32,
3332 height: i32,
3333 border: i32,
3334 format: u32,
3335 type_: u32,
3336 source: TexImageSource,
3337 ) -> Fallible<()> {
3338 if self.bound_pixel_unpack_buffer.get().is_some() {
3339 self.base.webgl_error(InvalidOperation);
3340 return Ok(());
3341 }
3342
3343 let validator = TexImage2DValidator::new(
3344 &self.base,
3345 target,
3346 level,
3347 internalformat as u32,
3348 width,
3349 height,
3350 border,
3351 format,
3352 type_,
3353 );
3354
3355 let TexImage2DValidatorResult {
3356 texture,
3357 target,
3358 width: _,
3359 height: _,
3360 level,
3361 border,
3362 internal_format,
3363 format,
3364 data_type,
3365 } = match validator.validate() {
3366 Ok(result) => result,
3367 Err(_) => return Ok(()),
3368 };
3369
3370 let unpacking_alignment = self.base.texture_unpacking_alignment();
3371
3372 let pixels = match self.base.get_image_pixels(source)? {
3373 Some(pixels) => pixels,
3374 None => return Ok(()),
3375 };
3376
3377 self.base.tex_image_2d(
3378 &texture,
3379 target,
3380 data_type,
3381 internal_format,
3382 format,
3383 level,
3384 border,
3385 unpacking_alignment,
3386 pixels.size(),
3387 TexSource::Pixels(pixels),
3388 );
3389
3390 Ok(())
3391 }
3392
3393 #[expect(unsafe_code)]
3395 fn TexImage2D____(
3396 &self,
3397 target: u32,
3398 level: i32,
3399 internalformat: i32,
3400 width: i32,
3401 height: i32,
3402 border: i32,
3403 format: u32,
3404 type_: u32,
3405 src_data: CustomAutoRooterGuard<ArrayBufferView>,
3406 src_offset: u32,
3407 ) -> Fallible<()> {
3408 if self.bound_pixel_unpack_buffer.get().is_some() {
3409 self.base.webgl_error(InvalidOperation);
3410 return Ok(());
3411 }
3412
3413 if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV {
3414 self.base.webgl_error(InvalidOperation);
3415 return Ok(());
3416 }
3417
3418 let validator = TexImage2DValidator::new(
3419 &self.base,
3420 target,
3421 level,
3422 internalformat as u32,
3423 width,
3424 height,
3425 border,
3426 format,
3427 type_,
3428 );
3429
3430 let TexImage2DValidatorResult {
3431 texture,
3432 target,
3433 width,
3434 height,
3435 level,
3436 border,
3437 internal_format,
3438 format,
3439 data_type,
3440 } = match validator.validate() {
3441 Ok(result) => result,
3442 Err(_) => return Ok(()),
3443 };
3444
3445 let unpacking_alignment = self.base.texture_unpacking_alignment();
3446
3447 let src_elem_size = src_data.get_array_type().byte_size().unwrap();
3448 let src_byte_offset = src_offset as usize * src_elem_size;
3449
3450 if src_data.len() <= src_byte_offset {
3451 self.base.webgl_error(InvalidOperation);
3452 return Ok(());
3453 }
3454
3455 let buff =
3456 GenericSharedMemory::from_bytes(unsafe { &src_data.as_slice()[src_byte_offset..] });
3457
3458 let expected_byte_length = match self.base.validate_tex_image_2d_data(
3459 width,
3460 height,
3461 format,
3462 data_type,
3463 unpacking_alignment,
3464 Some(&*src_data),
3465 ) {
3466 Ok(byte_length) => byte_length,
3467 Err(()) => return Ok(()),
3468 };
3469
3470 if expected_byte_length as usize > buff.len() {
3471 self.base.webgl_error(InvalidOperation);
3472 return Ok(());
3473 }
3474
3475 let size = Size2D::new(width, height);
3476
3477 let (alpha_treatment, y_axis_treatment) =
3478 self.base.get_current_unpack_state(Alpha::NotPremultiplied);
3479
3480 self.base.tex_image_2d(
3481 &texture,
3482 target,
3483 data_type,
3484 internal_format,
3485 format,
3486 level,
3487 border,
3488 unpacking_alignment,
3489 size,
3490 TexSource::Pixels(TexPixels::from_array(
3491 buff,
3492 size,
3493 alpha_treatment,
3494 y_axis_treatment,
3495 )),
3496 );
3497
3498 Ok(())
3499 }
3500
3501 fn TexSubImage2D(
3503 &self,
3504 target: u32,
3505 level: i32,
3506 xoffset: i32,
3507 yoffset: i32,
3508 width: i32,
3509 height: i32,
3510 format: u32,
3511 data_type: u32,
3512 pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3513 ) -> Fallible<()> {
3514 self.base.TexSubImage2D(
3515 target, level, xoffset, yoffset, width, height, format, data_type, pixels,
3516 )
3517 }
3518
3519 fn TexSubImage2D_(
3521 &self,
3522 target: u32,
3523 level: i32,
3524 xoffset: i32,
3525 yoffset: i32,
3526 format: u32,
3527 data_type: u32,
3528 source: TexImageSource,
3529 ) -> ErrorResult {
3530 self.base
3531 .TexSubImage2D_(target, level, xoffset, yoffset, format, data_type, source)
3532 }
3533
3534 fn TexParameterf(&self, target: u32, name: u32, value: f32) {
3536 self.base.TexParameterf(target, name, value)
3537 }
3538
3539 fn TexParameteri(&self, target: u32, name: u32, value: i32) {
3541 self.base.TexParameteri(target, name, value)
3542 }
3543
3544 fn CheckFramebufferStatus(&self, target: u32) -> u32 {
3546 let fb_slot = match target {
3547 constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3548 self.base.get_draw_framebuffer_slot()
3549 },
3550 constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3551 _ => {
3552 self.base.webgl_error(InvalidEnum);
3553 return 0;
3554 },
3555 };
3556 match fb_slot.get() {
3557 Some(fb) => fb.check_status(),
3558 None => constants::FRAMEBUFFER_COMPLETE,
3559 }
3560 }
3561
3562 fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
3564 self.base
3565 .RenderbufferStorage(target, internal_format, width, height)
3566 }
3567
3568 fn BlitFramebuffer(
3570 &self,
3571 src_x0: i32,
3572 src_y0: i32,
3573 src_x1: i32,
3574 src_y1: i32,
3575 dst_x0: i32,
3576 dst_y0: i32,
3577 dst_x1: i32,
3578 dst_y1: i32,
3579 mask: u32,
3580 filter: u32,
3581 ) {
3582 bitflags! {
3583 struct BlitFrameBufferFlags: u32 {
3584 const DEPTH = constants::DEPTH_BUFFER_BIT;
3585 const COLOR = constants::COLOR_BUFFER_BIT;
3586 const STENCIL = constants::STENCIL_BUFFER_BIT;
3587 const DEPTH_STENCIL = constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT;
3588 }
3589 };
3590 let Some(bits) = BlitFrameBufferFlags::from_bits(mask) else {
3591 return self.base.webgl_error(InvalidValue);
3592 };
3593 let attributes = self.base.GetContextAttributes().unwrap();
3594
3595 if bits.intersects(BlitFrameBufferFlags::DEPTH_STENCIL) {
3596 match filter {
3597 constants::LINEAR => return self.base.webgl_error(InvalidOperation),
3598 constants::NEAREST => {},
3599 _ => return self.base.webgl_error(InvalidOperation),
3600 }
3601 }
3602
3603 let src_fb = self.base.get_read_framebuffer_slot().get();
3604 let dst_fb = self.base.get_draw_framebuffer_slot().get();
3605
3606 let get_default_formats = || -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
3607 if attributes.antialias {
3609 return Err(InvalidOperation);
3610 };
3611 let color = if attributes.alpha {
3612 Some(constants::RGBA8)
3613 } else {
3614 Some(constants::RGB8)
3615 };
3616 let (depth, stencil) = match (attributes.depth, attributes.stencil) {
3617 (true, true) => (
3618 Some(constants::DEPTH24_STENCIL8),
3619 Some(constants::DEPTH24_STENCIL8),
3620 ),
3621 (true, false) => (Some(constants::DEPTH_COMPONENT16), None),
3622 (false, true) => (None, Some(constants::STENCIL_INDEX8)),
3623 _ => (None, None),
3624 };
3625 Ok((color, depth, stencil))
3626 };
3627
3628 let (src_color, src_depth, src_stencil) = match src_fb {
3629 Some(fb) => {
3630 handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
3631 },
3632 None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
3633 };
3634 let (dst_color, dst_depth, dst_stencil) = match dst_fb {
3635 Some(fb) => {
3636 handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
3637 },
3638 None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
3639 };
3640
3641 if bits.intersects(BlitFrameBufferFlags::COLOR) && src_color != dst_color {
3642 return self.base.webgl_error(InvalidOperation);
3643 }
3644 if bits.intersects(BlitFrameBufferFlags::DEPTH) && src_depth != dst_depth {
3645 return self.base.webgl_error(InvalidOperation);
3646 }
3647 if bits.intersects(BlitFrameBufferFlags::STENCIL) && src_stencil != dst_stencil {
3648 return self.base.webgl_error(InvalidOperation);
3649 }
3650
3651 let src_width = src_x1.checked_sub(src_x0);
3652 let dst_width = dst_x1.checked_sub(dst_x0);
3653 let src_height = src_y1.checked_sub(src_y0);
3654 let dst_height = dst_y1.checked_sub(dst_y0);
3655
3656 if src_width.is_none() ||
3657 dst_width.is_none() ||
3658 src_height.is_none() ||
3659 dst_height.is_none()
3660 {
3661 return self.base.webgl_error(InvalidOperation);
3662 }
3663
3664 self.base.send_command(WebGLCommand::BlitFrameBuffer(
3665 src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
3666 ));
3667 }
3668
3669 fn FramebufferRenderbuffer(
3671 &self,
3672 target: u32,
3673 attachment: u32,
3674 renderbuffertarget: u32,
3675 rb: Option<&WebGLRenderbuffer>,
3676 ) {
3677 if let Some(rb) = rb {
3678 handle_potential_webgl_error!(self.base, self.base.validate_ownership(rb), return);
3679 }
3680
3681 let fb_slot = match target {
3682 constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3683 self.base.get_draw_framebuffer_slot()
3684 },
3685 constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3686 _ => return self.base.webgl_error(InvalidEnum),
3687 };
3688
3689 if renderbuffertarget != constants::RENDERBUFFER {
3690 return self.base.webgl_error(InvalidEnum);
3691 }
3692
3693 match fb_slot.get() {
3694 Some(fb) => match attachment {
3695 constants::DEPTH_STENCIL_ATTACHMENT => {
3696 handle_potential_webgl_error!(
3697 self.base,
3698 fb.renderbuffer(constants::DEPTH_ATTACHMENT, rb)
3699 );
3700 handle_potential_webgl_error!(
3701 self.base,
3702 fb.renderbuffer(constants::STENCIL_ATTACHMENT, rb)
3703 );
3704 },
3705 _ => handle_potential_webgl_error!(self.base, fb.renderbuffer(attachment, rb)),
3706 },
3707 None => self.base.webgl_error(InvalidOperation),
3708 };
3709 }
3710
3711 fn FramebufferTexture2D(
3713 &self,
3714 target: u32,
3715 attachment: u32,
3716 textarget: u32,
3717 texture: Option<&WebGLTexture>,
3718 level: i32,
3719 ) {
3720 if let Some(texture) = texture {
3721 handle_potential_webgl_error!(self.base, self.base.validate_ownership(texture), return);
3722 }
3723
3724 let fb_slot = match target {
3725 constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3726 self.base.get_draw_framebuffer_slot()
3727 },
3728 constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3729 _ => return self.base.webgl_error(InvalidEnum),
3730 };
3731 match fb_slot.get() {
3732 Some(fb) => handle_potential_webgl_error!(
3733 self.base,
3734 fb.texture2d(attachment, textarget, texture, level)
3735 ),
3736 None => self.base.webgl_error(InvalidOperation),
3737 }
3738 }
3739
3740 fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
3742 self.base.GetAttachedShaders(program)
3743 }
3744
3745 fn DrawArraysInstanced(&self, mode: u32, first: i32, count: i32, primcount: i32) {
3747 self.validate_uniform_block_for_draw();
3748 self.validate_vertex_attribs_for_draw();
3749 handle_potential_webgl_error!(
3750 self.base,
3751 self.base
3752 .draw_arrays_instanced(mode, first, count, primcount)
3753 )
3754 }
3755
3756 fn DrawElementsInstanced(
3758 &self,
3759 mode: u32,
3760 count: i32,
3761 type_: u32,
3762 offset: i64,
3763 primcount: i32,
3764 ) {
3765 self.validate_uniform_block_for_draw();
3766 self.validate_vertex_attribs_for_draw();
3767 handle_potential_webgl_error!(
3768 self.base,
3769 self.base
3770 .draw_elements_instanced(mode, count, type_, offset, primcount)
3771 )
3772 }
3773
3774 fn DrawRangeElements(
3776 &self,
3777 mode: u32,
3778 start: u32,
3779 end: u32,
3780 count: i32,
3781 type_: u32,
3782 offset: i64,
3783 ) {
3784 if end < start {
3785 self.base.webgl_error(InvalidValue);
3786 return;
3787 }
3788 self.validate_uniform_block_for_draw();
3789 self.validate_vertex_attribs_for_draw();
3790 handle_potential_webgl_error!(
3791 self.base,
3792 self.base
3793 .draw_elements_instanced(mode, count, type_, offset, 1)
3794 )
3795 }
3796
3797 fn VertexAttribDivisor(&self, index: u32, divisor: u32) {
3799 self.base.vertex_attrib_divisor(index, divisor);
3800 }
3801
3802 fn CreateQuery(&self) -> Option<DomRoot<WebGLQuery>> {
3804 Some(WebGLQuery::new(&self.base, CanGc::note()))
3805 }
3806
3807 #[rustfmt::skip]
3809 fn DeleteQuery(&self, query: Option<&WebGLQuery>) {
3810 if let Some(query) = query {
3811 handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
3812
3813 if let Some(query_target) = query.target() {
3814 let slot = match query_target {
3815 constants::ANY_SAMPLES_PASSED |
3816 constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3817 &self.occlusion_query
3818 },
3819 constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3820 &self.primitives_query
3821 },
3822 _ => unreachable!(),
3823 };
3824 if let Some(stored_query) = slot.get() {
3825 if stored_query.target() == query.target() {
3826 slot.set(None);
3827 }
3828 }
3829 }
3830
3831 query.delete(Operation::Infallible);
3832 }
3833 }
3834
3835 fn IsQuery(&self, query: Option<&WebGLQuery>) -> bool {
3837 match query {
3838 Some(query) => self.base.validate_ownership(query).is_ok() && query.is_valid(),
3839 None => false,
3840 }
3841 }
3842
3843 fn CreateSampler(&self) -> Option<DomRoot<WebGLSampler>> {
3845 Some(WebGLSampler::new(&self.base, CanGc::note()))
3846 }
3847
3848 fn DeleteSampler(&self, sampler: Option<&WebGLSampler>) {
3850 if let Some(sampler) = sampler {
3851 handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
3852 for slot in self.samplers.iter() {
3853 if slot.get().is_some_and(|s| sampler == &*s) {
3854 slot.set(None);
3855 }
3856 }
3857 sampler.delete(Operation::Infallible);
3858 }
3859 }
3860
3861 fn IsSampler(&self, sampler: Option<&WebGLSampler>) -> bool {
3863 match sampler {
3864 Some(sampler) => self.base.validate_ownership(sampler).is_ok() && sampler.is_valid(),
3865 None => false,
3866 }
3867 }
3868
3869 #[rustfmt::skip]
3871 fn BeginQuery(&self, target: u32, query: &WebGLQuery) {
3872 handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
3873
3874 let active_query = match target {
3875 constants::ANY_SAMPLES_PASSED |
3876 constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3877 &self.occlusion_query
3878 },
3879 constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3880 &self.primitives_query
3881 },
3882 _ => {
3883 self.base.webgl_error(InvalidEnum);
3884 return;
3885 },
3886 };
3887 if active_query.get().is_some() {
3888 self.base.webgl_error(InvalidOperation);
3889 return;
3890 }
3891 let result = query.begin(&self.base, target);
3892 match result {
3893 Ok(_) => active_query.set(Some(query)),
3894 Err(error) => self.base.webgl_error(error),
3895 }
3896 }
3897
3898 #[rustfmt::skip]
3900 fn EndQuery(&self, target: u32) {
3901 let active_query = match target {
3902 constants::ANY_SAMPLES_PASSED |
3903 constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3904 self.occlusion_query.take()
3905 },
3906 constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3907 self.primitives_query.take()
3908 },
3909 _ => {
3910 self.base.webgl_error(InvalidEnum);
3911 return;
3912 },
3913 };
3914 match active_query {
3915 None => self.base.webgl_error(InvalidOperation),
3916 Some(query) => {
3917 let result = query.end(&self.base, target);
3918 if let Err(error) = result {
3919 self.base.webgl_error(error);
3920 }
3921 },
3922 }
3923 }
3924
3925 #[rustfmt::skip]
3927 fn GetQuery(&self, target: u32, pname: u32) -> Option<DomRoot<WebGLQuery>> {
3928 if pname != constants::CURRENT_QUERY {
3929 self.base.webgl_error(InvalidEnum);
3930 return None;
3931 }
3932 let active_query = match target {
3933 constants::ANY_SAMPLES_PASSED |
3934 constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3935 self.occlusion_query.get()
3936 },
3937 constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3938 self.primitives_query.get()
3939 },
3940 _ => {
3941 self.base.webgl_error(InvalidEnum);
3942 None
3943 },
3944 };
3945 if let Some(query) = active_query.as_ref() {
3946 if query.target() != Some(target) {
3947 return None;
3948 }
3949 }
3950 active_query
3951 }
3952
3953 #[rustfmt::skip]
3955 fn GetQueryParameter(&self, _cx: JSContext, query: &WebGLQuery, pname: u32, mut retval: MutableHandleValue) {
3956 handle_potential_webgl_error!(
3957 self.base,
3958 self.base.validate_ownership(query),
3959 return retval.set(NullValue())
3960 );
3961 match query.get_parameter(&self.base, pname) {
3962 Ok(value) => match pname {
3963 constants::QUERY_RESULT => retval.set(UInt32Value(value)),
3964 constants::QUERY_RESULT_AVAILABLE => retval.set(BooleanValue(value != 0)),
3965 _ => unreachable!(),
3966 },
3967 Err(error) => {
3968 self.base.webgl_error(error);
3969 retval.set(NullValue())
3970 },
3971 }
3972 }
3973
3974 fn FenceSync(&self, condition: u32, flags: u32) -> Option<DomRoot<WebGLSync>> {
3976 if flags != 0 {
3977 self.base.webgl_error(InvalidValue);
3978 return None;
3979 }
3980 if condition != constants::SYNC_GPU_COMMANDS_COMPLETE {
3981 self.base.webgl_error(InvalidEnum);
3982 return None;
3983 }
3984
3985 Some(WebGLSync::new(&self.base, CanGc::note()))
3986 }
3987
3988 fn IsSync(&self, sync: Option<&WebGLSync>) -> bool {
3990 match sync {
3991 Some(sync) => {
3992 if self.base.validate_ownership(sync).is_err() {
3995 return false;
3996 }
3997
3998 if !sync.is_valid() {
4000 return false;
4001 }
4002
4003 handle_potential_webgl_error!(
4005 self.base,
4006 self.base.validate_ownership(sync),
4007 return false
4008 );
4009 let (sender, receiver) = webgl_channel().unwrap();
4010 self.base
4011 .send_command(WebGLCommand::IsSync(sync.id(), sender));
4012 receiver.recv().unwrap()
4013 },
4014 None => false,
4015 }
4016 }
4017
4018 fn ClientWaitSync(&self, sync: &WebGLSync, flags: u32, timeout: u64) -> u32 {
4020 if !sync.is_valid() {
4021 self.base.webgl_error(InvalidOperation);
4022 return constants::WAIT_FAILED;
4023 }
4024 handle_potential_webgl_error!(
4025 self.base,
4026 self.base.validate_ownership(sync),
4027 return constants::WAIT_FAILED
4028 );
4029 if flags != 0 && flags != constants::SYNC_FLUSH_COMMANDS_BIT {
4030 self.base.webgl_error(InvalidValue);
4031 return constants::WAIT_FAILED;
4032 }
4033 if timeout > self.base.limits().max_client_wait_timeout_webgl.as_nanos() as u64 {
4034 self.base.webgl_error(InvalidOperation);
4035 return constants::WAIT_FAILED;
4036 }
4037
4038 match sync.client_wait_sync(&self.base, flags, timeout) {
4039 Some(status) => status,
4040 None => constants::WAIT_FAILED,
4041 }
4042 }
4043
4044 fn WaitSync(&self, sync: &WebGLSync, flags: u32, timeout: i64) {
4046 if !sync.is_valid() {
4047 self.base.webgl_error(InvalidOperation);
4048 return;
4049 }
4050 handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
4051 if flags != 0 {
4052 self.base.webgl_error(InvalidValue);
4053 return;
4054 }
4055 if timeout != constants::TIMEOUT_IGNORED {
4056 self.base.webgl_error(InvalidValue);
4057 return;
4058 }
4059
4060 self.base
4061 .send_command(WebGLCommand::WaitSync(sync.id(), flags, timeout));
4062 }
4063
4064 fn GetSyncParameter(
4066 &self,
4067 _cx: JSContext,
4068 sync: &WebGLSync,
4069 pname: u32,
4070 mut retval: MutableHandleValue,
4071 ) {
4072 if !sync.is_valid() {
4073 self.base.webgl_error(InvalidOperation);
4074 return retval.set(NullValue());
4075 }
4076 handle_potential_webgl_error!(
4077 self.base,
4078 self.base.validate_ownership(sync),
4079 return retval.set(NullValue())
4080 );
4081 match pname {
4082 constants::OBJECT_TYPE | constants::SYNC_CONDITION | constants::SYNC_FLAGS => {
4083 let (sender, receiver) = webgl_channel().unwrap();
4084 self.base
4085 .send_command(WebGLCommand::GetSyncParameter(sync.id(), pname, sender));
4086 retval.set(UInt32Value(receiver.recv().unwrap()))
4087 },
4088 constants::SYNC_STATUS => match sync.get_sync_status(pname, &self.base) {
4089 Some(status) => retval.set(UInt32Value(status)),
4090 None => retval.set(UInt32Value(constants::UNSIGNALED)),
4091 },
4092 _ => {
4093 self.base.webgl_error(InvalidEnum);
4094 retval.set(NullValue())
4095 },
4096 }
4097 }
4098
4099 fn DeleteSync(&self, sync: Option<&WebGLSync>) {
4101 if let Some(sync) = sync {
4102 handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
4103 sync.delete(Operation::Infallible);
4104 }
4105 }
4106
4107 fn BindSampler(&self, unit: u32, sampler: Option<&WebGLSampler>) {
4109 if let Some(sampler) = sampler {
4110 handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4111
4112 if unit as usize >= self.samplers.len() {
4113 self.base.webgl_error(InvalidValue);
4114 return;
4115 }
4116
4117 let result = sampler.bind(&self.base, unit);
4118 match result {
4119 Ok(_) => self.samplers[unit as usize].set(Some(sampler)),
4120 Err(error) => self.base.webgl_error(error),
4121 }
4122 }
4123 }
4124
4125 fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) {
4127 self.base.bind_vertex_array_webgl2(array);
4128 }
4129
4130 fn SamplerParameteri(&self, sampler: &WebGLSampler, pname: u32, param: i32) {
4132 handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4133 let param = WebGLSamplerValue::GLenum(param as u32);
4134 let result = sampler.set_parameter(&self.base, pname, param);
4135 if let Err(error) = result {
4136 self.base.webgl_error(error);
4137 }
4138 }
4139
4140 fn SamplerParameterf(&self, sampler: &WebGLSampler, pname: u32, param: f32) {
4142 handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4143 let param = WebGLSamplerValue::Float(param);
4144 let result = sampler.set_parameter(&self.base, pname, param);
4145 if let Err(error) = result {
4146 self.base.webgl_error(error);
4147 }
4148 }
4149
4150 fn GetSamplerParameter(
4152 &self,
4153 _cx: JSContext,
4154 sampler: &WebGLSampler,
4155 pname: u32,
4156 mut retval: MutableHandleValue,
4157 ) {
4158 handle_potential_webgl_error!(
4159 self.base,
4160 self.base.validate_ownership(sampler),
4161 return retval.set(NullValue())
4162 );
4163 match sampler.get_parameter(&self.base, pname) {
4164 Ok(value) => match value {
4165 WebGLSamplerValue::GLenum(value) => retval.set(UInt32Value(value)),
4166 WebGLSamplerValue::Float(value) => retval.set(DoubleValue(value as f64)),
4167 },
4168 Err(error) => {
4169 self.base.webgl_error(error);
4170 retval.set(NullValue())
4171 },
4172 }
4173 }
4174
4175 fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> {
4177 Some(WebGLTransformFeedback::new(&self.base, CanGc::note()))
4178 }
4179
4180 fn DeleteTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) {
4182 if let Some(tf) = tf {
4183 handle_potential_webgl_error!(self.base, self.base.validate_ownership(tf), return);
4184 if tf.is_active() {
4185 self.base.webgl_error(InvalidOperation);
4186 return;
4187 }
4188 tf.delete(Operation::Infallible);
4189 self.current_transform_feedback.set(None);
4190 }
4191 }
4192
4193 fn IsTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) -> bool {
4195 match tf {
4196 Some(tf) => {
4197 if self.base.validate_ownership(tf).is_err() {
4200 return false;
4201 }
4202
4203 if !tf.is_valid() {
4205 return false;
4206 }
4207
4208 handle_potential_webgl_error!(
4210 self.base,
4211 self.base.validate_ownership(tf),
4212 return false
4213 );
4214 let (sender, receiver) = webgl_channel().unwrap();
4215 self.base
4216 .send_command(WebGLCommand::IsTransformFeedback(tf.id(), sender));
4217 receiver.recv().unwrap()
4218 },
4219 None => false,
4221 }
4222 }
4223
4224 fn BindTransformFeedback(&self, target: u32, tf: Option<&WebGLTransformFeedback>) {
4226 if target != constants::TRANSFORM_FEEDBACK {
4227 self.base.webgl_error(InvalidEnum);
4228 return;
4229 }
4230 match tf {
4231 Some(transform_feedback) => {
4232 handle_potential_webgl_error!(
4233 self.base,
4234 self.base.validate_ownership(transform_feedback),
4235 return
4236 );
4237 if !transform_feedback.is_valid() {
4238 self.base.webgl_error(InvalidOperation);
4239 return;
4240 }
4241 if let Some(current_tf) = self.current_transform_feedback.get() {
4242 if current_tf.is_active() && !current_tf.is_paused() {
4243 self.base.webgl_error(InvalidOperation);
4244 return;
4245 }
4246 }
4247 transform_feedback.bind(&self.base, target);
4248 self.current_transform_feedback
4249 .set(Some(transform_feedback));
4250 },
4251 None => self
4252 .base
4253 .send_command(WebGLCommand::BindTransformFeedback(target, 0)),
4254 }
4255 }
4256
4257 #[expect(non_snake_case)]
4259 fn BeginTransformFeedback(&self, primitiveMode: u32) {
4260 match primitiveMode {
4261 constants::POINTS | constants::LINES | constants::TRIANGLES => {},
4262 _ => {
4263 self.base.webgl_error(InvalidEnum);
4264 return;
4265 },
4266 };
4267 let current_tf = match self.current_transform_feedback.get() {
4268 Some(current_tf) => current_tf,
4269 None => {
4270 self.base.webgl_error(InvalidOperation);
4271 return;
4272 },
4273 };
4274 if current_tf.is_active() {
4275 self.base.webgl_error(InvalidOperation);
4276 return;
4277 };
4278 let program = match self.base.current_program() {
4279 Some(program) => program,
4280 None => {
4281 self.base.webgl_error(InvalidOperation);
4282 return;
4283 },
4284 };
4285 if !program.is_linked() || program.transform_feedback_varyings_length() == 0 {
4286 self.base.webgl_error(InvalidOperation);
4287 return;
4288 };
4289 current_tf.begin(&self.base, primitiveMode);
4290 }
4291
4292 fn EndTransformFeedback(&self) {
4294 if let Some(current_tf) = self.current_transform_feedback.get() {
4295 if !current_tf.is_active() {
4296 self.base.webgl_error(InvalidOperation);
4297 return;
4298 }
4299 current_tf.end(&self.base);
4300 }
4301 }
4302
4303 fn ResumeTransformFeedback(&self) {
4305 if let Some(current_tf) = self.current_transform_feedback.get() {
4306 if !current_tf.is_active() || !current_tf.is_paused() {
4307 self.base.webgl_error(InvalidOperation);
4308 return;
4309 }
4310 current_tf.resume(&self.base);
4311 }
4312 }
4313
4314 fn PauseTransformFeedback(&self) {
4316 if let Some(current_tf) = self.current_transform_feedback.get() {
4317 if !current_tf.is_active() || current_tf.is_paused() {
4318 self.base.webgl_error(InvalidOperation);
4319 return;
4320 }
4321 current_tf.pause(&self.base);
4322 }
4323 }
4324
4325 #[expect(non_snake_case)]
4327 fn TransformFeedbackVaryings(
4328 &self,
4329 program: &WebGLProgram,
4330 varyings: Vec<DOMString>,
4331 bufferMode: u32,
4332 ) {
4333 if let Err(error) = self.base.validate_ownership(program) {
4336 self.base.webgl_error(error);
4337 return;
4338 }
4339 handle_potential_webgl_error!(self.base, program.validate(), return);
4340 let strs = varyings
4341 .iter()
4342 .map(|name| String::from(name.to_owned()))
4343 .collect::<Vec<String>>();
4344 match bufferMode {
4345 constants::INTERLEAVED_ATTRIBS => {
4346 self.base
4347 .send_command(WebGLCommand::TransformFeedbackVaryings(
4348 program.id(),
4349 strs,
4350 bufferMode,
4351 ));
4352 },
4353 constants::SEPARATE_ATTRIBS => {
4354 let max_tf_sp_att =
4355 self.base.limits().max_transform_feedback_separate_attribs as usize;
4356 if strs.len() >= max_tf_sp_att {
4357 self.base.webgl_error(InvalidValue);
4358 return;
4359 }
4360 self.base
4361 .send_command(WebGLCommand::TransformFeedbackVaryings(
4362 program.id(),
4363 strs,
4364 bufferMode,
4365 ));
4366 },
4367 _ => self.base.webgl_error(InvalidEnum),
4368 }
4369 }
4370
4371 fn GetTransformFeedbackVarying(
4373 &self,
4374 program: &WebGLProgram,
4375 index: u32,
4376 ) -> Option<DomRoot<WebGLActiveInfo>> {
4377 if let Err(error) = self.base.validate_ownership(program) {
4380 self.base.webgl_error(error);
4381 return None;
4382 }
4383
4384 handle_potential_webgl_error!(self.base, program.validate(), return None);
4385
4386 if index >= program.transform_feedback_varyings_length() as u32 {
4387 self.base.webgl_error(InvalidValue);
4388 return None;
4389 }
4390
4391 let (sender, receiver) = webgl_channel().unwrap();
4392 self.base
4393 .send_command(WebGLCommand::GetTransformFeedbackVarying(
4394 program.id(),
4395 index,
4396 sender,
4397 ));
4398 let (size, ty, name) = receiver.recv().unwrap();
4399 Some(WebGLActiveInfo::new(
4400 self.base.global().as_window(),
4401 size,
4402 ty,
4403 DOMString::from(name),
4404 CanGc::note(),
4405 ))
4406 }
4407
4408 fn BindBufferBase(&self, target: u32, index: u32, buffer: Option<&WebGLBuffer>) {
4410 let (generic_slot, indexed_bindings) = match target {
4411 constants::TRANSFORM_FEEDBACK_BUFFER => (
4412 &self.bound_transform_feedback_buffer,
4413 &self.indexed_transform_feedback_buffer_bindings,
4414 ),
4415 constants::UNIFORM_BUFFER => (
4416 &self.bound_uniform_buffer,
4417 &self.indexed_uniform_buffer_bindings,
4418 ),
4419 _ => return self.base.webgl_error(InvalidEnum),
4420 };
4421 let indexed_binding = match indexed_bindings.get(index as usize) {
4422 Some(slot) => slot,
4423 None => return self.base.webgl_error(InvalidValue),
4424 };
4425
4426 if let Some(buffer) = buffer {
4427 handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
4428
4429 if buffer.is_marked_for_deletion() {
4430 return self.base.webgl_error(InvalidOperation);
4431 }
4432 handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
4433
4434 buffer.increment_attached_counter();
4436 buffer.increment_attached_counter();
4437 }
4438
4439 self.base.send_command(WebGLCommand::BindBufferBase(
4440 target,
4441 index,
4442 buffer.map(|b| b.id()),
4443 ));
4444
4445 for slot in &[generic_slot, &indexed_binding.buffer] {
4446 if let Some(old) = slot.get() {
4447 old.decrement_attached_counter(Operation::Infallible);
4448 }
4449 slot.set(buffer);
4450 }
4451 indexed_binding.start.set(0);
4452 indexed_binding.size.set(0);
4453 }
4454
4455 fn BindBufferRange(
4457 &self,
4458 target: u32,
4459 index: u32,
4460 buffer: Option<&WebGLBuffer>,
4461 offset: i64,
4462 size: i64,
4463 ) {
4464 let (generic_slot, indexed_bindings) = match target {
4465 constants::TRANSFORM_FEEDBACK_BUFFER => (
4466 &self.bound_transform_feedback_buffer,
4467 &self.indexed_transform_feedback_buffer_bindings,
4468 ),
4469 constants::UNIFORM_BUFFER => (
4470 &self.bound_uniform_buffer,
4471 &self.indexed_uniform_buffer_bindings,
4472 ),
4473 _ => return self.base.webgl_error(InvalidEnum),
4474 };
4475 let indexed_binding = match indexed_bindings.get(index as usize) {
4476 Some(slot) => slot,
4477 None => return self.base.webgl_error(InvalidValue),
4478 };
4479
4480 if offset < 0 || size < 0 {
4481 return self.base.webgl_error(InvalidValue);
4482 }
4483 if buffer.is_some() && size == 0 {
4484 return self.base.webgl_error(InvalidValue);
4485 }
4486
4487 match target {
4488 constants::TRANSFORM_FEEDBACK_BUFFER => {
4489 if size % 4 != 0 && offset % 4 != 0 {
4490 return self.base.webgl_error(InvalidValue);
4491 }
4492 },
4493 constants::UNIFORM_BUFFER => {
4494 let offset_alignment = self.base.limits().uniform_buffer_offset_alignment;
4495 if offset % offset_alignment as i64 != 0 {
4496 return self.base.webgl_error(InvalidValue);
4497 }
4498 },
4499 _ => unreachable!(),
4500 }
4501
4502 if let Some(buffer) = buffer {
4503 handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
4504
4505 if buffer.is_marked_for_deletion() {
4506 return self.base.webgl_error(InvalidOperation);
4507 }
4508 handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
4509
4510 buffer.increment_attached_counter();
4512 buffer.increment_attached_counter();
4513 }
4514
4515 self.base.send_command(WebGLCommand::BindBufferRange(
4516 target,
4517 index,
4518 buffer.map(|b| b.id()),
4519 offset,
4520 size,
4521 ));
4522
4523 for slot in &[generic_slot, &indexed_binding.buffer] {
4524 if let Some(old) = slot.get() {
4525 old.decrement_attached_counter(Operation::Infallible);
4526 }
4527 slot.set(buffer);
4528 }
4529 indexed_binding.start.set(offset);
4530 indexed_binding.size.set(size);
4531 }
4532
4533 fn GetUniformIndices(&self, program: &WebGLProgram, names: Vec<DOMString>) -> Option<Vec<u32>> {
4535 handle_potential_webgl_error!(
4536 self.base,
4537 self.base.validate_ownership(program),
4538 return None
4539 );
4540 let indices = handle_potential_webgl_error!(
4541 self.base,
4542 program.get_uniform_indices(names),
4543 return None
4544 );
4545 Some(indices)
4546 }
4547
4548 fn GetActiveUniforms(
4550 &self,
4551 cx: JSContext,
4552 program: &WebGLProgram,
4553 indices: Vec<u32>,
4554 pname: u32,
4555 mut rval: MutableHandleValue,
4556 ) {
4557 handle_potential_webgl_error!(
4558 self.base,
4559 self.base.validate_ownership(program),
4560 return rval.set(NullValue())
4561 );
4562 let values = handle_potential_webgl_error!(
4563 self.base,
4564 program.get_active_uniforms(indices, pname),
4565 return rval.set(NullValue())
4566 );
4567
4568 match pname {
4569 constants::UNIFORM_SIZE |
4570 constants::UNIFORM_TYPE |
4571 constants::UNIFORM_BLOCK_INDEX |
4572 constants::UNIFORM_OFFSET |
4573 constants::UNIFORM_ARRAY_STRIDE |
4574 constants::UNIFORM_MATRIX_STRIDE => {
4575 values.safe_to_jsval(cx, rval, CanGc::note());
4576 },
4577 constants::UNIFORM_IS_ROW_MAJOR => {
4578 let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>();
4579 values.safe_to_jsval(cx, rval, CanGc::note());
4580 },
4581 _ => unreachable!(),
4582 }
4583 }
4584
4585 fn GetUniformBlockIndex(&self, program: &WebGLProgram, block_name: DOMString) -> u32 {
4587 handle_potential_webgl_error!(
4588 self.base,
4589 self.base.validate_ownership(program),
4590 return constants::INVALID_INDEX
4591 );
4592 handle_potential_webgl_error!(
4593 self.base,
4594 program.get_uniform_block_index(block_name),
4595 constants::INVALID_INDEX
4596 )
4597 }
4598
4599 #[expect(unsafe_code)]
4601 fn GetActiveUniformBlockParameter(
4602 &self,
4603 cx: JSContext,
4604 program: &WebGLProgram,
4605 block_index: u32,
4606 pname: u32,
4607 mut retval: MutableHandleValue,
4608 ) {
4609 handle_potential_webgl_error!(
4610 self.base,
4611 self.base.validate_ownership(program),
4612 return retval.set(NullValue())
4613 );
4614 let values = handle_potential_webgl_error!(
4615 self.base,
4616 program.get_active_uniform_block_parameter(block_index, pname),
4617 return retval.set(NullValue())
4618 );
4619 match pname {
4620 constants::UNIFORM_BLOCK_BINDING |
4621 constants::UNIFORM_BLOCK_DATA_SIZE |
4622 constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => {
4623 assert!(values.len() == 1);
4624 retval.set(UInt32Value(values[0] as u32))
4625 },
4626 constants::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
4627 let values = values.iter().map(|&v| v as u32).collect::<Vec<_>>();
4628 rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
4629 Uint32Array::create(*cx, CreateWith::Slice(&values), result.handle_mut()).unwrap();
4630 retval.set(ObjectValue(result.get()))
4631 },
4632 constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
4633 constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {
4634 assert!(values.len() == 1);
4635 retval.set(BooleanValue(values[0] != 0))
4636 },
4637 _ => unreachable!(),
4638 }
4639 }
4640
4641 fn GetActiveUniformBlockName(
4643 &self,
4644 program: &WebGLProgram,
4645 block_index: u32,
4646 ) -> Option<DOMString> {
4647 handle_potential_webgl_error!(
4648 self.base,
4649 self.base.validate_ownership(program),
4650 return None
4651 );
4652 let name = handle_potential_webgl_error!(
4653 self.base,
4654 program.get_active_uniform_block_name(block_index),
4655 return None
4656 );
4657 Some(DOMString::from(name))
4658 }
4659
4660 fn UniformBlockBinding(&self, program: &WebGLProgram, block_index: u32, block_binding: u32) {
4662 handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return);
4663
4664 if block_binding >= self.base.limits().max_uniform_buffer_bindings {
4665 return self.base.webgl_error(InvalidValue);
4666 }
4667
4668 handle_potential_webgl_error!(
4669 self.base,
4670 program.bind_uniform_block(block_index, block_binding)
4671 )
4672 }
4673
4674 fn ClearBufferfv(
4676 &self,
4677 buffer: u32,
4678 draw_buffer: i32,
4679 values: Float32ArrayOrUnrestrictedFloatSequence,
4680 src_offset: u32,
4681 ) {
4682 let array = match values {
4683 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4684 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4685 };
4686 self.clear_buffer::<f32>(
4687 buffer,
4688 draw_buffer,
4689 &[constants::COLOR, constants::DEPTH],
4690 src_offset,
4691 array,
4692 WebGLCommand::ClearBufferfv,
4693 )
4694 }
4695
4696 fn ClearBufferiv(
4698 &self,
4699 buffer: u32,
4700 draw_buffer: i32,
4701 values: Int32ArrayOrLongSequence,
4702 src_offset: u32,
4703 ) {
4704 let array = match values {
4705 Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
4706 Int32ArrayOrLongSequence::LongSequence(v) => v,
4707 };
4708 self.clear_buffer::<i32>(
4709 buffer,
4710 draw_buffer,
4711 &[constants::COLOR, constants::STENCIL],
4712 src_offset,
4713 array,
4714 WebGLCommand::ClearBufferiv,
4715 )
4716 }
4717
4718 fn ClearBufferuiv(
4720 &self,
4721 buffer: u32,
4722 draw_buffer: i32,
4723 values: Uint32ArrayOrUnsignedLongSequence,
4724 src_offset: u32,
4725 ) {
4726 let array = match values {
4727 Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
4728 Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
4729 };
4730 self.clear_buffer::<u32>(
4731 buffer,
4732 draw_buffer,
4733 &[constants::COLOR],
4734 src_offset,
4735 array,
4736 WebGLCommand::ClearBufferuiv,
4737 )
4738 }
4739
4740 fn ClearBufferfi(&self, buffer: u32, draw_buffer: i32, depth: f32, stencil: i32) {
4742 if buffer != constants::DEPTH_STENCIL {
4743 return self.base.webgl_error(InvalidEnum);
4744 }
4745
4746 handle_potential_webgl_error!(
4747 self.base,
4748 self.clearbuffer_array_size(buffer, draw_buffer),
4749 return
4750 );
4751
4752 self.base.send_command(WebGLCommand::ClearBufferfi(
4753 buffer,
4754 draw_buffer,
4755 depth,
4756 stencil,
4757 ));
4758 }
4759
4760 fn InvalidateFramebuffer(&self, target: u32, attachments: Vec<u32>) {
4762 if !self.valid_fb_attachment_values(target, &attachments) {
4763 return;
4764 }
4765
4766 self.base
4767 .send_command(WebGLCommand::InvalidateFramebuffer(target, attachments))
4768 }
4769
4770 fn InvalidateSubFramebuffer(
4772 &self,
4773 target: u32,
4774 attachments: Vec<u32>,
4775 x: i32,
4776 y: i32,
4777 width: i32,
4778 height: i32,
4779 ) {
4780 if !self.valid_fb_attachment_values(target, &attachments) {
4781 return;
4782 }
4783
4784 if width < 0 || height < 0 {
4785 return self.base.webgl_error(InvalidValue);
4786 }
4787
4788 self.base
4789 .send_command(WebGLCommand::InvalidateSubFramebuffer(
4790 target,
4791 attachments,
4792 x,
4793 y,
4794 width,
4795 height,
4796 ))
4797 }
4798
4799 fn FramebufferTextureLayer(
4801 &self,
4802 target: u32,
4803 attachment: u32,
4804 texture: Option<&WebGLTexture>,
4805 level: i32,
4806 layer: i32,
4807 ) {
4808 if let Some(tex) = texture {
4809 handle_potential_webgl_error!(self.base, self.base.validate_ownership(tex), return);
4810 }
4811
4812 let fb_slot = match target {
4813 constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
4814 self.base.get_draw_framebuffer_slot()
4815 },
4816 constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
4817 _ => return self.base.webgl_error(InvalidEnum),
4818 };
4819
4820 match fb_slot.get() {
4821 Some(fb) => handle_potential_webgl_error!(
4822 self.base,
4823 fb.texture_layer(attachment, texture, level, layer)
4824 ),
4825 None => self.base.webgl_error(InvalidOperation),
4826 }
4827 }
4828
4829 #[expect(unsafe_code)]
4831 fn GetInternalformatParameter(
4832 &self,
4833 cx: JSContext,
4834 target: u32,
4835 internal_format: u32,
4836 pname: u32,
4837 mut retval: MutableHandleValue,
4838 ) {
4839 if target != constants::RENDERBUFFER {
4840 self.base.webgl_error(InvalidEnum);
4841 return retval.set(NullValue());
4842 }
4843
4844 match handle_potential_webgl_error!(
4845 self.base,
4846 InternalFormatParameter::from_u32(pname),
4847 return retval.set(NullValue())
4848 ) {
4849 InternalFormatParameter::IntVec(param) => unsafe {
4850 let (sender, receiver) = webgl_channel().unwrap();
4851 self.base
4852 .send_command(WebGLCommand::GetInternalFormatIntVec(
4853 target,
4854 internal_format,
4855 param,
4856 sender,
4857 ));
4858
4859 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
4860 Int32Array::create(
4861 *cx,
4862 CreateWith::Slice(&receiver.recv().unwrap()),
4863 rval.handle_mut(),
4864 )
4865 .unwrap();
4866 retval.set(ObjectValue(rval.get()))
4867 },
4868 }
4869 }
4870
4871 fn RenderbufferStorageMultisample(
4873 &self,
4874 target: u32,
4875 samples: i32,
4876 internal_format: u32,
4877 width: i32,
4878 height: i32,
4879 ) {
4880 self.base
4881 .renderbuffer_storage(target, samples, internal_format, width, height)
4882 }
4883
4884 fn ReadBuffer(&self, src: u32) {
4886 match src {
4887 constants::BACK | constants::NONE => {},
4888 _ if self.base.valid_color_attachment_enum(src) => {},
4889 _ => return self.base.webgl_error(InvalidEnum),
4890 }
4891
4892 if let Some(fb) = self.base.get_read_framebuffer_slot().get() {
4893 handle_potential_webgl_error!(self.base, fb.set_read_buffer(src))
4894 } else {
4895 match src {
4896 constants::NONE | constants::BACK => {},
4897 _ => return self.base.webgl_error(InvalidOperation),
4898 }
4899
4900 self.default_fb_readbuffer.set(src);
4901 self.base.send_command(WebGLCommand::ReadBuffer(src));
4902 }
4903 }
4904
4905 fn DrawBuffers(&self, buffers: Vec<u32>) {
4907 if let Some(fb) = self.base.get_draw_framebuffer_slot().get() {
4908 handle_potential_webgl_error!(self.base, fb.set_draw_buffers(buffers))
4909 } else {
4910 if buffers.len() != 1 {
4911 return self.base.webgl_error(InvalidOperation);
4912 }
4913
4914 match buffers[0] {
4915 constants::NONE | constants::BACK => {},
4916 _ => return self.base.webgl_error(InvalidOperation),
4917 }
4918
4919 self.default_fb_drawbuffer.set(buffers[0]);
4920 self.base.send_command(WebGLCommand::DrawBuffers(buffers));
4921 }
4922 }
4923
4924 fn TexStorage2D(
4926 &self,
4927 target: u32,
4928 levels: i32,
4929 internal_format: u32,
4930 width: i32,
4931 height: i32,
4932 ) {
4933 self.tex_storage(2, target, levels, internal_format, width, height, 1)
4934 }
4935
4936 fn TexStorage3D(
4938 &self,
4939 target: u32,
4940 levels: i32,
4941 internal_format: u32,
4942 width: i32,
4943 height: i32,
4944 depth: i32,
4945 ) {
4946 self.tex_storage(3, target, levels, internal_format, width, height, depth)
4947 }
4948
4949 #[cfg(feature = "webxr")]
4951 fn MakeXRCompatible(&self, can_gc: CanGc) -> Rc<Promise> {
4952 let p = Promise::new(&self.global(), can_gc);
4954 p.resolve_native(&(), can_gc);
4955 p
4956 }
4957}
4958
4959impl WebGL2RenderingContextHelpers for WebGL2RenderingContext {
4960 fn is_webgl2_enabled(cx: JSContext, global: HandleObject) -> bool {
4961 Self::is_webgl2_enabled(cx, global)
4962 }
4963}