1use std::cell::Cell;
6use std::cmp;
7use std::ptr::{self, NonNull};
8#[cfg(feature = "webxr")]
9use std::rc::Rc;
10
11#[cfg(feature = "webgl_backtrace")]
12use backtrace::Backtrace;
13use bitflags::bitflags;
14use dom_struct::dom_struct;
15use euclid::default::{Point2D, Rect, Size2D};
16use js::jsapi::{JSContext, JSObject, Type};
17use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
18use js::rust::{CustomAutoRooterGuard, MutableHandleValue};
19use js::typedarray::{
20 ArrayBufferView, CreateWith, Float32, Float32Array, Int32, Int32Array, TypedArray,
21 TypedArrayElementCreator, Uint32Array,
22};
23use pixels::{self, Alpha, PixelFormat, Snapshot, SnapshotPixelFormat};
24use script_bindings::cell::{DomRefCell, Ref, RefMut};
25use script_bindings::conversions::SafeToJSValConvertible;
26use script_bindings::reflector::{AssociatedMemory, Reflector, reflect_dom_object_with_cx};
27use serde::{Deserialize, Serialize};
28use servo_base::generic_channel::GenericSharedMemory;
29use servo_base::{Epoch, generic_channel};
30use servo_canvas_traits::webgl::WebGLError::*;
31use servo_canvas_traits::webgl::{
32 AlphaTreatment, GLContextAttributes, GLLimits, GlType, Parameter, SizedDataType, TexDataType,
33 TexFormat, TexParameter, WebGLCommand, WebGLCommandBacktrace, WebGLContextId,
34 WebGLCreateContextResult, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender,
35 WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLVersion, YAxisTreatment, webgl_channel,
36};
37use servo_config::pref;
38use webrender_api::ImageKey;
39
40use crate::canvas_context::{CanvasContext, HTMLCanvasElementOrOffscreenCanvas};
41use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
42use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
43use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
44use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants;
45use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{
46 TexImageSource, WebGLContextAttributes, WebGLRenderingContextConstants as constants,
47 WebGLRenderingContextMethods,
48};
49use crate::dom::bindings::codegen::UnionTypes::{
50 ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence,
51 HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas,
52 Int32ArrayOrLongSequence,
53};
54use crate::dom::bindings::conversions::DerivedFrom;
55use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
56use crate::dom::bindings::inheritance::Castable;
57use crate::dom::bindings::reflector::DomGlobal;
58use crate::dom::bindings::root::{DomOnceCell, DomRoot, MutNullableDom};
59use crate::dom::bindings::str::DOMString;
60use crate::dom::event::{Event, EventBubbles, EventCancelable};
61#[cfg(feature = "webgl_backtrace")]
62use crate::dom::globalscope::GlobalScope;
63use crate::dom::node::NodeTraits;
64#[cfg(feature = "webxr")]
65use crate::dom::promise::Promise;
66use crate::dom::webgl::extensions::WebGLExtensions;
67use crate::dom::webgl::validations::WebGLValidator;
68use crate::dom::webgl::validations::tex_image_2d::{
69 CommonCompressedTexImage2DValidatorResult, CommonTexImage2DValidator,
70 CommonTexImage2DValidatorResult, CompressedTexImage2DValidator,
71 CompressedTexSubImage2DValidator, TexImage2DValidator, TexImage2DValidatorResult,
72};
73use crate::dom::webgl::validations::types::TexImageTarget;
74use crate::dom::webgl::vertexarrayobject::VertexAttribData;
75use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
76use crate::dom::webgl::webglbuffer::WebGLBuffer;
77use crate::dom::webgl::webglcontextevent::WebGLContextEvent;
78use crate::dom::webgl::webglframebuffer::{
79 CompleteForRendering, WebGLFramebuffer, WebGLFramebufferAttachmentRoot,
80};
81use crate::dom::webgl::webglobject::WebGLObject;
82use crate::dom::webgl::webglprogram::WebGLProgram;
83use crate::dom::webgl::webglrenderbuffer::WebGLRenderbuffer;
84use crate::dom::webgl::webglshader::WebGLShader;
85use crate::dom::webgl::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
86use crate::dom::webgl::webgltexture::{TexParameterValue, WebGLTexture};
87use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
88use crate::dom::webgl::webglvertexarrayobject::WebGLVertexArrayObject;
89use crate::dom::webgl::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
90use crate::dom::window::Window;
91use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
92
93macro_rules! handle_object_deletion {
102 ($self_:expr, $binding:expr, $object:ident, $unbind_command:expr) => {
103 if let Some(bound_object) = $binding.get() {
104 if bound_object.id() == $object.id() {
105 $binding.set(None);
106 if let Some(command) = $unbind_command {
107 $self_.send_command(command);
108 }
109 }
110 }
111 };
112}
113
114fn has_invalid_blend_constants(arg1: u32, arg2: u32) -> bool {
115 match (arg1, arg2) {
116 (constants::CONSTANT_COLOR, constants::CONSTANT_ALPHA) => true,
117 (constants::ONE_MINUS_CONSTANT_COLOR, constants::ONE_MINUS_CONSTANT_ALPHA) => true,
118 (constants::ONE_MINUS_CONSTANT_COLOR, constants::CONSTANT_ALPHA) => true,
119 (constants::CONSTANT_COLOR, constants::ONE_MINUS_CONSTANT_ALPHA) => true,
120 (_, _) => false,
121 }
122}
123
124pub(crate) fn uniform_get<T, F>(triple: (&WebGLRenderingContext, WebGLProgramId, i32), f: F) -> T
125where
126 F: FnOnce(WebGLProgramId, i32, generic_channel::GenericSender<T>) -> WebGLCommand,
127 T: for<'de> Deserialize<'de> + Serialize,
128{
129 let (sender, receiver) = webgl_channel().unwrap();
130 triple.0.send_command(f(triple.1, triple.2, sender));
131 receiver.recv().unwrap()
132}
133
134#[expect(unsafe_code)]
135pub(crate) unsafe fn uniform_typed<T>(
136 cx: *mut JSContext,
137 value: &[T::Element],
138 mut retval: MutableHandleValue,
139) where
140 T: TypedArrayElementCreator,
141{
142 rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
143 unsafe {
144 <TypedArray<T, *mut JSObject>>::create(cx, CreateWith::Slice(value), rval.handle_mut())
145 }
146 .unwrap();
147 retval.set(ObjectValue(rval.get()));
148}
149
150#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
152pub(crate) struct TextureUnpacking(u8);
153
154bitflags! {
155 impl TextureUnpacking: u8 {
156 const FLIP_Y_AXIS = 0x01;
157 const PREMULTIPLY_ALPHA = 0x02;
158 const CONVERT_COLORSPACE = 0x04;
159 }
160}
161
162#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
163pub(crate) enum VertexAttrib {
164 Float(f32, f32, f32, f32),
165 Int(i32, i32, i32, i32),
166 Uint(u32, u32, u32, u32),
167}
168
169#[derive(Clone, Copy, Debug)]
170pub(crate) enum Operation {
171 Fallible,
172 Infallible,
173}
174
175#[derive(JSTraceable, MallocSizeOf)]
176struct DroppableWebGLRenderingContext {
177 #[no_trace]
178 webgl_sender: WebGLMsgSender,
179}
180
181impl Drop for DroppableWebGLRenderingContext {
182 fn drop(&mut self) {
183 let _ = self.webgl_sender.send_remove();
184 }
185}
186
187#[dom_struct(associated_memory)]
188pub(crate) struct WebGLRenderingContext {
189 reflector_: Reflector<AssociatedMemory>,
190 #[no_trace]
191 webgl_version: WebGLVersion,
192 #[no_trace]
193 glsl_version: WebGLSLVersion,
194 #[ignore_malloc_size_of = "Defined in surfman"]
195 #[no_trace]
196 limits: GLLimits,
197 canvas: HTMLCanvasElementOrOffscreenCanvas,
198 #[ignore_malloc_size_of = "Defined in servo_canvas_traits"]
199 #[no_trace]
200 last_error: Cell<Option<WebGLError>>,
201 texture_packing_alignment: Cell<u8>,
202 texture_unpacking_settings: Cell<TextureUnpacking>,
203 texture_unpacking_alignment: Cell<u32>,
205 bound_draw_framebuffer: MutNullableDom<WebGLFramebuffer>,
206 bound_read_framebuffer: MutNullableDom<WebGLFramebuffer>,
209 bound_renderbuffer: MutNullableDom<WebGLRenderbuffer>,
210 bound_buffer_array: MutNullableDom<WebGLBuffer>,
211 current_program: MutNullableDom<WebGLProgram>,
212 current_vertex_attribs: DomRefCell<Box<[VertexAttrib]>>,
213 #[ignore_malloc_size_of = "Because it's small"]
214 current_scissor: Cell<(i32, i32, u32, u32)>,
215 #[ignore_malloc_size_of = "Because it's small"]
216 current_clear_color: Cell<(f32, f32, f32, f32)>,
217 #[no_trace]
218 size: Cell<Size2D<u32>>,
219 extension_manager: WebGLExtensions,
220 capabilities: Capabilities,
221 default_vao: DomOnceCell<WebGLVertexArrayObjectOES>,
222 current_vao: MutNullableDom<WebGLVertexArrayObjectOES>,
223 default_vao_webgl2: DomOnceCell<WebGLVertexArrayObject>,
224 current_vao_webgl2: MutNullableDom<WebGLVertexArrayObject>,
225 textures: Textures,
226 #[no_trace]
227 api_type: GlType,
228 droppable: DroppableWebGLRenderingContext,
229}
230
231impl WebGLRenderingContext {
232 pub(crate) fn create_context_data(
233 window: &Window,
234 webgl_version: WebGLVersion,
235 size: Size2D<u32>,
236 attrs: GLContextAttributes,
237 ) -> Result<WebGLCreateContextResult, String> {
238 if pref!(webgl_testing_context_creation_error) {
239 return Err("WebGL context creation error forced by pref `webgl.testing.context_creation_error`".into());
240 }
241
242 let webgl_chan = match window.webgl_chan() {
243 Some(chan) => chan,
244 None => return Err("WebGL initialization failed early on".into()),
245 };
246
247 let (sender, receiver) = webgl_channel().unwrap();
248 webgl_chan
249 .send(WebGLMsg::CreateContext(
250 window.webview_id().into(),
251 webgl_version,
252 size,
253 attrs,
254 sender,
255 ))
256 .unwrap();
257 receiver.recv().unwrap()
258 }
259
260 pub(crate) fn new_inherited(
261 canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
262 webgl_version: WebGLVersion,
263 size: Size2D<u32>,
264 ctx_data: WebGLCreateContextResult,
265 ) -> WebGLRenderingContext {
266 let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
267 let max_vertex_attribs = ctx_data.limits.max_vertex_attribs as usize;
268 WebGLRenderingContext {
269 reflector_: Reflector::new(),
270 webgl_version,
271 glsl_version: ctx_data.glsl_version,
272 limits: ctx_data.limits,
273 canvas: HTMLCanvasElementOrOffscreenCanvas::from(canvas),
274 last_error: Cell::new(None),
275 texture_packing_alignment: Cell::new(4),
276 texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE),
277 texture_unpacking_alignment: Cell::new(4),
278 bound_draw_framebuffer: MutNullableDom::new(None),
279 bound_read_framebuffer: MutNullableDom::new(None),
280 bound_buffer_array: MutNullableDom::new(None),
281 bound_renderbuffer: MutNullableDom::new(None),
282 current_program: MutNullableDom::new(None),
283 current_vertex_attribs: DomRefCell::new(
284 vec![VertexAttrib::Float(0f32, 0f32, 0f32, 1f32); max_vertex_attribs].into(),
285 ),
286 current_scissor: Cell::new((0, 0, size.width, size.height)),
287 size: Cell::new(size),
290 current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0)),
291 extension_manager: WebGLExtensions::new(
292 webgl_version,
293 ctx_data.api_type,
294 ctx_data.glsl_version,
295 ),
296 capabilities: Default::default(),
297 default_vao: Default::default(),
298 current_vao: Default::default(),
299 default_vao_webgl2: Default::default(),
300 current_vao_webgl2: Default::default(),
301 textures: Textures::new(max_combined_texture_image_units),
302 api_type: ctx_data.api_type,
303 droppable: DroppableWebGLRenderingContext {
304 webgl_sender: ctx_data.sender,
305 },
306 }
307 }
308
309 pub(crate) fn new(
310 cx: &mut js::context::JSContext,
311 window: &Window,
312 canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
313 webgl_version: WebGLVersion,
314 size: Size2D<u32>,
315 attrs: GLContextAttributes,
316 ) -> Option<DomRoot<WebGLRenderingContext>> {
317 let ctx_data = match Self::create_context_data(window, webgl_version, size, attrs) {
318 Ok(data) => data,
319 Err(msg) => {
320 error!("Couldn't create WebGLRenderingContext: {}", msg);
321 let event = WebGLContextEvent::new(
322 window,
323 atom!("webglcontextcreationerror"),
324 EventBubbles::DoesNotBubble,
325 EventCancelable::Cancelable,
326 DOMString::from(msg),
327 CanGc::from_cx(cx),
328 );
329 match canvas {
330 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
331 event.upcast::<Event>().fire(cx, canvas.upcast());
332 },
333 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => {
334 event.upcast::<Event>().fire(cx, canvas.upcast());
335 },
336 }
337 return None;
338 },
339 };
340
341 Some(reflect_dom_object_with_cx(
342 Box::new(WebGLRenderingContext::new_inherited(
343 canvas,
344 webgl_version,
345 size,
346 ctx_data,
347 )),
348 window,
349 cx,
350 ))
351 }
352
353 pub(crate) fn set_image_key(&self, image_key: ImageKey) {
354 self.droppable.webgl_sender.set_image_key(image_key);
355 }
356
357 pub(crate) fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
358 if !self.onscreen() {
359 return false;
360 }
361
362 let global = self.global();
363 let Some(window) = global.downcast::<Window>() else {
364 return false;
365 };
366
367 window
368 .webgl_chan()
369 .expect("Where's the WebGL channel?")
370 .send(WebGLMsg::SwapBuffers(
371 vec![self.context_id()],
372 Some(canvas_epoch),
373 0, ))
375 .is_ok()
376 }
377
378 pub(crate) fn webgl_version(&self) -> WebGLVersion {
379 self.webgl_version
380 }
381
382 pub(crate) fn limits(&self) -> &GLLimits {
383 &self.limits
384 }
385
386 pub(crate) fn texture_unpacking_alignment(&self) -> u32 {
387 self.texture_unpacking_alignment.get()
388 }
389
390 pub(crate) fn bound_draw_framebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
391 self.bound_draw_framebuffer.get()
392 }
393
394 pub(crate) fn current_vao(&self) -> DomRoot<WebGLVertexArrayObjectOES> {
395 self.current_vao.or_init(|| {
396 DomRoot::from_ref(
397 self.default_vao.init_once(|| {
398 WebGLVertexArrayObjectOES::new(self, None, CanGc::deprecated_note())
399 }),
400 )
401 })
402 }
403
404 pub(crate) fn current_vao_webgl2(&self) -> DomRoot<WebGLVertexArrayObject> {
405 self.current_vao_webgl2.or_init(|| {
406 DomRoot::from_ref(
407 self.default_vao_webgl2.init_once(|| {
408 WebGLVertexArrayObject::new(self, None, CanGc::deprecated_note())
409 }),
410 )
411 })
412 }
413
414 pub(crate) fn current_vertex_attribs(&self) -> RefMut<'_, Box<[VertexAttrib]>> {
415 self.current_vertex_attribs.borrow_mut()
416 }
417
418 #[inline]
419 pub(crate) fn sender(&self) -> &WebGLMsgSender {
420 &self.droppable.webgl_sender
421 }
422
423 #[inline]
424 pub(crate) fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
425 let result = self
426 .droppable
427 .webgl_sender
428 .send(command, capture_webgl_backtrace());
429 if matches!(fallibility, Operation::Infallible) {
430 result.expect("Operation failed");
431 }
432 }
433
434 #[inline]
435 pub(crate) fn send_command(&self, command: WebGLCommand) {
436 self.send_with_fallibility(command, Operation::Infallible);
437 }
438
439 pub(crate) fn send_command_ignored(&self, command: WebGLCommand) {
440 self.send_with_fallibility(command, Operation::Fallible);
441 }
442
443 pub(crate) fn webgl_error(&self, err: WebGLError) {
444 warn!(
446 "WebGL error: {:?}, previous error was {:?}",
447 err,
448 self.last_error.get()
449 );
450
451 if self.last_error.get().is_none() {
454 self.last_error.set(Some(err));
455 }
456 }
457
458 pub(crate) fn validate_framebuffer(&self) -> WebGLResult<()> {
477 match self.bound_draw_framebuffer.get() {
478 Some(fb) => match fb.check_status_for_rendering() {
479 CompleteForRendering::Complete => Ok(()),
480 CompleteForRendering::Incomplete => Err(InvalidFramebufferOperation),
481 CompleteForRendering::MissingColorAttachment => Err(InvalidOperation),
482 },
483 None => Ok(()),
484 }
485 }
486
487 pub(crate) fn validate_ownership<T>(&self, object: &T) -> WebGLResult<()>
488 where
489 T: DerivedFrom<WebGLObject>,
490 {
491 let Some(context) = object.upcast().context() else {
492 return Err(WebGLError::InvalidOperation);
493 };
494 if !std::ptr::eq(self, &*context) {
495 return Err(WebGLError::InvalidOperation);
496 }
497 Ok(())
498 }
499
500 pub(crate) fn with_location<F>(&self, location: Option<&WebGLUniformLocation>, f: F)
501 where
502 F: FnOnce(&WebGLUniformLocation) -> WebGLResult<()>,
503 {
504 let location = match location {
505 Some(loc) => loc,
506 None => return,
507 };
508 match self.current_program.get() {
509 Some(ref program)
510 if program.id() == location.program_id() &&
511 program.link_generation() == location.link_generation() => {},
512 _ => return self.webgl_error(InvalidOperation),
513 }
514 handle_potential_webgl_error!(self, f(location));
515 }
516
517 pub(crate) fn textures(&self) -> &Textures {
518 &self.textures
519 }
520
521 fn tex_parameter(&self, target: u32, param: u32, value: TexParameterValue) {
522 let texture_slot = handle_potential_webgl_error!(
523 self,
524 self.textures
525 .active_texture_slot(target, self.webgl_version()),
526 return
527 );
528 let texture =
529 handle_potential_webgl_error!(self, texture_slot.get().ok_or(InvalidOperation), return);
530
531 if !self
532 .extension_manager
533 .is_get_tex_parameter_name_enabled(param)
534 {
535 return self.webgl_error(InvalidEnum);
536 }
537
538 handle_potential_webgl_error!(self, texture.tex_parameter(param, value), return);
539
540 if target != constants::TEXTURE_2D {
542 return;
543 }
544
545 let target = TexImageTarget::Texture2D;
546 if let Some(info) = texture.image_info_for_target(&target, 0) {
547 self.validate_filterable_texture(
548 &texture,
549 target,
550 0,
551 info.internal_format(),
552 Size2D::new(info.width(), info.height()),
553 info.data_type().unwrap_or(TexDataType::UnsignedByte),
554 );
555 }
556 }
557
558 fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
559 if indx >= self.limits.max_vertex_attribs {
560 return self.webgl_error(InvalidValue);
561 }
562
563 match self.webgl_version() {
564 WebGLVersion::WebGL1 => self
565 .current_vao()
566 .set_vertex_attrib_type(indx, constants::FLOAT),
567 WebGLVersion::WebGL2 => self
568 .current_vao_webgl2()
569 .set_vertex_attrib_type(indx, constants::FLOAT),
570 };
571 self.current_vertex_attribs.borrow_mut()[indx as usize] = VertexAttrib::Float(x, y, z, w);
572
573 self.send_command(WebGLCommand::VertexAttrib(indx, x, y, z, w));
574 }
575
576 pub(crate) fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> {
577 match self.bound_draw_framebuffer.get() {
578 Some(fb) => fb.size(),
579
580 None => Some((self.DrawingBufferWidth(), self.DrawingBufferHeight())),
582 }
583 }
584
585 pub(crate) fn get_texture_packing_alignment(&self) -> u8 {
586 self.texture_packing_alignment.get()
587 }
588
589 pub(crate) fn get_current_unpack_state(
590 &self,
591 premultiplied: Alpha,
592 ) -> (Option<AlphaTreatment>, YAxisTreatment) {
593 let settings = self.texture_unpacking_settings.get();
594 let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA);
595
596 let alpha_treatment = match (premultiplied, dest_premultiplied) {
597 (Alpha::Premultiplied, false) => Some(AlphaTreatment::Unmultiply),
598 (Alpha::NotPremultiplied, true) => Some(AlphaTreatment::Premultiply),
599 _ => None,
600 };
601
602 let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) {
603 YAxisTreatment::Flipped
604 } else {
605 YAxisTreatment::AsIs
606 };
607
608 (alpha_treatment, y_axis_treatment)
609 }
610
611 fn validate_filterable_texture(
614 &self,
615 texture: &WebGLTexture,
616 target: TexImageTarget,
617 level: u32,
618 internal_format: TexFormat,
619 size: Size2D<u32>,
620 data_type: TexDataType,
621 ) -> bool {
622 if self
623 .extension_manager
624 .is_filterable(data_type.as_gl_constant()) ||
625 !texture.is_using_linear_filtering()
626 {
627 return true;
628 }
629
630 let data_type = TexDataType::UnsignedByte;
633 let expected_byte_length = size.area() * 4;
634 let mut pixels = vec![0u8; expected_byte_length as usize];
635 for rgba8 in pixels.chunks_mut(4) {
636 rgba8[3] = 255u8;
637 }
638
639 self.tex_image_2d(
643 texture,
644 target,
645 data_type,
646 internal_format,
647 internal_format.to_unsized(),
648 level,
649 0,
650 1,
651 size,
652 TexSource::Pixels(TexPixels::new(
653 GenericSharedMemory::from_vec(pixels),
654 size,
655 PixelFormat::RGBA8,
656 None,
657 YAxisTreatment::AsIs,
658 )),
659 );
660
661 false
662 }
663
664 fn validate_stencil_actions(&self, action: u32) -> bool {
665 matches!(
666 action,
667 0 | constants::KEEP |
668 constants::REPLACE |
669 constants::INCR |
670 constants::DECR |
671 constants::INVERT |
672 constants::INCR_WRAP |
673 constants::DECR_WRAP
674 )
675 }
676
677 pub(crate) fn get_image_pixels(&self, source: TexImageSource) -> Fallible<Option<TexPixels>> {
678 Ok(Some(match source {
679 TexImageSource::ImageBitmap(bitmap) => {
680 if !bitmap.origin_is_clean() {
681 return Err(Error::Security(None));
682 }
683
684 let Some(snapshot) = bitmap.bitmap_data().clone() else {
685 return Ok(None);
686 };
687
688 let snapshot = snapshot.to_shared();
689 let size = snapshot.size().cast();
690 let format = match snapshot.format() {
691 SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
692 SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
693 };
694
695 TexPixels::new(
702 snapshot.shared_memory(),
703 size,
704 format,
705 None,
706 YAxisTreatment::AsIs,
707 )
708 },
709 TexImageSource::ImageData(image_data) => {
710 let (alpha_treatment, y_axis_treatment) =
711 self.get_current_unpack_state(Alpha::NotPremultiplied);
712
713 TexPixels::new(
714 image_data.to_shared_memory(),
715 image_data.get_size(),
716 PixelFormat::RGBA8,
717 alpha_treatment,
718 y_axis_treatment,
719 )
720 },
721 TexImageSource::HTMLImageElement(image) => {
722 let document = match self.canvas {
723 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
724 canvas.owner_document()
725 },
726 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(ref _canvas) => {
727 return Ok(None);
729 },
730 };
731 if !image.same_origin(&document.origin()) {
732 return Err(Error::Security(None));
733 }
734
735 let Some(snapshot) = image.get_raster_image_data() else {
739 return Ok(None);
740 };
741
742 let snapshot = snapshot.to_shared();
743 let size = snapshot.size().cast();
744 let format: PixelFormat = match snapshot.format() {
745 SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
746 SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
747 };
748
749 let (alpha_treatment, y_axis_treatment) =
750 self.get_current_unpack_state(snapshot.alpha_mode().alpha());
751
752 TexPixels::new(
753 snapshot.shared_memory(),
754 size,
755 format,
756 alpha_treatment,
757 y_axis_treatment,
758 )
759 },
760 TexImageSource::HTMLCanvasElement(canvas) => {
764 if !canvas.origin_is_clean() {
765 return Err(Error::Security(None));
766 }
767
768 let Some(snapshot) = canvas.get_image_data() else {
769 return Ok(None);
770 };
771
772 let snapshot = snapshot.to_shared();
773 let size = snapshot.size().cast();
774 let format = match snapshot.format() {
775 SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
776 SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
777 };
778
779 let (alpha_treatment, y_axis_treatment) =
780 self.get_current_unpack_state(snapshot.alpha_mode().alpha());
781
782 TexPixels::new(
783 snapshot.shared_memory(),
784 size,
785 format,
786 alpha_treatment,
787 y_axis_treatment,
788 )
789 },
790 TexImageSource::HTMLVideoElement(video) => {
791 if !video.origin_is_clean() {
792 return Err(Error::Security(None));
793 }
794
795 let Some(snapshot) = video.get_current_frame_data() else {
796 return Ok(None);
797 };
798
799 let snapshot = snapshot.to_shared();
800 let size = snapshot.size().cast();
801 let format: PixelFormat = match snapshot.format() {
802 SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
803 SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
804 };
805
806 let (alpha_treatment, y_axis_treatment) =
807 self.get_current_unpack_state(snapshot.alpha_mode().alpha());
808
809 TexPixels::new(
810 snapshot.shared_memory(),
811 size,
812 format,
813 alpha_treatment,
814 y_axis_treatment,
815 )
816 },
817 }))
818 }
819
820 pub(crate) fn validate_tex_image_2d_data(
822 &self,
823 width: u32,
824 height: u32,
825 format: TexFormat,
826 data_type: TexDataType,
827 unpacking_alignment: u32,
828 data: Option<&ArrayBufferView>,
829 ) -> Result<u32, ()> {
830 let element_size = data_type.element_size();
831 let components_per_element = data_type.components_per_element();
832 let components = format.components();
833
834 let data_type_matches = data.as_ref().is_none_or(|buffer| {
842 Some(data_type.sized_data_type()) ==
843 array_buffer_type_to_sized_type(buffer.get_array_type()) &&
844 data_type.required_webgl_version() <= self.webgl_version()
845 });
846
847 if !data_type_matches {
848 self.webgl_error(InvalidOperation);
849 return Err(());
850 }
851
852 if height == 0 {
854 Ok(0)
855 } else {
856 let cpp = element_size * components / components_per_element;
861 let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
862 Ok(stride * (height - 1) + width * cpp)
863 }
864 }
865
866 #[allow(clippy::too_many_arguments)]
867 pub(crate) fn tex_image_2d(
868 &self,
869 texture: &WebGLTexture,
870 target: TexImageTarget,
871 data_type: TexDataType,
872 internal_format: TexFormat,
873 format: TexFormat,
874 level: u32,
875 _border: u32,
876 unpacking_alignment: u32,
877 size: Size2D<u32>,
878 source: TexSource,
879 ) {
880 handle_potential_webgl_error!(
882 self,
883 texture.initialize(
884 target,
885 size.width,
886 size.height,
887 1,
888 format,
889 level,
890 Some(data_type)
891 )
892 );
893
894 let internal_format = self
895 .extension_manager
896 .get_effective_tex_internal_format(internal_format, data_type.as_gl_constant());
897
898 let effective_data_type = self
899 .extension_manager
900 .effective_type(data_type.as_gl_constant());
901
902 match source {
903 TexSource::Pixels(pixels) => {
904 self.send_command(WebGLCommand::TexImage2D {
906 target: target.as_gl_constant(),
907 level,
908 internal_format,
909 size,
910 format,
911 data_type,
912 effective_data_type,
913 unpacking_alignment,
914 alpha_treatment: pixels.alpha_treatment,
915 y_axis_treatment: pixels.y_axis_treatment,
916 pixel_format: pixels.pixel_format,
917 data: pixels.data.into(),
918 });
919 },
920 TexSource::BufferOffset(offset) => {
921 self.send_command(WebGLCommand::TexImage2DPBO {
922 target: target.as_gl_constant(),
923 level,
924 internal_format,
925 size,
926 format,
927 effective_data_type,
928 unpacking_alignment,
929 offset,
930 });
931 },
932 }
933
934 if let Some(fb) = self.bound_draw_framebuffer.get() {
935 fb.invalidate_texture(texture);
936 }
937 }
938
939 #[allow(clippy::too_many_arguments)]
940 fn tex_sub_image_2d(
941 &self,
942 texture: DomRoot<WebGLTexture>,
943 target: TexImageTarget,
944 level: u32,
945 xoffset: i32,
946 yoffset: i32,
947 format: TexFormat,
948 data_type: TexDataType,
949 unpacking_alignment: u32,
950 pixels: TexPixels,
951 ) {
952 let image_info = match texture.image_info_for_target(&target, level) {
954 Some(info) => info,
955 None => return self.webgl_error(InvalidOperation),
956 };
957
958 if xoffset < 0 ||
963 (xoffset as u32 + pixels.size().width) > image_info.width() ||
964 yoffset < 0 ||
965 (yoffset as u32 + pixels.size().height) > image_info.height()
966 {
967 return self.webgl_error(InvalidValue);
968 }
969
970 debug_assert!(!format.is_sized());
972 if format != image_info.internal_format().to_unsized() {
973 return self.webgl_error(InvalidOperation);
974 }
975
976 if self.webgl_version() == WebGLVersion::WebGL1 &&
978 data_type != image_info.data_type().unwrap()
979 {
980 return self.webgl_error(InvalidOperation);
981 }
982
983 let effective_data_type = self
984 .extension_manager
985 .effective_type(data_type.as_gl_constant());
986
987 self.send_command(WebGLCommand::TexSubImage2D {
989 target: target.as_gl_constant(),
990 level,
991 xoffset,
992 yoffset,
993 size: pixels.size(),
994 format,
995 data_type,
996 effective_data_type,
997 unpacking_alignment,
998 alpha_treatment: pixels.alpha_treatment,
999 y_axis_treatment: pixels.y_axis_treatment,
1000 pixel_format: pixels.pixel_format,
1001 data: pixels.data.into(),
1002 });
1003 }
1004
1005 fn get_gl_extensions(&self) -> String {
1006 let (sender, receiver) = webgl_channel().unwrap();
1007 self.send_command(WebGLCommand::GetExtensions(sender));
1008 receiver.recv().unwrap()
1009 }
1010
1011 pub(crate) fn draw_arrays_instanced(
1013 &self,
1014 mode: u32,
1015 first: i32,
1016 count: i32,
1017 primcount: i32,
1018 ) -> WebGLResult<()> {
1019 match mode {
1020 constants::POINTS |
1021 constants::LINE_STRIP |
1022 constants::LINE_LOOP |
1023 constants::LINES |
1024 constants::TRIANGLE_STRIP |
1025 constants::TRIANGLE_FAN |
1026 constants::TRIANGLES => {},
1027 _ => {
1028 return Err(InvalidEnum);
1029 },
1030 }
1031 if first < 0 || count < 0 || primcount < 0 {
1032 return Err(InvalidValue);
1033 }
1034
1035 let current_program = self.current_program.get().ok_or(InvalidOperation)?;
1036
1037 let required_len = if count > 0 {
1038 first
1039 .checked_add(count)
1040 .map(|len| len as u32)
1041 .ok_or(InvalidOperation)?
1042 } else {
1043 0
1044 };
1045
1046 match self.webgl_version() {
1047 WebGLVersion::WebGL1 => self.current_vao().validate_for_draw(
1048 required_len,
1049 primcount as u32,
1050 ¤t_program.active_attribs(),
1051 )?,
1052 WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw(
1053 required_len,
1054 primcount as u32,
1055 ¤t_program.active_attribs(),
1056 )?,
1057 };
1058
1059 self.validate_framebuffer()?;
1060
1061 if count == 0 || primcount == 0 {
1062 return Ok(());
1063 }
1064
1065 self.send_command(if primcount == 1 {
1066 WebGLCommand::DrawArrays { mode, first, count }
1067 } else {
1068 WebGLCommand::DrawArraysInstanced {
1069 mode,
1070 first,
1071 count,
1072 primcount,
1073 }
1074 });
1075 self.mark_as_dirty();
1076 Ok(())
1077 }
1078
1079 pub(crate) fn draw_elements_instanced(
1081 &self,
1082 mode: u32,
1083 count: i32,
1084 type_: u32,
1085 offset: i64,
1086 primcount: i32,
1087 ) -> WebGLResult<()> {
1088 match mode {
1089 constants::POINTS |
1090 constants::LINE_STRIP |
1091 constants::LINE_LOOP |
1092 constants::LINES |
1093 constants::TRIANGLE_STRIP |
1094 constants::TRIANGLE_FAN |
1095 constants::TRIANGLES => {},
1096 _ => {
1097 return Err(InvalidEnum);
1098 },
1099 }
1100 if count < 0 || offset < 0 || primcount < 0 {
1101 return Err(InvalidValue);
1102 }
1103 let type_size = match type_ {
1104 constants::UNSIGNED_BYTE => 1,
1105 constants::UNSIGNED_SHORT => 2,
1106 constants::UNSIGNED_INT => match self.webgl_version() {
1107 WebGLVersion::WebGL1 if self.extension_manager.is_element_index_uint_enabled() => 4,
1108 WebGLVersion::WebGL2 => 4,
1109 _ => return Err(InvalidEnum),
1110 },
1111 _ => return Err(InvalidEnum),
1112 };
1113 if offset % type_size != 0 {
1114 return Err(InvalidOperation);
1115 }
1116
1117 let current_program = self.current_program.get().ok_or(InvalidOperation)?;
1118 let array_buffer = match self.webgl_version() {
1119 WebGLVersion::WebGL1 => self.current_vao().element_array_buffer().get(),
1120 WebGLVersion::WebGL2 => self.current_vao_webgl2().element_array_buffer().get(),
1121 }
1122 .ok_or(InvalidOperation)?;
1123
1124 if count > 0 && primcount > 0 {
1125 let val = offset as u64 + (count as u64 * type_size as u64);
1127 if val > array_buffer.capacity() as u64 {
1128 return Err(InvalidOperation);
1129 }
1130 }
1131
1132 match self.webgl_version() {
1134 WebGLVersion::WebGL1 => self.current_vao().validate_for_draw(
1135 0,
1136 primcount as u32,
1137 ¤t_program.active_attribs(),
1138 )?,
1139 WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw(
1140 0,
1141 primcount as u32,
1142 ¤t_program.active_attribs(),
1143 )?,
1144 };
1145
1146 self.validate_framebuffer()?;
1147
1148 if count == 0 || primcount == 0 {
1149 return Ok(());
1150 }
1151
1152 let offset = offset as u32;
1153 self.send_command(if primcount == 1 {
1154 WebGLCommand::DrawElements {
1155 mode,
1156 count,
1157 type_,
1158 offset,
1159 }
1160 } else {
1161 WebGLCommand::DrawElementsInstanced {
1162 mode,
1163 count,
1164 type_,
1165 offset,
1166 primcount,
1167 }
1168 });
1169 self.mark_as_dirty();
1170 Ok(())
1171 }
1172
1173 pub(crate) fn vertex_attrib_divisor(&self, index: u32, divisor: u32) {
1174 if index >= self.limits.max_vertex_attribs {
1175 return self.webgl_error(InvalidValue);
1176 }
1177
1178 match self.webgl_version() {
1179 WebGLVersion::WebGL1 => self.current_vao().vertex_attrib_divisor(index, divisor),
1180 WebGLVersion::WebGL2 => self
1181 .current_vao_webgl2()
1182 .vertex_attrib_divisor(index, divisor),
1183 };
1184 self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor });
1185 }
1186
1187 pub(crate) fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
1188 self.bound_buffer_array.get()
1189 }
1190
1191 pub(crate) fn array_buffer_slot(&self) -> &MutNullableDom<WebGLBuffer> {
1192 &self.bound_buffer_array
1193 }
1194
1195 pub(crate) fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
1196 match target {
1197 constants::ARRAY_BUFFER => Ok(self.bound_buffer_array.get()),
1198 constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()),
1199 _ => Err(WebGLError::InvalidEnum),
1200 }
1201 }
1202
1203 pub(crate) fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> {
1204 match usage {
1205 constants::STREAM_DRAW | constants::STATIC_DRAW | constants::DYNAMIC_DRAW => Ok(usage),
1206 _ => Err(WebGLError::InvalidEnum),
1207 }
1208 }
1209
1210 pub(crate) fn create_vertex_array(&self) -> Option<DomRoot<WebGLVertexArrayObjectOES>> {
1211 let (sender, receiver) = webgl_channel().unwrap();
1212 self.send_command(WebGLCommand::CreateVertexArray(sender));
1213 receiver
1214 .recv()
1215 .unwrap()
1216 .map(|id| WebGLVertexArrayObjectOES::new(self, Some(id), CanGc::deprecated_note()))
1217 }
1218
1219 pub(crate) fn create_vertex_array_webgl2(&self) -> Option<DomRoot<WebGLVertexArrayObject>> {
1220 let (sender, receiver) = webgl_channel().unwrap();
1221 self.send_command(WebGLCommand::CreateVertexArray(sender));
1222 receiver
1223 .recv()
1224 .unwrap()
1225 .map(|id| WebGLVertexArrayObject::new(self, Some(id), CanGc::deprecated_note()))
1226 }
1227
1228 pub(crate) fn delete_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
1229 if let Some(vao) = vao {
1230 handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1231 assert!(vao.id().is_some());
1233 if vao.is_deleted() {
1234 return;
1235 }
1236 if vao == &*self.current_vao() {
1237 self.current_vao.set(None);
1240 self.send_command(WebGLCommand::BindVertexArray(None));
1241 }
1242 vao.delete(Operation::Infallible);
1243 }
1244 }
1245
1246 pub(crate) fn delete_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) {
1247 if let Some(vao) = vao {
1248 handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1249 assert!(vao.id().is_some());
1251 if vao.is_deleted() {
1252 return;
1253 }
1254 if vao == &*self.current_vao_webgl2() {
1255 self.current_vao_webgl2.set(None);
1258 self.send_command(WebGLCommand::BindVertexArray(None));
1259 }
1260 vao.delete(Operation::Infallible);
1261 }
1262 }
1263
1264 pub(crate) fn is_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool {
1265 vao.is_some_and(|vao| {
1266 assert!(vao.id().is_some());
1268 self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted()
1269 })
1270 }
1271
1272 pub(crate) fn is_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) -> bool {
1273 vao.is_some_and(|vao| {
1274 assert!(vao.id().is_some());
1276 self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted()
1277 })
1278 }
1279
1280 pub(crate) fn bind_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
1281 if let Some(vao) = vao {
1282 assert!(vao.id().is_some());
1284 handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1285 if vao.is_deleted() {
1286 return self.webgl_error(InvalidOperation);
1287 }
1288 vao.set_ever_bound();
1289 }
1290 self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id())));
1291 self.current_vao.set(vao);
1294 }
1295
1296 pub(crate) fn bind_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) {
1297 if let Some(vao) = vao {
1298 assert!(vao.id().is_some());
1300 handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1301 if vao.is_deleted() {
1302 return self.webgl_error(InvalidOperation);
1303 }
1304 vao.set_ever_bound();
1305 }
1306 self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id())));
1307 self.current_vao_webgl2.set(vao);
1310 }
1311
1312 fn validate_blend_mode(&self, mode: u32) -> WebGLResult<()> {
1313 match mode {
1314 constants::FUNC_ADD | constants::FUNC_SUBTRACT | constants::FUNC_REVERSE_SUBTRACT => {
1315 Ok(())
1316 },
1317 EXTBlendMinmaxConstants::MIN_EXT | EXTBlendMinmaxConstants::MAX_EXT
1318 if self.extension_manager.is_blend_minmax_enabled() =>
1319 {
1320 Ok(())
1321 },
1322 _ => Err(InvalidEnum),
1323 }
1324 }
1325
1326 pub(crate) fn initialize_framebuffer(&self, clear_bits: u32) {
1327 if clear_bits == 0 {
1328 return;
1329 }
1330 self.send_command(WebGLCommand::InitializeFramebuffer {
1331 color: clear_bits & constants::COLOR_BUFFER_BIT != 0,
1332 depth: clear_bits & constants::DEPTH_BUFFER_BIT != 0,
1333 stencil: clear_bits & constants::STENCIL_BUFFER_BIT != 0,
1334 });
1335 }
1336
1337 pub(crate) fn extension_manager(&self) -> &WebGLExtensions {
1338 &self.extension_manager
1339 }
1340
1341 #[expect(unsafe_code)]
1342 pub(crate) fn buffer_data(
1343 &self,
1344 target: u32,
1345 data: Option<ArrayBufferViewOrArrayBuffer>,
1346 usage: u32,
1347 bound_buffer: Option<DomRoot<WebGLBuffer>>,
1348 ) {
1349 let data = handle_potential_webgl_error!(self, data.ok_or(InvalidValue), return);
1350 let bound_buffer =
1351 handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
1352
1353 let data = unsafe {
1354 match data {
1356 ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(),
1357 ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(),
1358 }
1359 };
1360 handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, data, usage));
1361 }
1362
1363 pub(crate) fn buffer_data_(
1364 &self,
1365 target: u32,
1366 size: i64,
1367 usage: u32,
1368 bound_buffer: Option<DomRoot<WebGLBuffer>>,
1369 ) {
1370 let bound_buffer =
1371 handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
1372
1373 if size < 0 {
1374 return self.webgl_error(InvalidValue);
1375 }
1376
1377 let data = vec![0u8; size as usize];
1380 handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data, usage));
1381 }
1382
1383 #[expect(unsafe_code)]
1384 pub(crate) fn buffer_sub_data(
1385 &self,
1386 target: u32,
1387 offset: i64,
1388 data: ArrayBufferViewOrArrayBuffer,
1389 bound_buffer: Option<DomRoot<WebGLBuffer>>,
1390 ) {
1391 let bound_buffer =
1392 handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
1393
1394 if offset < 0 {
1395 return self.webgl_error(InvalidValue);
1396 }
1397
1398 let data = unsafe {
1399 match data {
1401 ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(),
1402 ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(),
1403 }
1404 };
1405 if (offset as u64) + data.len() as u64 > bound_buffer.capacity() as u64 {
1406 return self.webgl_error(InvalidValue);
1407 }
1408 let (sender, receiver) = generic_channel::channel().unwrap();
1409 self.send_command(WebGLCommand::BufferSubData(
1410 target,
1411 offset as isize,
1412 receiver,
1413 ));
1414 let buffer = GenericSharedMemory::from_bytes(data);
1415 sender.send(buffer).unwrap();
1416 }
1417
1418 pub(crate) fn bind_buffer_maybe(
1419 &self,
1420 slot: &MutNullableDom<WebGLBuffer>,
1421 target: u32,
1422 buffer: Option<&WebGLBuffer>,
1423 ) {
1424 if let Some(buffer) = buffer {
1425 handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
1426
1427 if buffer.is_marked_for_deletion() {
1428 return self.webgl_error(InvalidOperation);
1429 }
1430 handle_potential_webgl_error!(self, buffer.set_target_maybe(target), return);
1431 buffer.increment_attached_counter();
1432 }
1433
1434 self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id())));
1435 if let Some(old) = slot.get() {
1436 old.decrement_attached_counter(Operation::Infallible);
1437 }
1438
1439 slot.set(buffer);
1440 }
1441
1442 pub(crate) fn current_program(&self) -> Option<DomRoot<WebGLProgram>> {
1443 self.current_program.get()
1444 }
1445
1446 pub(crate) fn uniform_check_program(
1447 &self,
1448 program: &WebGLProgram,
1449 location: &WebGLUniformLocation,
1450 ) -> WebGLResult<()> {
1451 self.validate_ownership(program)?;
1452
1453 if program.is_deleted() ||
1454 !program.is_linked() ||
1455 self.context_id() != location.context_id() ||
1456 program.id() != location.program_id() ||
1457 program.link_generation() != location.link_generation()
1458 {
1459 return Err(InvalidOperation);
1460 }
1461
1462 Ok(())
1463 }
1464
1465 fn uniform_vec_section_int(
1466 &self,
1467 vec: Int32ArrayOrLongSequence,
1468 offset: u32,
1469 length: u32,
1470 uniform_size: usize,
1471 uniform_location: &WebGLUniformLocation,
1472 ) -> WebGLResult<Vec<i32>> {
1473 let vec = match vec {
1474 Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
1475 Int32ArrayOrLongSequence::LongSequence(v) => v,
1476 };
1477 self.uniform_vec_section::<i32>(vec, offset, length, uniform_size, uniform_location)
1478 }
1479
1480 fn uniform_vec_section_float(
1481 &self,
1482 vec: Float32ArrayOrUnrestrictedFloatSequence,
1483 offset: u32,
1484 length: u32,
1485 uniform_size: usize,
1486 uniform_location: &WebGLUniformLocation,
1487 ) -> WebGLResult<Vec<f32>> {
1488 let vec = match vec {
1489 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
1490 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
1491 };
1492 self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location)
1493 }
1494
1495 pub(crate) fn uniform_vec_section<T: Clone>(
1496 &self,
1497 vec: Vec<T>,
1498 offset: u32,
1499 length: u32,
1500 uniform_size: usize,
1501 uniform_location: &WebGLUniformLocation,
1502 ) -> WebGLResult<Vec<T>> {
1503 let offset = offset as usize;
1504 if offset > vec.len() {
1505 return Err(InvalidValue);
1506 }
1507
1508 let length = if length > 0 {
1509 length as usize
1510 } else {
1511 vec.len() - offset
1512 };
1513 if offset + length > vec.len() {
1514 return Err(InvalidValue);
1515 }
1516
1517 let vec = if offset == 0 && length == vec.len() {
1518 vec
1519 } else {
1520 vec[offset..offset + length].to_vec()
1521 };
1522
1523 if vec.len() < uniform_size || vec.len() % uniform_size != 0 {
1524 return Err(InvalidValue);
1525 }
1526 if uniform_location.size().is_none() && vec.len() != uniform_size {
1527 return Err(InvalidOperation);
1528 }
1529
1530 Ok(vec)
1531 }
1532
1533 pub(crate) fn uniform_matrix_section(
1534 &self,
1535 vec: Float32ArrayOrUnrestrictedFloatSequence,
1536 offset: u32,
1537 length: u32,
1538 transpose: bool,
1539 uniform_size: usize,
1540 uniform_location: &WebGLUniformLocation,
1541 ) -> WebGLResult<Vec<f32>> {
1542 let vec = match vec {
1543 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
1544 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
1545 };
1546 if transpose {
1547 return Err(InvalidValue);
1548 }
1549 self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location)
1550 }
1551
1552 pub(crate) fn get_draw_framebuffer_slot(&self) -> &MutNullableDom<WebGLFramebuffer> {
1553 &self.bound_draw_framebuffer
1554 }
1555
1556 pub(crate) fn get_read_framebuffer_slot(&self) -> &MutNullableDom<WebGLFramebuffer> {
1557 &self.bound_read_framebuffer
1558 }
1559
1560 pub(crate) fn validate_new_framebuffer_binding(
1561 &self,
1562 framebuffer: Option<&WebGLFramebuffer>,
1563 ) -> WebGLResult<()> {
1564 if let Some(fb) = framebuffer {
1565 self.validate_ownership(fb)?;
1566 if fb.is_deleted() {
1567 return Err(InvalidOperation);
1573 }
1574 }
1575 Ok(())
1576 }
1577
1578 pub(crate) fn bind_framebuffer_to(
1579 &self,
1580 target: u32,
1581 framebuffer: Option<&WebGLFramebuffer>,
1582 slot: &MutNullableDom<WebGLFramebuffer>,
1583 ) {
1584 match framebuffer {
1585 Some(framebuffer) => framebuffer.bind(target),
1586 None => {
1587 let cmd =
1589 WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Default);
1590 self.send_command(cmd);
1591 },
1592 }
1593 slot.set(framebuffer);
1594 }
1595
1596 pub(crate) fn renderbuffer_storage(
1597 &self,
1598 target: u32,
1599 samples: i32,
1600 internal_format: u32,
1601 width: i32,
1602 height: i32,
1603 ) {
1604 if target != constants::RENDERBUFFER {
1605 return self.webgl_error(InvalidEnum);
1606 }
1607
1608 let max = self.limits.max_renderbuffer_size;
1609
1610 if samples < 0 || width < 0 || width as u32 > max || height < 0 || height as u32 > max {
1611 return self.webgl_error(InvalidValue);
1612 }
1613
1614 let rb = handle_potential_webgl_error!(
1615 self,
1616 self.bound_renderbuffer.get().ok_or(InvalidOperation),
1617 return
1618 );
1619 handle_potential_webgl_error!(
1620 self,
1621 rb.storage(self.api_type, samples, internal_format, width, height)
1622 );
1623 if let Some(fb) = self.bound_draw_framebuffer.get() {
1624 fb.invalidate_renderbuffer(&rb);
1625 }
1626
1627 }
1629
1630 pub(crate) fn valid_color_attachment_enum(&self, attachment: u32) -> bool {
1631 let last_slot = constants::COLOR_ATTACHMENT0 + self.limits().max_color_attachments - 1;
1632 constants::COLOR_ATTACHMENT0 <= attachment && attachment <= last_slot
1633 }
1634
1635 #[allow(clippy::too_many_arguments)]
1636 pub(crate) fn compressed_tex_image_2d(
1637 &self,
1638 target: u32,
1639 level: i32,
1640 internal_format: u32,
1641 width: i32,
1642 height: i32,
1643 border: i32,
1644 data: &[u8],
1645 ) {
1646 let validator = CompressedTexImage2DValidator::new(
1647 self,
1648 target,
1649 level,
1650 width,
1651 height,
1652 border,
1653 internal_format,
1654 data.len(),
1655 );
1656 let CommonCompressedTexImage2DValidatorResult {
1657 texture,
1658 target,
1659 level,
1660 width,
1661 height,
1662 compression,
1663 } = match validator.validate() {
1664 Ok(result) => result,
1665 Err(_) => return,
1666 };
1667
1668 if texture.is_immutable() {
1669 return self.webgl_error(InvalidOperation);
1670 }
1671
1672 let size = Size2D::new(width, height);
1673 let data = GenericSharedMemory::from_bytes(data);
1674
1675 handle_potential_webgl_error!(
1676 self,
1677 texture.initialize(
1678 target,
1679 size.width,
1680 size.height,
1681 1,
1682 compression.format,
1683 level,
1684 Some(TexDataType::UnsignedByte)
1685 )
1686 );
1687
1688 self.send_command(WebGLCommand::CompressedTexImage2D {
1689 target: target.as_gl_constant(),
1690 level,
1691 internal_format,
1692 size: Size2D::new(width, height),
1693 data: data.into(),
1694 });
1695
1696 if let Some(fb) = self.bound_draw_framebuffer.get() {
1697 fb.invalidate_texture(&texture);
1698 }
1699 }
1700
1701 #[allow(clippy::too_many_arguments)]
1702 pub(crate) fn compressed_tex_sub_image_2d(
1703 &self,
1704 target: u32,
1705 level: i32,
1706 xoffset: i32,
1707 yoffset: i32,
1708 width: i32,
1709 height: i32,
1710 format: u32,
1711 data: &[u8],
1712 ) {
1713 let validator = CompressedTexSubImage2DValidator::new(
1714 self,
1715 target,
1716 level,
1717 xoffset,
1718 yoffset,
1719 width,
1720 height,
1721 format,
1722 data.len(),
1723 );
1724 let CommonCompressedTexImage2DValidatorResult {
1725 texture: _,
1726 target,
1727 level,
1728 width,
1729 height,
1730 ..
1731 } = match validator.validate() {
1732 Ok(result) => result,
1733 Err(_) => return,
1734 };
1735
1736 let data = GenericSharedMemory::from_bytes(data);
1737
1738 self.send_command(WebGLCommand::CompressedTexSubImage2D {
1739 target: target.as_gl_constant(),
1740 level: level as i32,
1741 xoffset,
1742 yoffset,
1743 size: Size2D::new(width, height),
1744 format,
1745 data: data.into(),
1746 });
1747 }
1748
1749 pub(crate) fn uniform1iv(
1750 &self,
1751 location: Option<&WebGLUniformLocation>,
1752 val: Int32ArrayOrLongSequence,
1753 src_offset: u32,
1754 src_length: u32,
1755 ) {
1756 self.with_location(location, |location| {
1757 match location.type_() {
1758 constants::BOOL |
1759 constants::INT |
1760 constants::SAMPLER_2D |
1761 WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
1762 WebGL2RenderingContextConstants::SAMPLER_3D |
1763 constants::SAMPLER_CUBE => {},
1764 _ => return Err(InvalidOperation),
1765 }
1766
1767 let val = self.uniform_vec_section_int(val, src_offset, src_length, 1, location)?;
1768
1769 match location.type_() {
1770 constants::SAMPLER_2D |
1771 constants::SAMPLER_CUBE |
1772 WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
1773 WebGL2RenderingContextConstants::SAMPLER_3D => {
1774 for &v in val
1775 .iter()
1776 .take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
1777 {
1778 if v < 0 || v as u32 >= self.limits.max_combined_texture_image_units {
1779 return Err(InvalidValue);
1780 }
1781 }
1782 },
1783 _ => {},
1784 }
1785 self.send_command(WebGLCommand::Uniform1iv(location.id(), val));
1786 Ok(())
1787 });
1788 }
1789
1790 pub(crate) fn uniform1fv(
1791 &self,
1792 location: Option<&WebGLUniformLocation>,
1793 val: Float32ArrayOrUnrestrictedFloatSequence,
1794 src_offset: u32,
1795 src_length: u32,
1796 ) {
1797 self.with_location(location, |location| {
1798 match location.type_() {
1799 constants::BOOL | constants::FLOAT => {},
1800 _ => return Err(InvalidOperation),
1801 }
1802 let val = self.uniform_vec_section_float(val, src_offset, src_length, 1, location)?;
1803 self.send_command(WebGLCommand::Uniform1fv(location.id(), val));
1804 Ok(())
1805 });
1806 }
1807
1808 pub(crate) fn uniform2fv(
1809 &self,
1810 location: Option<&WebGLUniformLocation>,
1811 val: Float32ArrayOrUnrestrictedFloatSequence,
1812 src_offset: u32,
1813 src_length: u32,
1814 ) {
1815 self.with_location(location, |location| {
1816 match location.type_() {
1817 constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {},
1818 _ => return Err(InvalidOperation),
1819 }
1820 let val = self.uniform_vec_section_float(val, src_offset, src_length, 2, location)?;
1821 self.send_command(WebGLCommand::Uniform2fv(location.id(), val));
1822 Ok(())
1823 });
1824 }
1825
1826 pub(crate) fn uniform2iv(
1827 &self,
1828 location: Option<&WebGLUniformLocation>,
1829 val: Int32ArrayOrLongSequence,
1830 src_offset: u32,
1831 src_length: u32,
1832 ) {
1833 self.with_location(location, |location| {
1834 match location.type_() {
1835 constants::BOOL_VEC2 | constants::INT_VEC2 => {},
1836 _ => return Err(InvalidOperation),
1837 }
1838 let val = self.uniform_vec_section_int(val, src_offset, src_length, 2, location)?;
1839 self.send_command(WebGLCommand::Uniform2iv(location.id(), val));
1840 Ok(())
1841 });
1842 }
1843
1844 pub(crate) fn uniform3fv(
1845 &self,
1846 location: Option<&WebGLUniformLocation>,
1847 val: Float32ArrayOrUnrestrictedFloatSequence,
1848 src_offset: u32,
1849 src_length: u32,
1850 ) {
1851 self.with_location(location, |location| {
1852 match location.type_() {
1853 constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {},
1854 _ => return Err(InvalidOperation),
1855 }
1856 let val = self.uniform_vec_section_float(val, src_offset, src_length, 3, location)?;
1857 self.send_command(WebGLCommand::Uniform3fv(location.id(), val));
1858 Ok(())
1859 });
1860 }
1861
1862 pub(crate) fn uniform3iv(
1863 &self,
1864 location: Option<&WebGLUniformLocation>,
1865 val: Int32ArrayOrLongSequence,
1866 src_offset: u32,
1867 src_length: u32,
1868 ) {
1869 self.with_location(location, |location| {
1870 match location.type_() {
1871 constants::BOOL_VEC3 | constants::INT_VEC3 => {},
1872 _ => return Err(InvalidOperation),
1873 }
1874 let val = self.uniform_vec_section_int(val, src_offset, src_length, 3, location)?;
1875 self.send_command(WebGLCommand::Uniform3iv(location.id(), val));
1876 Ok(())
1877 });
1878 }
1879
1880 pub(crate) fn uniform4iv(
1881 &self,
1882 location: Option<&WebGLUniformLocation>,
1883 val: Int32ArrayOrLongSequence,
1884 src_offset: u32,
1885 src_length: u32,
1886 ) {
1887 self.with_location(location, |location| {
1888 match location.type_() {
1889 constants::BOOL_VEC4 | constants::INT_VEC4 => {},
1890 _ => return Err(InvalidOperation),
1891 }
1892 let val = self.uniform_vec_section_int(val, src_offset, src_length, 4, location)?;
1893 self.send_command(WebGLCommand::Uniform4iv(location.id(), val));
1894 Ok(())
1895 });
1896 }
1897
1898 pub(crate) fn uniform4fv(
1899 &self,
1900 location: Option<&WebGLUniformLocation>,
1901 val: Float32ArrayOrUnrestrictedFloatSequence,
1902 src_offset: u32,
1903 src_length: u32,
1904 ) {
1905 self.with_location(location, |location| {
1906 match location.type_() {
1907 constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {},
1908 _ => return Err(InvalidOperation),
1909 }
1910 let val = self.uniform_vec_section_float(val, src_offset, src_length, 4, location)?;
1911 self.send_command(WebGLCommand::Uniform4fv(location.id(), val));
1912 Ok(())
1913 });
1914 }
1915
1916 pub(crate) fn uniform_matrix_2fv(
1917 &self,
1918 location: Option<&WebGLUniformLocation>,
1919 transpose: bool,
1920 val: Float32ArrayOrUnrestrictedFloatSequence,
1921 src_offset: u32,
1922 src_length: u32,
1923 ) {
1924 self.with_location(location, |location| {
1925 match location.type_() {
1926 constants::FLOAT_MAT2 => {},
1927 _ => return Err(InvalidOperation),
1928 }
1929 let val =
1930 self.uniform_matrix_section(val, src_offset, src_length, transpose, 4, location)?;
1931 self.send_command(WebGLCommand::UniformMatrix2fv(location.id(), val));
1932 Ok(())
1933 });
1934 }
1935
1936 pub(crate) fn uniform_matrix_3fv(
1937 &self,
1938 location: Option<&WebGLUniformLocation>,
1939 transpose: bool,
1940 val: Float32ArrayOrUnrestrictedFloatSequence,
1941 src_offset: u32,
1942 src_length: u32,
1943 ) {
1944 self.with_location(location, |location| {
1945 match location.type_() {
1946 constants::FLOAT_MAT3 => {},
1947 _ => return Err(InvalidOperation),
1948 }
1949 let val =
1950 self.uniform_matrix_section(val, src_offset, src_length, transpose, 9, location)?;
1951 self.send_command(WebGLCommand::UniformMatrix3fv(location.id(), val));
1952 Ok(())
1953 });
1954 }
1955
1956 pub(crate) fn uniform_matrix_4fv(
1957 &self,
1958 location: Option<&WebGLUniformLocation>,
1959 transpose: bool,
1960 val: Float32ArrayOrUnrestrictedFloatSequence,
1961 src_offset: u32,
1962 src_length: u32,
1963 ) {
1964 self.with_location(location, |location| {
1965 match location.type_() {
1966 constants::FLOAT_MAT4 => {},
1967 _ => return Err(InvalidOperation),
1968 }
1969 let val =
1970 self.uniform_matrix_section(val, src_offset, src_length, transpose, 16, location)?;
1971 self.send_command(WebGLCommand::UniformMatrix4fv(location.id(), val));
1972 Ok(())
1973 });
1974 }
1975
1976 pub(crate) fn get_buffer_param(
1977 &self,
1978 buffer: Option<DomRoot<WebGLBuffer>>,
1979 parameter: u32,
1980 mut retval: MutableHandleValue,
1981 ) {
1982 let buffer = handle_potential_webgl_error!(
1983 self,
1984 buffer.ok_or(InvalidOperation),
1985 return retval.set(NullValue())
1986 );
1987
1988 retval.set(match parameter {
1989 constants::BUFFER_SIZE => Int32Value(buffer.capacity() as i32),
1990 constants::BUFFER_USAGE => Int32Value(buffer.usage() as i32),
1991 _ => {
1992 self.webgl_error(InvalidEnum);
1993 NullValue()
1994 },
1995 })
1996 }
1997}
1998
1999impl CanvasContext for WebGLRenderingContext {
2000 type ID = WebGLContextId;
2001
2002 fn context_id(&self) -> Self::ID {
2003 self.droppable.webgl_sender.context_id()
2004 }
2005
2006 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
2007 Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
2008 }
2009
2010 fn resize(&self) {
2011 let size = self.size().cast();
2012 let (sender, receiver) = webgl_channel().unwrap();
2013 self.droppable
2014 .webgl_sender
2015 .send_resize(size, sender)
2016 .unwrap();
2017 self.size.set(size);
2020 self.reflector_
2021 .update_memory_size(self, size.cast::<usize>().area() * 4);
2022
2023 if let Err(msg) = receiver.recv().unwrap() {
2024 error!("Error resizing WebGLContext: {}", msg);
2025 return;
2026 };
2027
2028 let color = self.current_clear_color.get();
2031 self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
2032
2033 let rect = self.current_scissor.get();
2038 self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
2039
2040 if let Some(texture) = self
2045 .textures
2046 .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
2047 .unwrap()
2048 .get()
2049 {
2050 self.send_command(WebGLCommand::BindTexture(
2051 constants::TEXTURE_2D,
2052 Some(texture.id()),
2053 ));
2054 }
2055
2056 if let Some(fbo) = self.bound_draw_framebuffer.get() {
2060 let id = WebGLFramebufferBindingRequest::Explicit(fbo.id());
2061 self.send_command(WebGLCommand::BindFramebuffer(constants::FRAMEBUFFER, id));
2062 }
2063 }
2064
2065 fn reset_bitmap(&self) {
2066 warn!("The WebGLRenderingContext 'reset_bitmap' is not implemented yet");
2067 }
2068
2069 fn get_image_data(&self) -> Option<Snapshot> {
2076 handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
2077 let mut size = self.size().cast();
2078
2079 let (fb_width, fb_height) = handle_potential_webgl_error!(
2080 self,
2081 self.get_current_framebuffer_size().ok_or(InvalidOperation),
2082 return None
2083 );
2084 size.width = cmp::min(size.width, fb_width as u32);
2085 size.height = cmp::min(size.height, fb_height as u32);
2086
2087 let (sender, receiver) = generic_channel::channel().unwrap();
2088 self.send_command(WebGLCommand::ReadPixels(
2089 Rect::from_size(size),
2090 constants::RGBA,
2091 constants::UNSIGNED_BYTE,
2092 sender,
2093 ));
2094 let (data, alpha_mode) = receiver.recv().unwrap();
2095 Some(Snapshot::from_vec(
2096 size.cast(),
2097 SnapshotPixelFormat::RGBA,
2098 alpha_mode,
2099 data.to_vec(),
2100 ))
2101 }
2102
2103 fn mark_as_dirty(&self) {
2104 if self.bound_draw_framebuffer.get().is_some() {
2106 return;
2107 }
2108
2109 if self.global().as_window().in_immersive_xr_session() {
2112 return;
2113 }
2114
2115 self.canvas.mark_as_dirty();
2116 }
2117}
2118
2119#[cfg(not(feature = "webgl_backtrace"))]
2120#[inline]
2121pub(crate) fn capture_webgl_backtrace() -> WebGLCommandBacktrace {
2122 WebGLCommandBacktrace {}
2123}
2124
2125#[cfg(feature = "webgl_backtrace")]
2126#[cfg_attr(feature = "webgl_backtrace", expect(unsafe_code))]
2127pub(crate) fn capture_webgl_backtrace() -> WebGLCommandBacktrace {
2128 let bt = Backtrace::new();
2129 unsafe {
2130 capture_stack!(in(*GlobalScope::get_cx()) let stack);
2131 WebGLCommandBacktrace {
2132 backtrace: format!("{:?}", bt),
2133 js_backtrace: stack.and_then(|s| s.as_string(None, js::jsapi::StackFormat::Default)),
2134 }
2135 }
2136}
2137
2138impl WebGLRenderingContextMethods<crate::DomTypeHolder> for WebGLRenderingContext {
2139 fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
2141 RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)
2142 }
2143
2144 fn Flush(&self) {
2146 self.send_command(WebGLCommand::Flush);
2147 }
2148
2149 fn Finish(&self) {
2151 let (sender, receiver) = webgl_channel().unwrap();
2152 self.send_command(WebGLCommand::Finish(sender));
2153 receiver.recv().unwrap()
2154 }
2155
2156 fn DrawingBufferWidth(&self) -> i32 {
2158 let (sender, receiver) = webgl_channel().unwrap();
2159 self.send_command(WebGLCommand::DrawingBufferWidth(sender));
2160 receiver.recv().unwrap()
2161 }
2162
2163 fn DrawingBufferHeight(&self) -> i32 {
2165 let (sender, receiver) = webgl_channel().unwrap();
2166 self.send_command(WebGLCommand::DrawingBufferHeight(sender));
2167 receiver.recv().unwrap()
2168 }
2169
2170 fn GetBufferParameter(
2172 &self,
2173 _cx: SafeJSContext,
2174 target: u32,
2175 parameter: u32,
2176 mut retval: MutableHandleValue,
2177 ) {
2178 let buffer = handle_potential_webgl_error!(
2179 self,
2180 self.bound_buffer(target),
2181 return retval.set(NullValue())
2182 );
2183 self.get_buffer_param(buffer, parameter, retval)
2184 }
2185
2186 #[expect(unsafe_code)]
2187 fn GetParameter(&self, cx: SafeJSContext, parameter: u32, mut retval: MutableHandleValue) {
2189 if !self
2190 .extension_manager
2191 .is_get_parameter_name_enabled(parameter)
2192 {
2193 self.webgl_error(WebGLError::InvalidEnum);
2194 return retval.set(NullValue());
2195 }
2196
2197 match parameter {
2198 constants::ARRAY_BUFFER_BINDING => {
2199 self.bound_buffer_array
2200 .get()
2201 .safe_to_jsval(cx, retval, CanGc::deprecated_note());
2202 return;
2203 },
2204 constants::CURRENT_PROGRAM => {
2205 self.current_program
2206 .get()
2207 .safe_to_jsval(cx, retval, CanGc::deprecated_note());
2208 return;
2209 },
2210 constants::ELEMENT_ARRAY_BUFFER_BINDING => {
2211 let buffer = self.current_vao().element_array_buffer().get();
2212 buffer.safe_to_jsval(cx, retval, CanGc::deprecated_note());
2213 return;
2214 },
2215 constants::FRAMEBUFFER_BINDING => {
2216 self.bound_draw_framebuffer.get().safe_to_jsval(
2217 cx,
2218 retval,
2219 CanGc::deprecated_note(),
2220 );
2221 return;
2222 },
2223 constants::RENDERBUFFER_BINDING => {
2224 self.bound_renderbuffer
2225 .get()
2226 .safe_to_jsval(cx, retval, CanGc::deprecated_note());
2227 return;
2228 },
2229 constants::TEXTURE_BINDING_2D => {
2230 let texture = self
2231 .textures
2232 .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
2233 .unwrap()
2234 .get();
2235 texture.safe_to_jsval(cx, retval, CanGc::deprecated_note());
2236 return;
2237 },
2238 WebGL2RenderingContextConstants::TEXTURE_BINDING_2D_ARRAY => {
2239 let texture = self
2240 .textures
2241 .active_texture_slot(
2242 WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY,
2243 self.webgl_version(),
2244 )
2245 .unwrap()
2246 .get();
2247 texture.safe_to_jsval(cx, retval, CanGc::deprecated_note());
2248 return;
2249 },
2250 WebGL2RenderingContextConstants::TEXTURE_BINDING_3D => {
2251 let texture = self
2252 .textures
2253 .active_texture_slot(
2254 WebGL2RenderingContextConstants::TEXTURE_3D,
2255 self.webgl_version(),
2256 )
2257 .unwrap()
2258 .get();
2259 texture.safe_to_jsval(cx, retval, CanGc::deprecated_note());
2260 return;
2261 },
2262 constants::TEXTURE_BINDING_CUBE_MAP => {
2263 let texture = self
2264 .textures
2265 .active_texture_slot(constants::TEXTURE_CUBE_MAP, self.webgl_version())
2266 .unwrap()
2267 .get();
2268 texture.safe_to_jsval(cx, retval, CanGc::deprecated_note());
2269 return;
2270 },
2271 OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES => {
2272 let vao = self.current_vao.get().filter(|vao| vao.id().is_some());
2273 vao.safe_to_jsval(cx, retval, CanGc::deprecated_note());
2274 return;
2275 },
2276 constants::IMPLEMENTATION_COLOR_READ_FORMAT => {
2282 if self.validate_framebuffer().is_err() {
2283 self.webgl_error(InvalidOperation);
2284 return retval.set(NullValue());
2285 }
2286 return retval.set(Int32Value(constants::RGBA as i32));
2287 },
2288 constants::IMPLEMENTATION_COLOR_READ_TYPE => {
2289 if self.validate_framebuffer().is_err() {
2290 self.webgl_error(InvalidOperation);
2291 return retval.set(NullValue());
2292 }
2293 return retval.set(Int32Value(constants::UNSIGNED_BYTE as i32));
2294 },
2295 constants::COMPRESSED_TEXTURE_FORMATS => unsafe {
2296 let format_ids = self.extension_manager.get_tex_compression_ids();
2297
2298 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2299 Uint32Array::create(*cx, CreateWith::Slice(&format_ids), rval.handle_mut())
2300 .unwrap();
2301 return retval.set(ObjectValue(rval.get()));
2302 },
2303 constants::VERSION => {
2304 "WebGL 1.0".safe_to_jsval(cx, retval, CanGc::deprecated_note());
2305 return;
2306 },
2307 constants::RENDERER | constants::VENDOR => {
2308 "Mozilla/Servo".safe_to_jsval(cx, retval, CanGc::deprecated_note());
2309 return;
2310 },
2311 constants::SHADING_LANGUAGE_VERSION => {
2312 "WebGL GLSL ES 1.0".safe_to_jsval(cx, retval, CanGc::deprecated_note());
2313 return;
2314 },
2315 constants::UNPACK_FLIP_Y_WEBGL => {
2316 let unpack = self.texture_unpacking_settings.get();
2317 retval.set(BooleanValue(unpack.contains(TextureUnpacking::FLIP_Y_AXIS)));
2318 return;
2319 },
2320 constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
2321 let unpack = self.texture_unpacking_settings.get();
2322 retval.set(BooleanValue(
2323 unpack.contains(TextureUnpacking::PREMULTIPLY_ALPHA),
2324 ));
2325 return;
2326 },
2327 constants::PACK_ALIGNMENT => {
2328 retval.set(UInt32Value(self.texture_packing_alignment.get() as u32));
2329 return;
2330 },
2331 constants::UNPACK_ALIGNMENT => {
2332 retval.set(UInt32Value(self.texture_unpacking_alignment.get()));
2333 return;
2334 },
2335 constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
2336 let unpack = self.texture_unpacking_settings.get();
2337 retval.set(UInt32Value(
2338 if unpack.contains(TextureUnpacking::CONVERT_COLORSPACE) {
2339 constants::BROWSER_DEFAULT_WEBGL
2340 } else {
2341 constants::NONE
2342 },
2343 ));
2344 return;
2345 },
2346 _ => {},
2347 }
2348
2349 let limit = match parameter {
2352 constants::MAX_VERTEX_ATTRIBS => Some(self.limits.max_vertex_attribs),
2353 constants::MAX_TEXTURE_SIZE => Some(self.limits.max_tex_size),
2354 constants::MAX_CUBE_MAP_TEXTURE_SIZE => Some(self.limits.max_cube_map_tex_size),
2355 constants::MAX_COMBINED_TEXTURE_IMAGE_UNITS => {
2356 Some(self.limits.max_combined_texture_image_units)
2357 },
2358 constants::MAX_FRAGMENT_UNIFORM_VECTORS => {
2359 Some(self.limits.max_fragment_uniform_vectors)
2360 },
2361 constants::MAX_RENDERBUFFER_SIZE => Some(self.limits.max_renderbuffer_size),
2362 constants::MAX_TEXTURE_IMAGE_UNITS => Some(self.limits.max_texture_image_units),
2363 constants::MAX_VARYING_VECTORS => Some(self.limits.max_varying_vectors),
2364 constants::MAX_VERTEX_TEXTURE_IMAGE_UNITS => {
2365 Some(self.limits.max_vertex_texture_image_units)
2366 },
2367 constants::MAX_VERTEX_UNIFORM_VECTORS => Some(self.limits.max_vertex_uniform_vectors),
2368 _ => None,
2369 };
2370 if let Some(limit) = limit {
2371 retval.set(UInt32Value(limit));
2372 return;
2373 }
2374
2375 if let Ok(value) = self.capabilities.is_enabled(parameter) {
2376 retval.set(BooleanValue(value));
2377 return;
2378 }
2379
2380 match handle_potential_webgl_error!(
2381 self,
2382 Parameter::from_u32(parameter),
2383 return retval.set(NullValue())
2384 ) {
2385 Parameter::Bool(param) => {
2386 let (sender, receiver) = webgl_channel().unwrap();
2387 self.send_command(WebGLCommand::GetParameterBool(param, sender));
2388 retval.set(BooleanValue(receiver.recv().unwrap()))
2389 },
2390 Parameter::Bool4(param) => {
2391 let (sender, receiver) = webgl_channel().unwrap();
2392 self.send_command(WebGLCommand::GetParameterBool4(param, sender));
2393 receiver
2394 .recv()
2395 .unwrap()
2396 .safe_to_jsval(cx, retval, CanGc::deprecated_note());
2397 },
2398 Parameter::Int(param) => {
2399 let (sender, receiver) = webgl_channel().unwrap();
2400 self.send_command(WebGLCommand::GetParameterInt(param, sender));
2401 retval.set(Int32Value(receiver.recv().unwrap()))
2402 },
2403 Parameter::Int2(param) => unsafe {
2404 let (sender, receiver) = webgl_channel().unwrap();
2405 self.send_command(WebGLCommand::GetParameterInt2(param, sender));
2406 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2407 Int32Array::create(
2408 *cx,
2409 CreateWith::Slice(&receiver.recv().unwrap()),
2410 rval.handle_mut(),
2411 )
2412 .unwrap();
2413 retval.set(ObjectValue(rval.get()))
2414 },
2415 Parameter::Int4(param) => unsafe {
2416 let (sender, receiver) = webgl_channel().unwrap();
2417 self.send_command(WebGLCommand::GetParameterInt4(param, sender));
2418 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2419 Int32Array::create(
2420 *cx,
2421 CreateWith::Slice(&receiver.recv().unwrap()),
2422 rval.handle_mut(),
2423 )
2424 .unwrap();
2425 retval.set(ObjectValue(rval.get()))
2426 },
2427 Parameter::Float(param) => {
2428 let (sender, receiver) = webgl_channel().unwrap();
2429 self.send_command(WebGLCommand::GetParameterFloat(param, sender));
2430 retval.set(DoubleValue(receiver.recv().unwrap() as f64))
2431 },
2432 Parameter::Float2(param) => unsafe {
2433 let (sender, receiver) = webgl_channel().unwrap();
2434 self.send_command(WebGLCommand::GetParameterFloat2(param, sender));
2435 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2436 Float32Array::create(
2437 *cx,
2438 CreateWith::Slice(&receiver.recv().unwrap()),
2439 rval.handle_mut(),
2440 )
2441 .unwrap();
2442 retval.set(ObjectValue(rval.get()))
2443 },
2444 Parameter::Float4(param) => unsafe {
2445 let (sender, receiver) = webgl_channel().unwrap();
2446 self.send_command(WebGLCommand::GetParameterFloat4(param, sender));
2447 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2448 Float32Array::create(
2449 *cx,
2450 CreateWith::Slice(&receiver.recv().unwrap()),
2451 rval.handle_mut(),
2452 )
2453 .unwrap();
2454 retval.set(ObjectValue(rval.get()))
2455 },
2456 }
2457 }
2458
2459 fn GetTexParameter(
2461 &self,
2462 _cx: SafeJSContext,
2463 target: u32,
2464 pname: u32,
2465 mut retval: MutableHandleValue,
2466 ) {
2467 let texture_slot = handle_potential_webgl_error!(
2468 self,
2469 self.textures
2470 .active_texture_slot(target, self.webgl_version()),
2471 return retval.set(NullValue())
2472 );
2473 let texture = handle_potential_webgl_error!(
2474 self,
2475 texture_slot.get().ok_or(InvalidOperation),
2476 return retval.set(NullValue())
2477 );
2478
2479 if !self
2480 .extension_manager
2481 .is_get_tex_parameter_name_enabled(pname)
2482 {
2483 self.webgl_error(InvalidEnum);
2484 return retval.set(NullValue());
2485 }
2486
2487 match pname {
2488 constants::TEXTURE_MAG_FILTER => return retval.set(UInt32Value(texture.mag_filter())),
2489 constants::TEXTURE_MIN_FILTER => return retval.set(UInt32Value(texture.min_filter())),
2490 _ => {},
2491 }
2492
2493 let texparam = handle_potential_webgl_error!(
2494 self,
2495 TexParameter::from_u32(pname),
2496 return retval.set(NullValue())
2497 );
2498 if self.webgl_version() < texparam.required_webgl_version() {
2499 self.webgl_error(InvalidEnum);
2500 return retval.set(NullValue());
2501 }
2502
2503 if let Some(value) = texture.maybe_get_tex_parameter(texparam) {
2504 match value {
2505 TexParameterValue::Float(v) => retval.set(DoubleValue(v as f64)),
2506 TexParameterValue::Int(v) => retval.set(Int32Value(v)),
2507 TexParameterValue::Bool(v) => retval.set(BooleanValue(v)),
2508 }
2509 return;
2510 }
2511
2512 match texparam {
2513 TexParameter::Float(param) => {
2514 let (sender, receiver) = webgl_channel().unwrap();
2515 self.send_command(WebGLCommand::GetTexParameterFloat(target, param, sender));
2516 retval.set(DoubleValue(receiver.recv().unwrap() as f64))
2517 },
2518 TexParameter::Int(param) => {
2519 let (sender, receiver) = webgl_channel().unwrap();
2520 self.send_command(WebGLCommand::GetTexParameterInt(target, param, sender));
2521 retval.set(Int32Value(receiver.recv().unwrap()))
2522 },
2523 TexParameter::Bool(param) => {
2524 let (sender, receiver) = webgl_channel().unwrap();
2525 self.send_command(WebGLCommand::GetTexParameterBool(target, param, sender));
2526 retval.set(BooleanValue(receiver.recv().unwrap()))
2527 },
2528 }
2529 }
2530
2531 fn GetError(&self) -> u32 {
2533 let error_code = if let Some(error) = self.last_error.get() {
2534 match error {
2535 WebGLError::InvalidEnum => constants::INVALID_ENUM,
2536 WebGLError::InvalidFramebufferOperation => constants::INVALID_FRAMEBUFFER_OPERATION,
2537 WebGLError::InvalidValue => constants::INVALID_VALUE,
2538 WebGLError::InvalidOperation => constants::INVALID_OPERATION,
2539 WebGLError::OutOfMemory => constants::OUT_OF_MEMORY,
2540 WebGLError::ContextLost => constants::CONTEXT_LOST_WEBGL,
2541 }
2542 } else {
2543 constants::NO_ERROR
2544 };
2545 self.last_error.set(None);
2546 error_code
2547 }
2548
2549 fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
2551 let (sender, receiver) = webgl_channel().unwrap();
2552
2553 let backtrace = capture_webgl_backtrace();
2555 if self
2556 .droppable
2557 .webgl_sender
2558 .send(WebGLCommand::GetContextAttributes(sender), backtrace)
2559 .is_err()
2560 {
2561 return None;
2562 }
2563
2564 let attrs = receiver.recv().unwrap();
2565
2566 Some(WebGLContextAttributes {
2567 alpha: attrs.alpha,
2568 antialias: attrs.antialias,
2569 depth: attrs.depth,
2570 failIfMajorPerformanceCaveat: false,
2571 preferLowPowerToHighPerformance: false,
2572 premultipliedAlpha: attrs.premultiplied_alpha,
2573 preserveDrawingBuffer: attrs.preserve_drawing_buffer,
2574 stencil: attrs.stencil,
2575 })
2576 }
2577
2578 fn IsContextLost(&self) -> bool {
2580 false
2581 }
2582
2583 fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
2585 self.extension_manager
2586 .init_once(|| self.get_gl_extensions());
2587 let extensions = self.extension_manager.get_supported_extensions();
2588 Some(
2589 extensions
2590 .iter()
2591 .map(|name| DOMString::from(*name))
2592 .collect(),
2593 )
2594 }
2595
2596 fn GetExtension(&self, _cx: SafeJSContext, name: DOMString) -> Option<NonNull<JSObject>> {
2598 self.extension_manager
2599 .init_once(|| self.get_gl_extensions());
2600 self.extension_manager.get_or_init_extension(&name, self)
2601 }
2602
2603 fn ActiveTexture(&self, texture: u32) {
2605 handle_potential_webgl_error!(self, self.textures.set_active_unit_enum(texture), return);
2606 self.send_command(WebGLCommand::ActiveTexture(texture));
2607 }
2608
2609 fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
2611 self.send_command(WebGLCommand::BlendColor(r, g, b, a));
2612 }
2613
2614 fn BlendEquation(&self, mode: u32) {
2616 handle_potential_webgl_error!(self, self.validate_blend_mode(mode), return);
2617 self.send_command(WebGLCommand::BlendEquation(mode))
2618 }
2619
2620 fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
2622 handle_potential_webgl_error!(self, self.validate_blend_mode(mode_rgb), return);
2623 handle_potential_webgl_error!(self, self.validate_blend_mode(mode_alpha), return);
2624 self.send_command(WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha));
2625 }
2626
2627 fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
2629 if has_invalid_blend_constants(src_factor, dest_factor) {
2635 return self.webgl_error(InvalidOperation);
2636 }
2637 if has_invalid_blend_constants(dest_factor, src_factor) {
2638 return self.webgl_error(InvalidOperation);
2639 }
2640
2641 self.send_command(WebGLCommand::BlendFunc(src_factor, dest_factor));
2642 }
2643
2644 fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
2646 if has_invalid_blend_constants(src_rgb, dest_rgb) {
2652 return self.webgl_error(InvalidOperation);
2653 }
2654 if has_invalid_blend_constants(dest_rgb, src_rgb) {
2655 return self.webgl_error(InvalidOperation);
2656 }
2657
2658 self.send_command(WebGLCommand::BlendFuncSeparate(
2659 src_rgb, dest_rgb, src_alpha, dest_alpha,
2660 ));
2661 }
2662
2663 fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
2665 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
2666 handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
2667 handle_potential_webgl_error!(self, program.attach_shader(shader));
2668 }
2669
2670 fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
2672 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
2673 handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
2674 handle_potential_webgl_error!(self, program.detach_shader(shader));
2675 }
2676
2677 fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
2679 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
2680 handle_potential_webgl_error!(self, program.bind_attrib_location(index, name));
2681 }
2682
2683 fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
2685 let current_vao;
2686 let slot = match target {
2687 constants::ARRAY_BUFFER => &self.bound_buffer_array,
2688 constants::ELEMENT_ARRAY_BUFFER => {
2689 current_vao = self.current_vao();
2690 current_vao.element_array_buffer()
2691 },
2692 _ => return self.webgl_error(InvalidEnum),
2693 };
2694 self.bind_buffer_maybe(slot, target, buffer);
2695 }
2696
2697 fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
2699 handle_potential_webgl_error!(
2700 self,
2701 self.validate_new_framebuffer_binding(framebuffer),
2702 return
2703 );
2704
2705 if target != constants::FRAMEBUFFER {
2706 return self.webgl_error(InvalidEnum);
2707 }
2708
2709 self.bind_framebuffer_to(target, framebuffer, &self.bound_draw_framebuffer)
2710 }
2711
2712 fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
2714 if let Some(rb) = renderbuffer {
2715 handle_potential_webgl_error!(self, self.validate_ownership(rb), return);
2716 }
2717
2718 if target != constants::RENDERBUFFER {
2719 return self.webgl_error(InvalidEnum);
2720 }
2721
2722 match renderbuffer {
2723 Some(renderbuffer) if !renderbuffer.is_deleted() => {
2727 self.bound_renderbuffer.set(Some(renderbuffer));
2728 renderbuffer.bind(target);
2729 },
2730 _ => {
2731 if renderbuffer.is_some() {
2732 self.webgl_error(InvalidOperation);
2733 }
2734
2735 self.bound_renderbuffer.set(None);
2736 self.send_command(WebGLCommand::BindRenderbuffer(target, None));
2738 },
2739 }
2740 }
2741
2742 fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
2744 if let Some(texture) = texture {
2745 handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
2746 }
2747
2748 let texture_slot = handle_potential_webgl_error!(
2749 self,
2750 self.textures
2751 .active_texture_slot(target, self.webgl_version()),
2752 return
2753 );
2754
2755 if let Some(texture) = texture {
2756 handle_potential_webgl_error!(self, texture.bind(target), return);
2757 } else {
2758 self.send_command(WebGLCommand::BindTexture(target, None));
2759 }
2760 texture_slot.set(texture);
2761 }
2762
2763 fn GenerateMipmap(&self, target: u32) {
2765 let texture_slot = handle_potential_webgl_error!(
2766 self,
2767 self.textures
2768 .active_texture_slot(target, self.webgl_version()),
2769 return
2770 );
2771 let texture =
2772 handle_potential_webgl_error!(self, texture_slot.get().ok_or(InvalidOperation), return);
2773 handle_potential_webgl_error!(self, texture.generate_mipmap());
2774 }
2775
2776 fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) {
2778 let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return);
2779 let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
2780 self.buffer_data(target, data, usage, bound_buffer)
2781 }
2782
2783 fn BufferData(&self, target: u32, size: i64, usage: u32) {
2785 let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return);
2786 let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
2787 self.buffer_data_(target, size, usage, bound_buffer)
2788 }
2789
2790 fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) {
2792 let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
2793 self.buffer_sub_data(target, offset, data, bound_buffer)
2794 }
2795
2796 #[expect(unsafe_code)]
2798 fn CompressedTexImage2D(
2799 &self,
2800 target: u32,
2801 level: i32,
2802 internal_format: u32,
2803 width: i32,
2804 height: i32,
2805 border: i32,
2806 data: CustomAutoRooterGuard<ArrayBufferView>,
2807 ) {
2808 let data = unsafe { data.as_slice() };
2809 self.compressed_tex_image_2d(target, level, internal_format, width, height, border, data)
2810 }
2811
2812 #[expect(unsafe_code)]
2814 fn CompressedTexSubImage2D(
2815 &self,
2816 target: u32,
2817 level: i32,
2818 xoffset: i32,
2819 yoffset: i32,
2820 width: i32,
2821 height: i32,
2822 format: u32,
2823 data: CustomAutoRooterGuard<ArrayBufferView>,
2824 ) {
2825 let data = unsafe { data.as_slice() };
2826 self.compressed_tex_sub_image_2d(
2827 target, level, xoffset, yoffset, width, height, format, data,
2828 )
2829 }
2830
2831 fn CopyTexImage2D(
2833 &self,
2834 target: u32,
2835 level: i32,
2836 internal_format: u32,
2837 x: i32,
2838 y: i32,
2839 width: i32,
2840 height: i32,
2841 border: i32,
2842 ) {
2843 handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
2844
2845 let validator = CommonTexImage2DValidator::new(
2846 self,
2847 target,
2848 level,
2849 internal_format,
2850 width,
2851 height,
2852 border,
2853 );
2854 let CommonTexImage2DValidatorResult {
2855 texture,
2856 target,
2857 level,
2858 internal_format,
2859 width,
2860 height,
2861 border,
2862 } = match validator.validate() {
2863 Ok(result) => result,
2864 Err(_) => return,
2865 };
2866
2867 if texture.is_immutable() {
2868 return self.webgl_error(InvalidOperation);
2869 }
2870
2871 let framebuffer_format = match self.bound_draw_framebuffer.get() {
2872 Some(fb) => match fb.attachment(constants::COLOR_ATTACHMENT0) {
2873 Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => {
2874 TexFormat::from_gl_constant(rb.internal_format())
2875 },
2876 Some(WebGLFramebufferAttachmentRoot::Texture(texture)) => texture
2877 .image_info_for_target(&target, 0)
2878 .map(|info| info.internal_format()),
2879 None => None,
2880 },
2881 None => {
2882 let attrs = self.GetContextAttributes().unwrap();
2883 Some(if attrs.alpha {
2884 TexFormat::RGBA
2885 } else {
2886 TexFormat::RGB
2887 })
2888 },
2889 };
2890
2891 let framebuffer_format = match framebuffer_format {
2892 Some(f) => f,
2893 None => {
2894 self.webgl_error(InvalidOperation);
2895 return;
2896 },
2897 };
2898
2899 match (framebuffer_format, internal_format) {
2900 (a, b) if a == b => (),
2901 (TexFormat::RGBA, TexFormat::RGB) => (),
2902 (TexFormat::RGBA, TexFormat::Alpha) => (),
2903 (TexFormat::RGBA, TexFormat::Luminance) => (),
2904 (TexFormat::RGBA, TexFormat::LuminanceAlpha) => (),
2905 (TexFormat::RGB, TexFormat::Luminance) => (),
2906 _ => {
2907 self.webgl_error(InvalidOperation);
2908 return;
2909 },
2910 }
2911
2912 handle_potential_webgl_error!(
2914 self,
2915 texture.initialize(target, width, height, 1, internal_format, level, None)
2916 );
2917
2918 let msg = WebGLCommand::CopyTexImage2D(
2919 target.as_gl_constant(),
2920 level as i32,
2921 internal_format.as_gl_constant(),
2922 x,
2923 y,
2924 width as i32,
2925 height as i32,
2926 border as i32,
2927 );
2928
2929 self.send_command(msg);
2930
2931 if let Some(framebuffer) = self.bound_draw_framebuffer.get() {
2932 framebuffer.invalidate_texture(&texture);
2933 }
2934 }
2935
2936 fn CopyTexSubImage2D(
2938 &self,
2939 target: u32,
2940 level: i32,
2941 xoffset: i32,
2942 yoffset: i32,
2943 x: i32,
2944 y: i32,
2945 width: i32,
2946 height: i32,
2947 ) {
2948 handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
2949
2950 let validator = CommonTexImage2DValidator::new(
2953 self,
2954 target,
2955 level,
2956 TexFormat::RGBA.as_gl_constant(),
2957 width,
2958 height,
2959 0,
2960 );
2961 let CommonTexImage2DValidatorResult {
2962 texture,
2963 target,
2964 level,
2965 width,
2966 height,
2967 ..
2968 } = match validator.validate() {
2969 Ok(result) => result,
2970 Err(_) => return,
2971 };
2972
2973 let image_info = match texture.image_info_for_target(&target, level) {
2974 Some(info) => info,
2975 None => return self.webgl_error(InvalidOperation),
2976 };
2977
2978 if xoffset < 0 ||
2983 (xoffset as u32 + width) > image_info.width() ||
2984 yoffset < 0 ||
2985 (yoffset as u32 + height) > image_info.height()
2986 {
2987 self.webgl_error(InvalidValue);
2988 return;
2989 }
2990
2991 let msg = WebGLCommand::CopyTexSubImage2D(
2992 target.as_gl_constant(),
2993 level as i32,
2994 xoffset,
2995 yoffset,
2996 x,
2997 y,
2998 width as i32,
2999 height as i32,
3000 );
3001
3002 self.send_command(msg);
3003 }
3004
3005 fn Clear(&self, mask: u32) {
3007 handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
3008 if mask &
3009 !(constants::DEPTH_BUFFER_BIT |
3010 constants::STENCIL_BUFFER_BIT |
3011 constants::COLOR_BUFFER_BIT) !=
3012 0
3013 {
3014 return self.webgl_error(InvalidValue);
3015 }
3016
3017 self.send_command(WebGLCommand::Clear(mask));
3018 self.mark_as_dirty();
3019 }
3020
3021 fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
3023 self.current_clear_color.set((red, green, blue, alpha));
3024 self.send_command(WebGLCommand::ClearColor(red, green, blue, alpha));
3025 }
3026
3027 fn ClearDepth(&self, depth: f32) {
3029 self.send_command(WebGLCommand::ClearDepth(depth))
3030 }
3031
3032 fn ClearStencil(&self, stencil: i32) {
3034 self.send_command(WebGLCommand::ClearStencil(stencil))
3035 }
3036
3037 fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
3039 self.send_command(WebGLCommand::ColorMask(r, g, b, a))
3040 }
3041
3042 fn CullFace(&self, mode: u32) {
3044 match mode {
3045 constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => {
3046 self.send_command(WebGLCommand::CullFace(mode))
3047 },
3048 _ => self.webgl_error(InvalidEnum),
3049 }
3050 }
3051
3052 fn FrontFace(&self, mode: u32) {
3054 match mode {
3055 constants::CW | constants::CCW => self.send_command(WebGLCommand::FrontFace(mode)),
3056 _ => self.webgl_error(InvalidEnum),
3057 }
3058 }
3059 fn DepthFunc(&self, func: u32) {
3061 match func {
3062 constants::NEVER |
3063 constants::LESS |
3064 constants::EQUAL |
3065 constants::LEQUAL |
3066 constants::GREATER |
3067 constants::NOTEQUAL |
3068 constants::GEQUAL |
3069 constants::ALWAYS => self.send_command(WebGLCommand::DepthFunc(func)),
3070 _ => self.webgl_error(InvalidEnum),
3071 }
3072 }
3073
3074 fn DepthMask(&self, flag: bool) {
3076 self.send_command(WebGLCommand::DepthMask(flag))
3077 }
3078
3079 fn DepthRange(&self, near: f32, far: f32) {
3081 if near > far {
3083 return self.webgl_error(InvalidOperation);
3084 }
3085 self.send_command(WebGLCommand::DepthRange(near, far))
3086 }
3087
3088 fn Enable(&self, cap: u32) {
3090 if handle_potential_webgl_error!(self, self.capabilities.set(cap, true), return) {
3091 self.send_command(WebGLCommand::Enable(cap));
3092 }
3093 }
3094
3095 fn Disable(&self, cap: u32) {
3097 if handle_potential_webgl_error!(self, self.capabilities.set(cap, false), return) {
3098 self.send_command(WebGLCommand::Disable(cap));
3099 }
3100 }
3101
3102 fn CompileShader(&self, shader: &WebGLShader) {
3104 handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
3105 handle_potential_webgl_error!(
3106 self,
3107 shader.compile(
3108 self.api_type,
3109 self.webgl_version,
3110 self.glsl_version,
3111 &self.limits,
3112 &self.extension_manager,
3113 )
3114 )
3115 }
3116
3117 fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> {
3119 WebGLBuffer::maybe_new(self, CanGc::deprecated_note())
3120 }
3121
3122 fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
3124 WebGLFramebuffer::maybe_new(self, CanGc::deprecated_note())
3125 }
3126
3127 fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
3129 WebGLRenderbuffer::maybe_new(self)
3130 }
3131
3132 fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> {
3134 WebGLTexture::maybe_new(self)
3135 }
3136
3137 fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> {
3139 WebGLProgram::maybe_new(self, CanGc::deprecated_note())
3140 }
3141
3142 fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
3144 match shader_type {
3145 constants::VERTEX_SHADER | constants::FRAGMENT_SHADER => {},
3146 _ => {
3147 self.webgl_error(InvalidEnum);
3148 return None;
3149 },
3150 }
3151 WebGLShader::maybe_new(self, shader_type)
3152 }
3153
3154 fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
3156 let buffer = match buffer {
3157 Some(buffer) => buffer,
3158 None => return,
3159 };
3160 handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
3161 if buffer.is_marked_for_deletion() {
3162 return;
3163 }
3164 self.current_vao().unbind_buffer(buffer);
3165 if self.bound_buffer_array.get().is_some_and(|b| buffer == &*b) {
3166 self.bound_buffer_array.set(None);
3167 buffer.decrement_attached_counter(Operation::Infallible);
3168 }
3169 buffer.mark_for_deletion(Operation::Infallible);
3170 }
3171
3172 fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
3174 if let Some(framebuffer) = framebuffer {
3175 handle_potential_webgl_error!(self, framebuffer.validate_transparent(), return);
3179 handle_potential_webgl_error!(self, self.validate_ownership(framebuffer), return);
3180 handle_object_deletion!(
3181 self,
3182 self.bound_draw_framebuffer,
3183 framebuffer,
3184 Some(WebGLCommand::BindFramebuffer(
3185 framebuffer.target().unwrap(),
3186 WebGLFramebufferBindingRequest::Default
3187 ))
3188 );
3189 framebuffer.delete(Operation::Infallible)
3190 }
3191 }
3192
3193 fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
3195 if let Some(renderbuffer) = renderbuffer {
3196 handle_potential_webgl_error!(self, self.validate_ownership(renderbuffer), return);
3197 handle_object_deletion!(
3198 self,
3199 self.bound_renderbuffer,
3200 renderbuffer,
3201 Some(WebGLCommand::BindRenderbuffer(
3202 constants::RENDERBUFFER,
3203 None
3204 ))
3205 );
3206 renderbuffer.delete(Operation::Infallible)
3207 }
3208 }
3209
3210 fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
3212 if let Some(texture) = texture {
3213 handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
3214
3215 let mut active_unit_enum = self.textures.active_unit_enum();
3224 for (unit_enum, slot) in self.textures.iter() {
3225 if let Some(target) = slot.unbind(texture) {
3226 if unit_enum != active_unit_enum {
3227 self.send_command(WebGLCommand::ActiveTexture(unit_enum));
3228 active_unit_enum = unit_enum;
3229 }
3230 self.send_command(WebGLCommand::BindTexture(target, None));
3231 }
3232 }
3233
3234 if active_unit_enum != self.textures.active_unit_enum() {
3236 self.send_command(WebGLCommand::ActiveTexture(
3237 self.textures.active_unit_enum(),
3238 ));
3239 }
3240
3241 texture.delete(Operation::Infallible)
3242 }
3243 }
3244
3245 fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
3247 if let Some(program) = program {
3248 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
3249 program.mark_for_deletion(Operation::Infallible)
3250 }
3251 }
3252
3253 fn DeleteShader(&self, shader: Option<&WebGLShader>) {
3255 if let Some(shader) = shader {
3256 handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
3257 shader.mark_for_deletion(Operation::Infallible)
3258 }
3259 }
3260
3261 fn DrawArrays(&self, mode: u32, first: i32, count: i32) {
3263 handle_potential_webgl_error!(self, self.draw_arrays_instanced(mode, first, count, 1));
3264 }
3265
3266 fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
3268 handle_potential_webgl_error!(
3269 self,
3270 self.draw_elements_instanced(mode, count, type_, offset, 1)
3271 );
3272 }
3273
3274 fn EnableVertexAttribArray(&self, attrib_id: u32) {
3276 if attrib_id >= self.limits.max_vertex_attribs {
3277 return self.webgl_error(InvalidValue);
3278 }
3279 match self.webgl_version() {
3280 WebGLVersion::WebGL1 => self
3281 .current_vao()
3282 .enabled_vertex_attrib_array(attrib_id, true),
3283 WebGLVersion::WebGL2 => self
3284 .current_vao_webgl2()
3285 .enabled_vertex_attrib_array(attrib_id, true),
3286 };
3287 self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id));
3288 }
3289
3290 fn DisableVertexAttribArray(&self, attrib_id: u32) {
3292 if attrib_id >= self.limits.max_vertex_attribs {
3293 return self.webgl_error(InvalidValue);
3294 }
3295 match self.webgl_version() {
3296 WebGLVersion::WebGL1 => self
3297 .current_vao()
3298 .enabled_vertex_attrib_array(attrib_id, false),
3299 WebGLVersion::WebGL2 => self
3300 .current_vao_webgl2()
3301 .enabled_vertex_attrib_array(attrib_id, false),
3302 };
3303 self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id));
3304 }
3305
3306 fn GetActiveUniform(
3308 &self,
3309 program: &WebGLProgram,
3310 index: u32,
3311 ) -> Option<DomRoot<WebGLActiveInfo>> {
3312 handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3313 match program.get_active_uniform(index, CanGc::deprecated_note()) {
3314 Ok(ret) => Some(ret),
3315 Err(e) => {
3316 self.webgl_error(e);
3317 None
3318 },
3319 }
3320 }
3321
3322 fn GetActiveAttrib(
3324 &self,
3325 program: &WebGLProgram,
3326 index: u32,
3327 ) -> Option<DomRoot<WebGLActiveInfo>> {
3328 handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3329 handle_potential_webgl_error!(
3330 self,
3331 program
3332 .get_active_attrib(index, CanGc::deprecated_note())
3333 .map(Some),
3334 None
3335 )
3336 }
3337
3338 fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
3340 handle_potential_webgl_error!(self, self.validate_ownership(program), return -1);
3341 handle_potential_webgl_error!(self, program.get_attrib_location(name), -1)
3342 }
3343
3344 fn GetFramebufferAttachmentParameter(
3346 &self,
3347 cx: SafeJSContext,
3348 target: u32,
3349 attachment: u32,
3350 pname: u32,
3351 mut retval: MutableHandleValue,
3352 ) {
3353 if let Some(fb) = self.bound_draw_framebuffer.get() {
3355 handle_potential_webgl_error!(
3358 self,
3359 fb.validate_transparent(),
3360 return retval.set(NullValue())
3361 );
3362 } else {
3363 self.webgl_error(InvalidOperation);
3364 return retval.set(NullValue());
3365 }
3366
3367 let target_matches = match target {
3369 constants::FRAMEBUFFER => true,
3372 _ => false,
3373 };
3374 let attachment_matches = match attachment {
3375 constants::COLOR_ATTACHMENT0 |
3378 constants::DEPTH_STENCIL_ATTACHMENT |
3379 constants::DEPTH_ATTACHMENT |
3380 constants::STENCIL_ATTACHMENT => true,
3381 _ => false,
3382 };
3383 let pname_matches = match pname {
3384 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME |
3394 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
3395 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
3396 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL => true,
3397 _ => false,
3398 };
3399
3400 let bound_attachment_matches = match self
3401 .bound_draw_framebuffer
3402 .get()
3403 .unwrap()
3404 .attachment(attachment)
3405 {
3406 Some(attachment_root) => match attachment_root {
3407 WebGLFramebufferAttachmentRoot::Renderbuffer(_) => matches!(
3408 pname,
3409 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
3410 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
3411 ),
3412 WebGLFramebufferAttachmentRoot::Texture(_) => matches!(
3413 pname,
3414 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
3415 constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME |
3416 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
3417 constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE
3418 ),
3419 },
3420 _ => matches!(pname, constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE),
3421 };
3422
3423 if !target_matches || !attachment_matches || !pname_matches || !bound_attachment_matches {
3424 self.webgl_error(InvalidEnum);
3425 return retval.set(NullValue());
3426 }
3427
3428 if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
3435 let fb = self.bound_draw_framebuffer.get().unwrap();
3438 if let Some(webgl_attachment) = fb.attachment(attachment) {
3439 match webgl_attachment {
3440 WebGLFramebufferAttachmentRoot::Renderbuffer(rb) => {
3441 rb.safe_to_jsval(cx, retval, CanGc::deprecated_note());
3442 return;
3443 },
3444 WebGLFramebufferAttachmentRoot::Texture(texture) => {
3445 texture.safe_to_jsval(cx, retval, CanGc::deprecated_note());
3446 return;
3447 },
3448 }
3449 }
3450 self.webgl_error(InvalidEnum);
3451 return retval.set(NullValue());
3452 }
3453
3454 let (sender, receiver) = webgl_channel().unwrap();
3455 self.send_command(WebGLCommand::GetFramebufferAttachmentParameter(
3456 target, attachment, pname, sender,
3457 ));
3458
3459 retval.set(Int32Value(receiver.recv().unwrap()))
3460 }
3461
3462 fn GetRenderbufferParameter(
3464 &self,
3465 _cx: SafeJSContext,
3466 target: u32,
3467 pname: u32,
3468 mut retval: MutableHandleValue,
3469 ) {
3470 let target_matches = target == constants::RENDERBUFFER;
3473
3474 let pname_matches = matches!(
3475 pname,
3476 constants::RENDERBUFFER_WIDTH |
3477 constants::RENDERBUFFER_HEIGHT |
3478 constants::RENDERBUFFER_INTERNAL_FORMAT |
3479 constants::RENDERBUFFER_RED_SIZE |
3480 constants::RENDERBUFFER_GREEN_SIZE |
3481 constants::RENDERBUFFER_BLUE_SIZE |
3482 constants::RENDERBUFFER_ALPHA_SIZE |
3483 constants::RENDERBUFFER_DEPTH_SIZE |
3484 constants::RENDERBUFFER_STENCIL_SIZE
3485 );
3486
3487 if !target_matches || !pname_matches {
3488 self.webgl_error(InvalidEnum);
3489 return retval.set(NullValue());
3490 }
3491
3492 if self.bound_renderbuffer.get().is_none() {
3493 self.webgl_error(InvalidOperation);
3494 return retval.set(NullValue());
3495 }
3496
3497 let result = if pname == constants::RENDERBUFFER_INTERNAL_FORMAT {
3498 let rb = self.bound_renderbuffer.get().unwrap();
3499 rb.internal_format() as i32
3500 } else {
3501 let (sender, receiver) = webgl_channel().unwrap();
3502 self.send_command(WebGLCommand::GetRenderbufferParameter(
3503 target, pname, sender,
3504 ));
3505 receiver.recv().unwrap()
3506 };
3507
3508 retval.set(Int32Value(result))
3509 }
3510
3511 fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
3513 handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3514 match program.get_info_log() {
3515 Ok(value) => Some(DOMString::from(value)),
3516 Err(e) => {
3517 self.webgl_error(e);
3518 None
3519 },
3520 }
3521 }
3522
3523 fn GetProgramParameter(
3525 &self,
3526 _: SafeJSContext,
3527 program: &WebGLProgram,
3528 param: u32,
3529 mut retval: MutableHandleValue,
3530 ) {
3531 handle_potential_webgl_error!(
3532 self,
3533 self.validate_ownership(program),
3534 return retval.set(NullValue())
3535 );
3536 if program.is_deleted() {
3537 self.webgl_error(InvalidOperation);
3538 return retval.set(NullValue());
3539 }
3540 retval.set(match param {
3541 constants::DELETE_STATUS => BooleanValue(program.is_marked_for_deletion()),
3542 constants::LINK_STATUS => BooleanValue(program.is_linked()),
3543 constants::VALIDATE_STATUS => {
3544 let (sender, receiver) = webgl_channel().unwrap();
3547 self.send_command(WebGLCommand::GetProgramValidateStatus(program.id(), sender));
3548 BooleanValue(receiver.recv().unwrap())
3549 },
3550 constants::ATTACHED_SHADERS => {
3551 Int32Value(
3553 program
3554 .attached_shaders()
3555 .map(|shaders| shaders.len() as i32)
3556 .unwrap_or(0),
3557 )
3558 },
3559 constants::ACTIVE_ATTRIBUTES => Int32Value(program.active_attribs().len() as i32),
3560 constants::ACTIVE_UNIFORMS => Int32Value(program.active_uniforms().len() as i32),
3561 _ => {
3562 self.webgl_error(InvalidEnum);
3563 NullValue()
3564 },
3565 })
3566 }
3567
3568 fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
3570 handle_potential_webgl_error!(self, self.validate_ownership(shader), return None);
3571 Some(shader.info_log())
3572 }
3573
3574 fn GetShaderParameter(
3576 &self,
3577 _: SafeJSContext,
3578 shader: &WebGLShader,
3579 param: u32,
3580 mut retval: MutableHandleValue,
3581 ) {
3582 handle_potential_webgl_error!(
3583 self,
3584 self.validate_ownership(shader),
3585 return retval.set(NullValue())
3586 );
3587 if shader.is_deleted() {
3588 self.webgl_error(InvalidValue);
3589 return retval.set(NullValue());
3590 }
3591 retval.set(match param {
3592 constants::DELETE_STATUS => BooleanValue(shader.is_marked_for_deletion()),
3593 constants::COMPILE_STATUS => BooleanValue(shader.successfully_compiled()),
3594 constants::SHADER_TYPE => UInt32Value(shader.gl_type()),
3595 _ => {
3596 self.webgl_error(InvalidEnum);
3597 NullValue()
3598 },
3599 })
3600 }
3601
3602 fn GetShaderPrecisionFormat(
3604 &self,
3605 shader_type: u32,
3606 precision_type: u32,
3607 ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
3608 match shader_type {
3609 constants::FRAGMENT_SHADER | constants::VERTEX_SHADER => (),
3610 _ => {
3611 self.webgl_error(InvalidEnum);
3612 return None;
3613 },
3614 }
3615
3616 match precision_type {
3617 constants::LOW_FLOAT |
3618 constants::MEDIUM_FLOAT |
3619 constants::HIGH_FLOAT |
3620 constants::LOW_INT |
3621 constants::MEDIUM_INT |
3622 constants::HIGH_INT => (),
3623 _ => {
3624 self.webgl_error(InvalidEnum);
3625 return None;
3626 },
3627 }
3628
3629 let (sender, receiver) = webgl_channel().unwrap();
3630 self.send_command(WebGLCommand::GetShaderPrecisionFormat(
3631 shader_type,
3632 precision_type,
3633 sender,
3634 ));
3635
3636 let (range_min, range_max, precision) = receiver.recv().unwrap();
3637 Some(WebGLShaderPrecisionFormat::new(
3638 self.global().as_window(),
3639 range_min,
3640 range_max,
3641 precision,
3642 CanGc::deprecated_note(),
3643 ))
3644 }
3645
3646 fn GetUniformLocation(
3648 &self,
3649 program: &WebGLProgram,
3650 name: DOMString,
3651 ) -> Option<DomRoot<WebGLUniformLocation>> {
3652 handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3653 handle_potential_webgl_error!(
3654 self,
3655 program.get_uniform_location(name, CanGc::deprecated_note()),
3656 None
3657 )
3658 }
3659
3660 #[expect(unsafe_code)]
3661 fn GetVertexAttrib(
3663 &self,
3664 cx: SafeJSContext,
3665 index: u32,
3666 param: u32,
3667 mut retval: MutableHandleValue,
3668 ) {
3669 let mut get_attrib = |data: Ref<'_, VertexAttribData>| {
3670 if param == constants::CURRENT_VERTEX_ATTRIB {
3671 let attrib = self.current_vertex_attribs.borrow()[index as usize];
3672 match attrib {
3673 VertexAttrib::Float(x, y, z, w) => {
3674 let value = [x, y, z, w];
3675 unsafe {
3676 rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
3677 Float32Array::create(
3678 *cx,
3679 CreateWith::Slice(&value),
3680 result.handle_mut(),
3681 )
3682 .unwrap();
3683 return retval.set(ObjectValue(result.get()));
3684 }
3685 },
3686 VertexAttrib::Int(x, y, z, w) => {
3687 let value = [x, y, z, w];
3688 unsafe {
3689 rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
3690 Int32Array::create(*cx, CreateWith::Slice(&value), result.handle_mut())
3691 .unwrap();
3692 return retval.set(ObjectValue(result.get()));
3693 }
3694 },
3695 VertexAttrib::Uint(x, y, z, w) => {
3696 let value = [x, y, z, w];
3697 unsafe {
3698 rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
3699 Uint32Array::create(
3700 *cx,
3701 CreateWith::Slice(&value),
3702 result.handle_mut(),
3703 )
3704 .unwrap();
3705 return retval.set(ObjectValue(result.get()));
3706 }
3707 },
3708 };
3709 }
3710 if !self
3711 .extension_manager
3712 .is_get_vertex_attrib_name_enabled(param)
3713 {
3714 self.webgl_error(WebGLError::InvalidEnum);
3715 return retval.set(NullValue());
3716 }
3717
3718 match param {
3719 constants::VERTEX_ATTRIB_ARRAY_ENABLED => {
3720 retval.set(BooleanValue(data.enabled_as_array))
3721 },
3722 constants::VERTEX_ATTRIB_ARRAY_SIZE => retval.set(Int32Value(data.size as i32)),
3723 constants::VERTEX_ATTRIB_ARRAY_TYPE => retval.set(Int32Value(data.type_ as i32)),
3724 constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => {
3725 retval.set(BooleanValue(data.normalized))
3726 },
3727 constants::VERTEX_ATTRIB_ARRAY_STRIDE => retval.set(Int32Value(data.stride as i32)),
3728 constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => {
3729 if let Some(buffer) = data.buffer() {
3730 buffer.safe_to_jsval(cx, retval.reborrow(), CanGc::deprecated_note());
3731 } else {
3732 retval.set(NullValue());
3733 }
3734 },
3735 ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => {
3736 retval.set(UInt32Value(data.divisor))
3737 },
3738 _ => {
3739 self.webgl_error(InvalidEnum);
3740 retval.set(NullValue())
3741 },
3742 }
3743 };
3744
3745 match self.webgl_version() {
3746 WebGLVersion::WebGL1 => {
3747 let current_vao = self.current_vao();
3748 let data = handle_potential_webgl_error!(
3749 self,
3750 current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3751 return retval.set(NullValue())
3752 );
3753 get_attrib(data)
3754 },
3755 WebGLVersion::WebGL2 => {
3756 let current_vao = self.current_vao_webgl2();
3757 let data = handle_potential_webgl_error!(
3758 self,
3759 current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3760 return retval.set(NullValue())
3761 );
3762 get_attrib(data)
3763 },
3764 }
3765 }
3766
3767 fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
3769 if pname != constants::VERTEX_ATTRIB_ARRAY_POINTER {
3770 self.webgl_error(InvalidEnum);
3771 return 0;
3772 }
3773 match self.webgl_version() {
3774 WebGLVersion::WebGL1 => {
3775 let current_vao = self.current_vao();
3776 let data = handle_potential_webgl_error!(
3777 self,
3778 current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3779 return 0
3780 );
3781 data.offset as i64
3782 },
3783 WebGLVersion::WebGL2 => {
3784 let current_vao = self.current_vao_webgl2();
3785 let data = handle_potential_webgl_error!(
3786 self,
3787 current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3788 return 0
3789 );
3790 data.offset as i64
3791 },
3792 }
3793 }
3794
3795 fn Hint(&self, target: u32, mode: u32) {
3797 if target != constants::GENERATE_MIPMAP_HINT &&
3798 !self.extension_manager.is_hint_target_enabled(target)
3799 {
3800 return self.webgl_error(InvalidEnum);
3801 }
3802
3803 match mode {
3804 constants::FASTEST | constants::NICEST | constants::DONT_CARE => (),
3805
3806 _ => return self.webgl_error(InvalidEnum),
3807 }
3808
3809 self.send_command(WebGLCommand::Hint(target, mode));
3810 }
3811
3812 fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
3814 buffer.is_some_and(|buf| {
3815 self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted()
3816 })
3817 }
3818
3819 fn IsEnabled(&self, cap: u32) -> bool {
3821 handle_potential_webgl_error!(self, self.capabilities.is_enabled(cap), false)
3822 }
3823
3824 fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
3826 frame_buffer.is_some_and(|buf| {
3827 self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted()
3828 })
3829 }
3830
3831 fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
3833 program.is_some_and(|p| self.validate_ownership(p).is_ok() && !p.is_deleted())
3834 }
3835
3836 fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
3838 render_buffer.is_some_and(|buf| {
3839 self.validate_ownership(buf).is_ok() && buf.ever_bound() && !buf.is_deleted()
3840 })
3841 }
3842
3843 fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
3845 shader.is_some_and(|s| self.validate_ownership(s).is_ok() && !s.is_deleted())
3846 }
3847
3848 fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
3850 texture.is_some_and(|tex| {
3851 self.validate_ownership(tex).is_ok() && tex.target().is_some() && !tex.is_invalid()
3852 })
3853 }
3854
3855 fn LineWidth(&self, width: f32) {
3857 if width.is_nan() || width <= 0f32 {
3858 return self.webgl_error(InvalidValue);
3859 }
3860
3861 self.send_command(WebGLCommand::LineWidth(width))
3862 }
3863
3864 fn PixelStorei(&self, param_name: u32, param_value: i32) {
3869 let mut texture_settings = self.texture_unpacking_settings.get();
3870 match param_name {
3871 constants::UNPACK_FLIP_Y_WEBGL => {
3872 texture_settings.set(TextureUnpacking::FLIP_Y_AXIS, param_value != 0);
3873 },
3874 constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
3875 texture_settings.set(TextureUnpacking::PREMULTIPLY_ALPHA, param_value != 0);
3876 },
3877 constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
3878 let convert = match param_value as u32 {
3879 constants::BROWSER_DEFAULT_WEBGL => true,
3880 constants::NONE => false,
3881 _ => return self.webgl_error(InvalidEnum),
3882 };
3883 texture_settings.set(TextureUnpacking::CONVERT_COLORSPACE, convert);
3884 },
3885 constants::UNPACK_ALIGNMENT => {
3886 match param_value {
3887 1 | 2 | 4 | 8 => (),
3888 _ => return self.webgl_error(InvalidValue),
3889 }
3890 self.texture_unpacking_alignment.set(param_value as u32);
3891 return;
3892 },
3893 constants::PACK_ALIGNMENT => {
3894 match param_value {
3895 1 | 2 | 4 | 8 => (),
3896 _ => return self.webgl_error(InvalidValue),
3897 }
3898 self.texture_packing_alignment.set(param_value as u8);
3902 return;
3903 },
3904 _ => return self.webgl_error(InvalidEnum),
3905 }
3906 self.texture_unpacking_settings.set(texture_settings);
3907 }
3908
3909 fn PolygonOffset(&self, factor: f32, units: f32) {
3911 self.send_command(WebGLCommand::PolygonOffset(factor, units))
3912 }
3913
3914 #[expect(unsafe_code)]
3916 fn ReadPixels(
3917 &self,
3918 x: i32,
3919 y: i32,
3920 width: i32,
3921 height: i32,
3922 format: u32,
3923 pixel_type: u32,
3924 mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3925 ) {
3926 handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
3927
3928 let pixels =
3929 handle_potential_webgl_error!(self, pixels.as_mut().ok_or(InvalidValue), return);
3930
3931 if width < 0 || height < 0 {
3932 return self.webgl_error(InvalidValue);
3933 }
3934
3935 if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
3936 return self.webgl_error(InvalidOperation);
3937 }
3938
3939 if pixels.get_array_type() != Type::Uint8 {
3940 return self.webgl_error(InvalidOperation);
3941 }
3942
3943 let (fb_width, fb_height) = handle_potential_webgl_error!(
3944 self,
3945 self.get_current_framebuffer_size().ok_or(InvalidOperation),
3946 return
3947 );
3948
3949 if width == 0 || height == 0 {
3950 return;
3951 }
3952
3953 let bytes_per_pixel = 4;
3954
3955 let row_len = handle_potential_webgl_error!(
3956 self,
3957 width.checked_mul(bytes_per_pixel).ok_or(InvalidOperation),
3958 return
3959 );
3960
3961 let pack_alignment = self.texture_packing_alignment.get() as i32;
3962 let dest_padding = match row_len % pack_alignment {
3963 0 => 0,
3964 remainder => pack_alignment - remainder,
3965 };
3966 let dest_stride = row_len + dest_padding;
3967
3968 let full_rows_len = handle_potential_webgl_error!(
3969 self,
3970 dest_stride.checked_mul(height - 1).ok_or(InvalidOperation),
3971 return
3972 );
3973 let required_dest_len = handle_potential_webgl_error!(
3974 self,
3975 full_rows_len.checked_add(row_len).ok_or(InvalidOperation),
3976 return
3977 );
3978
3979 let dest = unsafe { pixels.as_mut_slice() };
3980 if dest.len() < required_dest_len as usize {
3981 return self.webgl_error(InvalidOperation);
3982 }
3983
3984 let src_origin = Point2D::new(x, y);
3985 let src_size = Size2D::new(width as u32, height as u32);
3986 let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
3987 let src_rect = match pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()) {
3988 Some(rect) => rect,
3989 None => return,
3990 };
3991
3992 let src_rect = src_rect.to_u32();
3996
3997 let mut dest_offset = 0;
3998 if x < 0 {
3999 dest_offset += -x * bytes_per_pixel;
4000 }
4001 if y < 0 {
4002 dest_offset += -y * row_len;
4003 }
4004
4005 let (sender, receiver) = generic_channel::channel().unwrap();
4006 self.send_command(WebGLCommand::ReadPixels(
4007 src_rect, format, pixel_type, sender,
4008 ));
4009 let (src, _) = receiver.recv().unwrap();
4010
4011 let src_row_len = src_rect.size.width as usize * bytes_per_pixel as usize;
4012 for i in 0..src_rect.size.height {
4013 let dest_start = dest_offset as usize + i as usize * dest_stride as usize;
4014 let dest_end = dest_start + src_row_len;
4015 let src_start = i as usize * src_row_len;
4016 let src_end = src_start + src_row_len;
4017 dest[dest_start..dest_end].copy_from_slice(&src[src_start..src_end]);
4018 }
4019 }
4020
4021 fn SampleCoverage(&self, value: f32, invert: bool) {
4023 self.send_command(WebGLCommand::SampleCoverage(value, invert));
4024 }
4025
4026 fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
4028 if width < 0 || height < 0 {
4029 return self.webgl_error(InvalidValue);
4030 }
4031
4032 let width = width as u32;
4033 let height = height as u32;
4034
4035 self.current_scissor.set((x, y, width, height));
4036 self.send_command(WebGLCommand::Scissor(x, y, width, height));
4037 }
4038
4039 fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
4041 match func {
4042 constants::NEVER |
4043 constants::LESS |
4044 constants::EQUAL |
4045 constants::LEQUAL |
4046 constants::GREATER |
4047 constants::NOTEQUAL |
4048 constants::GEQUAL |
4049 constants::ALWAYS => self.send_command(WebGLCommand::StencilFunc(func, ref_, mask)),
4050 _ => self.webgl_error(InvalidEnum),
4051 }
4052 }
4053
4054 fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
4056 match face {
4057 constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => (),
4058 _ => return self.webgl_error(InvalidEnum),
4059 }
4060
4061 match func {
4062 constants::NEVER |
4063 constants::LESS |
4064 constants::EQUAL |
4065 constants::LEQUAL |
4066 constants::GREATER |
4067 constants::NOTEQUAL |
4068 constants::GEQUAL |
4069 constants::ALWAYS => {
4070 self.send_command(WebGLCommand::StencilFuncSeparate(face, func, ref_, mask))
4071 },
4072 _ => self.webgl_error(InvalidEnum),
4073 }
4074 }
4075
4076 fn StencilMask(&self, mask: u32) {
4078 self.send_command(WebGLCommand::StencilMask(mask))
4079 }
4080
4081 fn StencilMaskSeparate(&self, face: u32, mask: u32) {
4083 match face {
4084 constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => {
4085 self.send_command(WebGLCommand::StencilMaskSeparate(face, mask))
4086 },
4087 _ => self.webgl_error(InvalidEnum),
4088 };
4089 }
4090
4091 fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
4093 if self.validate_stencil_actions(fail) &&
4094 self.validate_stencil_actions(zfail) &&
4095 self.validate_stencil_actions(zpass)
4096 {
4097 self.send_command(WebGLCommand::StencilOp(fail, zfail, zpass));
4098 } else {
4099 self.webgl_error(InvalidEnum)
4100 }
4101 }
4102
4103 fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
4105 match face {
4106 constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => (),
4107 _ => return self.webgl_error(InvalidEnum),
4108 }
4109
4110 if self.validate_stencil_actions(fail) &&
4111 self.validate_stencil_actions(zfail) &&
4112 self.validate_stencil_actions(zpass)
4113 {
4114 self.send_command(WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass))
4115 } else {
4116 self.webgl_error(InvalidEnum)
4117 }
4118 }
4119
4120 fn LinkProgram(&self, program: &WebGLProgram) {
4122 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
4123 if program.is_deleted() {
4124 return self.webgl_error(InvalidValue);
4125 }
4126 handle_potential_webgl_error!(self, program.link());
4127 }
4128
4129 fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
4131 handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
4132 shader.set_source(source)
4133 }
4134
4135 fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
4137 handle_potential_webgl_error!(self, self.validate_ownership(shader), return None);
4138 Some(shader.source())
4139 }
4140
4141 fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
4143 self.with_location(location, |location| {
4144 match location.type_() {
4145 constants::BOOL | constants::FLOAT => {},
4146 _ => return Err(InvalidOperation),
4147 }
4148 self.send_command(WebGLCommand::Uniform1f(location.id(), val));
4149 Ok(())
4150 });
4151 }
4152
4153 fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
4155 self.with_location(location, |location| {
4156 match location.type_() {
4157 constants::BOOL | constants::INT => {},
4158 constants::SAMPLER_2D |
4159 WebGL2RenderingContextConstants::SAMPLER_3D |
4160 WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
4161 constants::SAMPLER_CUBE => {
4162 if val < 0 || val as u32 >= self.limits.max_combined_texture_image_units {
4163 return Err(InvalidValue);
4164 }
4165 },
4166 _ => return Err(InvalidOperation),
4167 }
4168 self.send_command(WebGLCommand::Uniform1i(location.id(), val));
4169 Ok(())
4170 });
4171 }
4172
4173 fn Uniform1iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4175 self.uniform1iv(location, val, 0, 0)
4176 }
4177
4178 fn Uniform1fv(
4180 &self,
4181 location: Option<&WebGLUniformLocation>,
4182 val: Float32ArrayOrUnrestrictedFloatSequence,
4183 ) {
4184 self.uniform1fv(location, val, 0, 0)
4185 }
4186
4187 fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
4189 self.with_location(location, |location| {
4190 match location.type_() {
4191 constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {},
4192 _ => return Err(InvalidOperation),
4193 }
4194 self.send_command(WebGLCommand::Uniform2f(location.id(), x, y));
4195 Ok(())
4196 });
4197 }
4198
4199 fn Uniform2fv(
4201 &self,
4202 location: Option<&WebGLUniformLocation>,
4203 val: Float32ArrayOrUnrestrictedFloatSequence,
4204 ) {
4205 self.uniform2fv(location, val, 0, 0)
4206 }
4207
4208 fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
4210 self.with_location(location, |location| {
4211 match location.type_() {
4212 constants::BOOL_VEC2 | constants::INT_VEC2 => {},
4213 _ => return Err(InvalidOperation),
4214 }
4215 self.send_command(WebGLCommand::Uniform2i(location.id(), x, y));
4216 Ok(())
4217 });
4218 }
4219
4220 fn Uniform2iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4222 self.uniform2iv(location, val, 0, 0)
4223 }
4224
4225 fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
4227 self.with_location(location, |location| {
4228 match location.type_() {
4229 constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {},
4230 _ => return Err(InvalidOperation),
4231 }
4232 self.send_command(WebGLCommand::Uniform3f(location.id(), x, y, z));
4233 Ok(())
4234 });
4235 }
4236
4237 fn Uniform3fv(
4239 &self,
4240 location: Option<&WebGLUniformLocation>,
4241 val: Float32ArrayOrUnrestrictedFloatSequence,
4242 ) {
4243 self.uniform3fv(location, val, 0, 0)
4244 }
4245
4246 fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
4248 self.with_location(location, |location| {
4249 match location.type_() {
4250 constants::BOOL_VEC3 | constants::INT_VEC3 => {},
4251 _ => return Err(InvalidOperation),
4252 }
4253 self.send_command(WebGLCommand::Uniform3i(location.id(), x, y, z));
4254 Ok(())
4255 });
4256 }
4257
4258 fn Uniform3iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4260 self.uniform3iv(location, val, 0, 0)
4261 }
4262
4263 fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
4265 self.with_location(location, |location| {
4266 match location.type_() {
4267 constants::BOOL_VEC4 | constants::INT_VEC4 => {},
4268 _ => return Err(InvalidOperation),
4269 }
4270 self.send_command(WebGLCommand::Uniform4i(location.id(), x, y, z, w));
4271 Ok(())
4272 });
4273 }
4274
4275 fn Uniform4iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4277 self.uniform4iv(location, val, 0, 0)
4278 }
4279
4280 fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
4282 self.with_location(location, |location| {
4283 match location.type_() {
4284 constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {},
4285 _ => return Err(InvalidOperation),
4286 }
4287 self.send_command(WebGLCommand::Uniform4f(location.id(), x, y, z, w));
4288 Ok(())
4289 });
4290 }
4291
4292 fn Uniform4fv(
4294 &self,
4295 location: Option<&WebGLUniformLocation>,
4296 val: Float32ArrayOrUnrestrictedFloatSequence,
4297 ) {
4298 self.uniform4fv(location, val, 0, 0)
4299 }
4300
4301 fn UniformMatrix2fv(
4303 &self,
4304 location: Option<&WebGLUniformLocation>,
4305 transpose: bool,
4306 val: Float32ArrayOrUnrestrictedFloatSequence,
4307 ) {
4308 self.uniform_matrix_2fv(location, transpose, val, 0, 0)
4309 }
4310
4311 fn UniformMatrix3fv(
4313 &self,
4314 location: Option<&WebGLUniformLocation>,
4315 transpose: bool,
4316 val: Float32ArrayOrUnrestrictedFloatSequence,
4317 ) {
4318 self.uniform_matrix_3fv(location, transpose, val, 0, 0)
4319 }
4320
4321 fn UniformMatrix4fv(
4323 &self,
4324 location: Option<&WebGLUniformLocation>,
4325 transpose: bool,
4326 val: Float32ArrayOrUnrestrictedFloatSequence,
4327 ) {
4328 self.uniform_matrix_4fv(location, transpose, val, 0, 0)
4329 }
4330
4331 #[expect(unsafe_code)]
4333 fn GetUniform(
4334 &self,
4335 cx: SafeJSContext,
4336 program: &WebGLProgram,
4337 location: &WebGLUniformLocation,
4338 mut rval: MutableHandleValue,
4339 ) {
4340 handle_potential_webgl_error!(
4341 self,
4342 self.uniform_check_program(program, location),
4343 return rval.set(NullValue())
4344 );
4345
4346 let triple = (self, program.id(), location.id());
4347
4348 match location.type_() {
4349 constants::BOOL => rval.set(BooleanValue(uniform_get(
4350 triple,
4351 WebGLCommand::GetUniformBool,
4352 ))),
4353 constants::BOOL_VEC2 => uniform_get(triple, WebGLCommand::GetUniformBool2)
4354 .safe_to_jsval(cx, rval, CanGc::deprecated_note()),
4355 constants::BOOL_VEC3 => uniform_get(triple, WebGLCommand::GetUniformBool3)
4356 .safe_to_jsval(cx, rval, CanGc::deprecated_note()),
4357 constants::BOOL_VEC4 => uniform_get(triple, WebGLCommand::GetUniformBool4)
4358 .safe_to_jsval(cx, rval, CanGc::deprecated_note()),
4359 constants::INT |
4360 constants::SAMPLER_2D |
4361 constants::SAMPLER_CUBE |
4362 WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
4363 WebGL2RenderingContextConstants::SAMPLER_3D => {
4364 rval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
4365 },
4366 constants::INT_VEC2 => unsafe {
4367 uniform_typed::<Int32>(
4368 *cx,
4369 &uniform_get(triple, WebGLCommand::GetUniformInt2),
4370 rval,
4371 )
4372 },
4373 constants::INT_VEC3 => unsafe {
4374 uniform_typed::<Int32>(
4375 *cx,
4376 &uniform_get(triple, WebGLCommand::GetUniformInt3),
4377 rval,
4378 )
4379 },
4380 constants::INT_VEC4 => unsafe {
4381 uniform_typed::<Int32>(
4382 *cx,
4383 &uniform_get(triple, WebGLCommand::GetUniformInt4),
4384 rval,
4385 )
4386 },
4387 constants::FLOAT => rval
4388 .set(DoubleValue(
4389 uniform_get(triple, WebGLCommand::GetUniformFloat) as f64,
4390 )),
4391 constants::FLOAT_VEC2 => unsafe {
4392 uniform_typed::<Float32>(
4393 *cx,
4394 &uniform_get(triple, WebGLCommand::GetUniformFloat2),
4395 rval,
4396 )
4397 },
4398 constants::FLOAT_VEC3 => unsafe {
4399 uniform_typed::<Float32>(
4400 *cx,
4401 &uniform_get(triple, WebGLCommand::GetUniformFloat3),
4402 rval,
4403 )
4404 },
4405 constants::FLOAT_VEC4 | constants::FLOAT_MAT2 => unsafe {
4406 uniform_typed::<Float32>(
4407 *cx,
4408 &uniform_get(triple, WebGLCommand::GetUniformFloat4),
4409 rval,
4410 )
4411 },
4412 constants::FLOAT_MAT3 => unsafe {
4413 uniform_typed::<Float32>(
4414 *cx,
4415 &uniform_get(triple, WebGLCommand::GetUniformFloat9),
4416 rval,
4417 )
4418 },
4419 constants::FLOAT_MAT4 => unsafe {
4420 uniform_typed::<Float32>(
4421 *cx,
4422 &uniform_get(triple, WebGLCommand::GetUniformFloat16),
4423 rval,
4424 )
4425 },
4426 _ => panic!("wrong uniform type"),
4427 }
4428 }
4429
4430 fn UseProgram(&self, program: Option<&WebGLProgram>) {
4432 if let Some(program) = program {
4433 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
4434 if program.is_deleted() || !program.is_linked() {
4435 return self.webgl_error(InvalidOperation);
4436 }
4437 if program.is_in_use() {
4438 return;
4439 }
4440 program.in_use(true);
4441 }
4442 match self.current_program.get() {
4443 Some(ref current) if program != Some(&**current) => current.in_use(false),
4444 _ => {},
4445 }
4446 self.send_command(WebGLCommand::UseProgram(program.map(|p| p.id())));
4447 self.current_program.set(program);
4448 }
4449
4450 fn ValidateProgram(&self, program: &WebGLProgram) {
4452 handle_potential_webgl_error!(self, self.validate_ownership(program), return);
4453 if let Err(e) = program.validate() {
4454 self.webgl_error(e);
4455 }
4456 }
4457
4458 fn VertexAttrib1f(&self, indx: u32, x: f32) {
4460 self.vertex_attrib(indx, x, 0f32, 0f32, 1f32)
4461 }
4462
4463 fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4465 let values = match v {
4466 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4467 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4468 };
4469 if values.is_empty() {
4470 return self.webgl_error(InvalidValue);
4472 }
4473 self.vertex_attrib(indx, values[0], 0f32, 0f32, 1f32);
4474 }
4475
4476 fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) {
4478 self.vertex_attrib(indx, x, y, 0f32, 1f32)
4479 }
4480
4481 fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4483 let values = match v {
4484 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4485 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4486 };
4487 if values.len() < 2 {
4488 return self.webgl_error(InvalidValue);
4490 }
4491 self.vertex_attrib(indx, values[0], values[1], 0f32, 1f32);
4492 }
4493
4494 fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) {
4496 self.vertex_attrib(indx, x, y, z, 1f32)
4497 }
4498
4499 fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4501 let values = match v {
4502 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4503 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4504 };
4505 if values.len() < 3 {
4506 return self.webgl_error(InvalidValue);
4508 }
4509 self.vertex_attrib(indx, values[0], values[1], values[2], 1f32);
4510 }
4511
4512 fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
4514 self.vertex_attrib(indx, x, y, z, w)
4515 }
4516
4517 fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4519 let values = match v {
4520 Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4521 Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4522 };
4523 if values.len() < 4 {
4524 return self.webgl_error(InvalidValue);
4526 }
4527 self.vertex_attrib(indx, values[0], values[1], values[2], values[3]);
4528 }
4529
4530 fn VertexAttribPointer(
4532 &self,
4533 index: u32,
4534 size: i32,
4535 type_: u32,
4536 normalized: bool,
4537 stride: i32,
4538 offset: i64,
4539 ) {
4540 let res = match self.webgl_version() {
4541 WebGLVersion::WebGL1 => self
4542 .current_vao()
4543 .vertex_attrib_pointer(index, size, type_, normalized, stride, offset),
4544 WebGLVersion::WebGL2 => self
4545 .current_vao_webgl2()
4546 .vertex_attrib_pointer(index, size, type_, normalized, stride, offset),
4547 };
4548 handle_potential_webgl_error!(self, res);
4549 }
4550
4551 fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
4553 if width < 0 || height < 0 {
4554 return self.webgl_error(InvalidValue);
4555 }
4556
4557 self.send_command(WebGLCommand::SetViewport(x, y, width, height))
4558 }
4559
4560 #[expect(unsafe_code)]
4562 fn TexImage2D(
4563 &self,
4564 target: u32,
4565 level: i32,
4566 internal_format: i32,
4567 width: i32,
4568 height: i32,
4569 border: i32,
4570 format: u32,
4571 data_type: u32,
4572 pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
4573 ) -> ErrorResult {
4574 if !self.extension_manager.is_tex_type_enabled(data_type) {
4575 self.webgl_error(InvalidEnum);
4576 return Ok(());
4577 }
4578
4579 let validator = TexImage2DValidator::new(
4580 self,
4581 target,
4582 level,
4583 internal_format as u32,
4584 width,
4585 height,
4586 border,
4587 format,
4588 data_type,
4589 );
4590
4591 let TexImage2DValidatorResult {
4592 texture,
4593 target,
4594 width,
4595 height,
4596 level,
4597 border,
4598 internal_format,
4599 format,
4600 data_type,
4601 } = match validator.validate() {
4602 Ok(result) => result,
4603 Err(_) => return Ok(()), };
4605
4606 if !internal_format.compatible_data_types().contains(&data_type) {
4607 return {
4608 self.webgl_error(InvalidOperation);
4609 Ok(())
4610 };
4611 }
4612 if texture.is_immutable() {
4613 return {
4614 self.webgl_error(InvalidOperation);
4615 Ok(())
4616 };
4617 }
4618
4619 let unpacking_alignment = self.texture_unpacking_alignment.get();
4620
4621 let expected_byte_length = match self.validate_tex_image_2d_data(
4622 width,
4623 height,
4624 format,
4625 data_type,
4626 unpacking_alignment,
4627 pixels.as_ref(),
4628 ) {
4629 Ok(byte_length) => byte_length,
4630 Err(()) => return Ok(()),
4631 };
4632
4633 let buff = match *pixels {
4636 None => GenericSharedMemory::from_byte(0, expected_byte_length as usize),
4637 Some(ref data) => GenericSharedMemory::from_bytes(unsafe { data.as_slice() }),
4638 };
4639
4640 if buff.len() < expected_byte_length as usize {
4647 return {
4648 self.webgl_error(InvalidOperation);
4649 Ok(())
4650 };
4651 }
4652
4653 let size = Size2D::new(width, height);
4654
4655 if !self.validate_filterable_texture(
4656 &texture,
4657 target,
4658 level,
4659 internal_format,
4660 size,
4661 data_type,
4662 ) {
4663 return Ok(());
4666 }
4667
4668 let size = Size2D::new(width, height);
4669
4670 let (alpha_treatment, y_axis_treatment) =
4671 self.get_current_unpack_state(Alpha::NotPremultiplied);
4672
4673 self.tex_image_2d(
4674 &texture,
4675 target,
4676 data_type,
4677 internal_format,
4678 format,
4679 level,
4680 border,
4681 unpacking_alignment,
4682 size,
4683 TexSource::Pixels(TexPixels::from_array(
4684 buff,
4685 size,
4686 alpha_treatment,
4687 y_axis_treatment,
4688 )),
4689 );
4690
4691 Ok(())
4692 }
4693
4694 fn TexImage2D_(
4696 &self,
4697 target: u32,
4698 level: i32,
4699 internal_format: i32,
4700 format: u32,
4701 data_type: u32,
4702 source: TexImageSource,
4703 ) -> ErrorResult {
4704 if !self.extension_manager.is_tex_type_enabled(data_type) {
4705 self.webgl_error(InvalidEnum);
4706 return Ok(());
4707 }
4708
4709 let pixels = match self.get_image_pixels(source)? {
4710 Some(pixels) => pixels,
4711 None => return Ok(()),
4712 };
4713
4714 let validator = TexImage2DValidator::new(
4715 self,
4716 target,
4717 level,
4718 internal_format as u32,
4719 pixels.size().width as i32,
4720 pixels.size().height as i32,
4721 0,
4722 format,
4723 data_type,
4724 );
4725
4726 let TexImage2DValidatorResult {
4727 texture,
4728 target,
4729 level,
4730 border,
4731 internal_format,
4732 format,
4733 data_type,
4734 ..
4735 } = match validator.validate() {
4736 Ok(result) => result,
4737 Err(_) => return Ok(()), };
4739
4740 if !internal_format.compatible_data_types().contains(&data_type) {
4741 return {
4742 self.webgl_error(InvalidOperation);
4743 Ok(())
4744 };
4745 }
4746 if texture.is_immutable() {
4747 return {
4748 self.webgl_error(InvalidOperation);
4749 Ok(())
4750 };
4751 }
4752
4753 if !self.validate_filterable_texture(
4754 &texture,
4755 target,
4756 level,
4757 internal_format,
4758 pixels.size(),
4759 data_type,
4760 ) {
4761 return Ok(());
4764 }
4765
4766 self.tex_image_2d(
4767 &texture,
4768 target,
4769 data_type,
4770 internal_format,
4771 format,
4772 level,
4773 border,
4774 1,
4775 pixels.size(),
4776 TexSource::Pixels(pixels),
4777 );
4778 Ok(())
4779 }
4780
4781 #[expect(unsafe_code)]
4783 fn TexSubImage2D(
4784 &self,
4785 target: u32,
4786 level: i32,
4787 xoffset: i32,
4788 yoffset: i32,
4789 width: i32,
4790 height: i32,
4791 format: u32,
4792 data_type: u32,
4793 pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
4794 ) -> ErrorResult {
4795 let validator = TexImage2DValidator::new(
4796 self, target, level, format, width, height, 0, format, data_type,
4797 );
4798 let TexImage2DValidatorResult {
4799 texture,
4800 target,
4801 width,
4802 height,
4803 level,
4804 format,
4805 data_type,
4806 ..
4807 } = match validator.validate() {
4808 Ok(result) => result,
4809 Err(_) => return Ok(()), };
4811
4812 let unpacking_alignment = self.texture_unpacking_alignment.get();
4813
4814 let expected_byte_length = match self.validate_tex_image_2d_data(
4815 width,
4816 height,
4817 format,
4818 data_type,
4819 unpacking_alignment,
4820 pixels.as_ref(),
4821 ) {
4822 Ok(byte_length) => byte_length,
4823 Err(()) => return Ok(()),
4824 };
4825
4826 let buff = handle_potential_webgl_error!(
4827 self,
4828 pixels
4829 .as_ref()
4830 .map(|p| GenericSharedMemory::from_bytes(unsafe { p.as_slice() }))
4831 .ok_or(InvalidValue),
4832 return Ok(())
4833 );
4834
4835 if buff.len() < expected_byte_length as usize {
4842 return {
4843 self.webgl_error(InvalidOperation);
4844 Ok(())
4845 };
4846 }
4847
4848 let (alpha_treatment, y_axis_treatment) =
4849 self.get_current_unpack_state(Alpha::NotPremultiplied);
4850
4851 self.tex_sub_image_2d(
4852 texture,
4853 target,
4854 level,
4855 xoffset,
4856 yoffset,
4857 format,
4858 data_type,
4859 unpacking_alignment,
4860 TexPixels::from_array(
4861 buff,
4862 Size2D::new(width, height),
4863 alpha_treatment,
4864 y_axis_treatment,
4865 ),
4866 );
4867 Ok(())
4868 }
4869
4870 fn TexSubImage2D_(
4872 &self,
4873 target: u32,
4874 level: i32,
4875 xoffset: i32,
4876 yoffset: i32,
4877 format: u32,
4878 data_type: u32,
4879 source: TexImageSource,
4880 ) -> ErrorResult {
4881 let pixels = match self.get_image_pixels(source)? {
4882 Some(pixels) => pixels,
4883 None => return Ok(()),
4884 };
4885
4886 let validator = TexImage2DValidator::new(
4887 self,
4888 target,
4889 level,
4890 format,
4891 pixels.size().width as i32,
4892 pixels.size().height as i32,
4893 0,
4894 format,
4895 data_type,
4896 );
4897 let TexImage2DValidatorResult {
4898 texture,
4899 target,
4900 level,
4901 format,
4902 data_type,
4903 ..
4904 } = match validator.validate() {
4905 Ok(result) => result,
4906 Err(_) => return Ok(()), };
4908
4909 self.tex_sub_image_2d(
4910 texture, target, level, xoffset, yoffset, format, data_type, 1, pixels,
4911 );
4912 Ok(())
4913 }
4914
4915 fn TexParameterf(&self, target: u32, name: u32, value: f32) {
4917 self.tex_parameter(target, name, TexParameterValue::Float(value))
4918 }
4919
4920 fn TexParameteri(&self, target: u32, name: u32, value: i32) {
4922 self.tex_parameter(target, name, TexParameterValue::Int(value))
4923 }
4924
4925 fn CheckFramebufferStatus(&self, target: u32) -> u32 {
4927 if target != constants::FRAMEBUFFER {
4933 self.webgl_error(InvalidEnum);
4934 return 0;
4935 }
4936
4937 match self.bound_draw_framebuffer.get() {
4938 Some(fb) => fb.check_status(),
4939 None => constants::FRAMEBUFFER_COMPLETE,
4940 }
4941 }
4942
4943 fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
4945 self.renderbuffer_storage(target, 0, internal_format, width, height)
4946 }
4947
4948 fn FramebufferRenderbuffer(
4950 &self,
4951 target: u32,
4952 attachment: u32,
4953 renderbuffertarget: u32,
4954 rb: Option<&WebGLRenderbuffer>,
4955 ) {
4956 if let Some(rb) = rb {
4957 handle_potential_webgl_error!(self, self.validate_ownership(rb), return);
4958 }
4959
4960 if target != constants::FRAMEBUFFER || renderbuffertarget != constants::RENDERBUFFER {
4961 return self.webgl_error(InvalidEnum);
4962 }
4963
4964 match self.bound_draw_framebuffer.get() {
4965 Some(fb) => handle_potential_webgl_error!(self, fb.renderbuffer(attachment, rb)),
4966 None => self.webgl_error(InvalidOperation),
4967 };
4968 }
4969
4970 fn FramebufferTexture2D(
4972 &self,
4973 target: u32,
4974 attachment: u32,
4975 textarget: u32,
4976 texture: Option<&WebGLTexture>,
4977 level: i32,
4978 ) {
4979 if let Some(texture) = texture {
4980 handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
4981 }
4982
4983 if target != constants::FRAMEBUFFER {
4984 return self.webgl_error(InvalidEnum);
4985 }
4986
4987 if level != 0 {
4993 return self.webgl_error(InvalidValue);
4994 }
4995
4996 match self.bound_draw_framebuffer.get() {
4997 Some(fb) => handle_potential_webgl_error!(
4998 self,
4999 fb.texture2d(attachment, textarget, texture, level)
5000 ),
5001 None => self.webgl_error(InvalidOperation),
5002 };
5003 }
5004
5005 fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
5007 handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
5008 handle_potential_webgl_error!(self, program.attached_shaders().map(Some), None)
5009 }
5010
5011 #[cfg(feature = "webxr")]
5013 fn MakeXRCompatible(&self, can_gc: CanGc) -> Rc<Promise> {
5014 let p = Promise::new(&self.global(), can_gc);
5016 p.resolve_native(&(), can_gc);
5017 p
5018 }
5019}
5020
5021#[derive(Default, JSTraceable, MallocSizeOf)]
5022struct Capabilities {
5023 value: Cell<CapFlags>,
5024}
5025
5026impl Capabilities {
5027 fn set(&self, cap: u32, set: bool) -> WebGLResult<bool> {
5028 let cap = CapFlags::from_enum(cap)?;
5029 let mut value = self.value.get();
5030 if value.contains(cap) == set {
5031 return Ok(false);
5032 }
5033 value.set(cap, set);
5034 self.value.set(value);
5035 Ok(true)
5036 }
5037
5038 fn is_enabled(&self, cap: u32) -> WebGLResult<bool> {
5039 Ok(self.value.get().contains(CapFlags::from_enum(cap)?))
5040 }
5041}
5042
5043impl Default for CapFlags {
5044 fn default() -> Self {
5045 CapFlags::DITHER
5046 }
5047}
5048
5049macro_rules! capabilities {
5050 ($name:ident, $next:ident, $($rest:ident,)*) => {
5051 capabilities!($name, $next, $($rest,)* [$name = 1;]);
5052 };
5053 ($prev:ident, $name:ident, $($rest:ident,)* [$($tt:tt)*]) => {
5054 capabilities!($name, $($rest,)* [$($tt)* $name = Self::$prev.bits() << 1;]);
5055 };
5056 ($prev:ident, [$($name:ident = $value:expr;)*]) => {
5057 #[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
5058 pub(crate) struct CapFlags(u16);
5059
5060 bitflags! {
5061 impl CapFlags: u16 {
5062 $(const $name = $value;)*
5063 }
5064 }
5065
5066 impl CapFlags {
5067 fn from_enum(cap: u32) -> WebGLResult<Self> {
5068 match cap {
5069 $(constants::$name => Ok(Self::$name),)*
5070 _ => Err(InvalidEnum),
5071 }
5072 }
5073 }
5074 };
5075}
5076
5077capabilities! {
5078 BLEND,
5079 CULL_FACE,
5080 DEPTH_TEST,
5081 DITHER,
5082 POLYGON_OFFSET_FILL,
5083 SAMPLE_ALPHA_TO_COVERAGE,
5084 SAMPLE_COVERAGE,
5085 SCISSOR_TEST,
5086 STENCIL_TEST,
5087}
5088
5089#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
5090#[derive(JSTraceable, MallocSizeOf)]
5091pub(crate) struct Textures {
5092 active_unit: Cell<u32>,
5093 units: Box<[TextureUnit]>,
5094}
5095
5096impl Textures {
5097 fn new(max_combined_textures: u32) -> Self {
5098 Self {
5099 active_unit: Default::default(),
5100 units: (0..max_combined_textures)
5101 .map(|_| Default::default())
5102 .collect::<Vec<_>>()
5103 .into(),
5104 }
5105 }
5106
5107 pub(crate) fn active_unit_enum(&self) -> u32 {
5108 self.active_unit.get() + constants::TEXTURE0
5109 }
5110
5111 fn set_active_unit_enum(&self, index: u32) -> WebGLResult<()> {
5112 if (constants::TEXTURE0..constants::TEXTURE0 + self.units.len() as u32).contains(&index) {
5113 self.active_unit.set(index - constants::TEXTURE0);
5114 Ok(())
5115 } else {
5116 Err(InvalidEnum)
5117 }
5118 }
5119
5120 pub(crate) fn active_texture_slot(
5121 &self,
5122 target: u32,
5123 webgl_version: WebGLVersion,
5124 ) -> WebGLResult<&MutNullableDom<WebGLTexture>> {
5125 let active_unit = self.active_unit();
5126 let is_webgl2 = webgl_version == WebGLVersion::WebGL2;
5127 match target {
5128 constants::TEXTURE_2D => Ok(&active_unit.tex_2d),
5129 constants::TEXTURE_CUBE_MAP => Ok(&active_unit.tex_cube_map),
5130 WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY if is_webgl2 => {
5131 Ok(&active_unit.tex_2d_array)
5132 },
5133 WebGL2RenderingContextConstants::TEXTURE_3D if is_webgl2 => Ok(&active_unit.tex_3d),
5134 _ => Err(InvalidEnum),
5135 }
5136 }
5137
5138 pub(crate) fn active_texture_for_image_target(
5139 &self,
5140 target: TexImageTarget,
5141 ) -> Option<DomRoot<WebGLTexture>> {
5142 let active_unit = self.active_unit();
5143 match target {
5144 TexImageTarget::Texture2D => active_unit.tex_2d.get(),
5145 TexImageTarget::Texture2DArray => active_unit.tex_2d_array.get(),
5146 TexImageTarget::Texture3D => active_unit.tex_3d.get(),
5147 TexImageTarget::CubeMap |
5148 TexImageTarget::CubeMapPositiveX |
5149 TexImageTarget::CubeMapNegativeX |
5150 TexImageTarget::CubeMapPositiveY |
5151 TexImageTarget::CubeMapNegativeY |
5152 TexImageTarget::CubeMapPositiveZ |
5153 TexImageTarget::CubeMapNegativeZ => active_unit.tex_cube_map.get(),
5154 }
5155 }
5156
5157 fn active_unit(&self) -> &TextureUnit {
5158 &self.units[self.active_unit.get() as usize]
5159 }
5160
5161 fn iter(&self) -> impl Iterator<Item = (u32, &TextureUnit)> {
5162 self.units
5163 .iter()
5164 .enumerate()
5165 .map(|(index, unit)| (index as u32 + constants::TEXTURE0, unit))
5166 }
5167}
5168
5169#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
5170#[derive(Default, JSTraceable, MallocSizeOf)]
5171struct TextureUnit {
5172 tex_2d: MutNullableDom<WebGLTexture>,
5173 tex_cube_map: MutNullableDom<WebGLTexture>,
5174 tex_2d_array: MutNullableDom<WebGLTexture>,
5175 tex_3d: MutNullableDom<WebGLTexture>,
5176}
5177
5178impl TextureUnit {
5179 fn unbind(&self, texture: &WebGLTexture) -> Option<u32> {
5180 let fields = [
5181 (&self.tex_2d, constants::TEXTURE_2D),
5182 (&self.tex_cube_map, constants::TEXTURE_CUBE_MAP),
5183 (
5184 &self.tex_2d_array,
5185 WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY,
5186 ),
5187 (&self.tex_3d, WebGL2RenderingContextConstants::TEXTURE_3D),
5188 ];
5189 for &(slot, target) in &fields {
5190 if slot.get().is_some_and(|t| texture == &*t) {
5191 slot.set(None);
5192 return Some(target);
5193 }
5194 }
5195 None
5196 }
5197}
5198
5199pub(crate) struct TexPixels {
5200 data: GenericSharedMemory,
5201 size: Size2D<u32>,
5202 pixel_format: Option<PixelFormat>,
5203 alpha_treatment: Option<AlphaTreatment>,
5204 y_axis_treatment: YAxisTreatment,
5205}
5206
5207impl TexPixels {
5208 fn new(
5209 data: GenericSharedMemory,
5210 size: Size2D<u32>,
5211 pixel_format: PixelFormat,
5212 alpha_treatment: Option<AlphaTreatment>,
5213 y_axis_treatment: YAxisTreatment,
5214 ) -> Self {
5215 Self {
5216 data,
5217 size,
5218 pixel_format: Some(pixel_format),
5219 alpha_treatment,
5220 y_axis_treatment,
5221 }
5222 }
5223
5224 pub(crate) fn from_array(
5225 data: GenericSharedMemory,
5226 size: Size2D<u32>,
5227 alpha_treatment: Option<AlphaTreatment>,
5228 y_axis_treatment: YAxisTreatment,
5229 ) -> Self {
5230 Self {
5231 data,
5232 size,
5233 pixel_format: None,
5234 alpha_treatment,
5235 y_axis_treatment,
5236 }
5237 }
5238
5239 pub(crate) fn size(&self) -> Size2D<u32> {
5240 self.size
5241 }
5242
5243 pub(crate) fn pixel_format(&self) -> Option<PixelFormat> {
5244 self.pixel_format
5245 }
5246
5247 pub(crate) fn alpha_treatment(&self) -> Option<AlphaTreatment> {
5248 self.alpha_treatment
5249 }
5250
5251 pub(crate) fn y_axis_treatment(&self) -> YAxisTreatment {
5252 self.y_axis_treatment
5253 }
5254
5255 pub(crate) fn into_shared_memory(self) -> GenericSharedMemory {
5256 self.data
5257 }
5258}
5259
5260pub(crate) enum TexSource {
5261 Pixels(TexPixels),
5262 BufferOffset(i64),
5263}
5264
5265fn array_buffer_type_to_sized_type(type_: Type) -> Option<SizedDataType> {
5266 match type_ {
5267 Type::Uint8 | Type::Uint8Clamped => Some(SizedDataType::Uint8),
5268 Type::Uint16 => Some(SizedDataType::Uint16),
5269 Type::Uint32 => Some(SizedDataType::Uint32),
5270 Type::Int8 => Some(SizedDataType::Int8),
5271 Type::Int16 => Some(SizedDataType::Int16),
5272 Type::Int32 => Some(SizedDataType::Int32),
5273 Type::Float32 => Some(SizedDataType::Float32),
5274 Type::Float16 |
5275 Type::Float64 |
5276 Type::BigInt64 |
5277 Type::BigUint64 |
5278 Type::MaxTypedArrayViewType |
5279 Type::Int64 |
5280 Type::Simd128 => None,
5281 }
5282}