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