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