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