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