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