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