WebGLTypes.h (37261B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef WEBGLTYPES_H_ 7 #define WEBGLTYPES_H_ 8 9 #include <limits> 10 #include <string> 11 #include <string_view> 12 #include <tuple> 13 #include <type_traits> 14 #include <unordered_map> 15 #include <vector> 16 17 #include "GLContextTypes.h" 18 #include "GLDefs.h" 19 #include "ImageContainer.h" 20 #include "gfxTypes.h" 21 #include "mozilla/Casting.h" 22 #include "mozilla/CheckedInt.h" 23 #include "mozilla/EnumTypeTraits.h" 24 #include "mozilla/IsEnumCase.h" 25 #include "mozilla/Range.h" 26 #include "mozilla/RefCounted.h" 27 #include "mozilla/Result.h" 28 #include "mozilla/ResultVariant.h" 29 #include "mozilla/Span.h" 30 #include "mozilla/TiedFields.h" 31 #include "mozilla/TypedEnumBits.h" 32 #include "mozilla/WeakPtr.h" 33 #include "mozilla/dom/WebGLRenderingContextBinding.h" 34 #include "mozilla/gfx/2D.h" 35 #include "mozilla/gfx/BuildConstants.h" 36 #include "mozilla/gfx/Logging.h" 37 #include "mozilla/gfx/Point.h" 38 #include "mozilla/gfx/Rect.h" 39 #include "mozilla/ipc/Shmem.h" 40 #include "mozilla/layers/LayersSurfaces.h" 41 #include "nsString.h" 42 #include "nsTArray.h" 43 44 // Manual reflection of WebIDL typedefs that are different from their 45 // OpenGL counterparts. 46 using WebGLsizeiptr = int64_t; 47 using WebGLintptr = int64_t; 48 using WebGLboolean = bool; 49 50 // - 51 52 namespace mozilla { 53 namespace gl { 54 class GLContext; // This is going to be needed a lot. 55 } // namespace gl 56 57 // - 58 // Prevent implicit conversions into calloc and malloc. (mozilla namespace 59 // only!) 60 61 template <typename DestT> 62 class ForbidNarrowing final { 63 DestT mVal; 64 65 public: 66 template <typename SrcT> 67 MOZ_IMPLICIT ForbidNarrowing(SrcT val) : mVal(val) { 68 static_assert( 69 std::numeric_limits<SrcT>::min() >= std::numeric_limits<DestT>::min(), 70 "SrcT must be narrower than DestT."); 71 static_assert( 72 std::numeric_limits<SrcT>::max() <= std::numeric_limits<DestT>::max(), 73 "SrcT must be narrower than DestT."); 74 } 75 76 explicit operator DestT() const { return mVal; } 77 }; 78 79 inline void* malloc(const ForbidNarrowing<size_t> s) { 80 return ::malloc(size_t(s)); 81 } 82 83 inline void* calloc(const ForbidNarrowing<size_t> n, 84 const ForbidNarrowing<size_t> size) { 85 return ::calloc(size_t(n), size_t(size)); 86 } 87 88 // - 89 90 // TODO: Remove this now-mere-alias. 91 template <typename From> 92 inline auto AutoAssertCast(const From val) { 93 return LazyAssertedCast(val); 94 } 95 96 const char* GetEnumName(GLenum val, const char* defaultRet = "<unknown>"); 97 std::string EnumString(GLenum val); 98 99 namespace webgl { 100 template <typename T> 101 struct QueueParamTraits; 102 class TexUnpackBytes; 103 class TexUnpackSurface; 104 } // namespace webgl 105 106 class ClientWebGLContext; 107 struct WebGLTexPboOffset; 108 class WebGLTexture; 109 class WebGLBuffer; 110 class WebGLFramebuffer; 111 class WebGLProgram; 112 class WebGLQuery; 113 class WebGLRenderbuffer; 114 class WebGLSampler; 115 class WebGLShader; 116 class WebGLSync; 117 class WebGLTexture; 118 class WebGLTransformFeedback; 119 class WebGLVertexArray; 120 121 // - 122 123 class VRefCounted : public RefCounted<VRefCounted> { 124 public: 125 virtual ~VRefCounted() = default; 126 127 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING 128 virtual const char* typeName() const = 0; 129 virtual size_t typeSize() const = 0; 130 #endif 131 }; 132 133 // - 134 135 /* 136 * Implementing WebGL (or OpenGL ES 2.0) on top of desktop OpenGL requires 137 * emulating the vertex attrib 0 array when it's not enabled. Indeed, 138 * OpenGL ES 2.0 allows drawing without vertex attrib 0 array enabled, but 139 * desktop OpenGL does not allow that. 140 */ 141 enum class WebGLVertexAttrib0Status : uint8_t { 142 Default, // default status - no emulation needed 143 EmulatedUninitializedArray, // need an artificial attrib 0 array, but 144 // contents may be left uninitialized 145 EmulatedInitializedArray // need an artificial attrib 0 array, and contents 146 // must be initialized 147 }; 148 149 /* 150 * The formats that may participate, either as source or destination formats, 151 * in WebGL texture conversions. This includes: 152 * - all the formats accepted by WebGL.texImage2D, e.g. RGBA4444 153 * - additional formats provided by extensions, e.g. RGB32F 154 * - additional source formats, depending on browser details, used when 155 * uploading textures from DOM elements. See gfxImageSurface::Format(). 156 */ 157 enum class WebGLTexelFormat : uint8_t { 158 // returned by SurfaceFromElementResultToImageSurface to indicate absence of 159 // image data 160 None, 161 // common value for formats for which format conversions are not supported 162 FormatNotSupportingAnyConversion, 163 // dummy pseudo-format meaning "use the other format". 164 // For example, if SrcFormat=Auto and DstFormat=RGB8, then the source 165 // is implicitly treated as being RGB8 itself. 166 Auto, 167 // 1-channel formats 168 A8, 169 A16F, // OES_texture_half_float 170 A32F, // OES_texture_float 171 R8, 172 R16F, // OES_texture_half_float 173 R32F, // OES_texture_float 174 // 2-channel formats 175 RA8, 176 RA16F, // OES_texture_half_float 177 RA32F, // OES_texture_float 178 RG8, 179 RG16F, 180 RG32F, 181 // 3-channel formats 182 RGB8, 183 RGB565, 184 RGB11F11F10F, 185 RGB16F, // OES_texture_half_float 186 RGB32F, // OES_texture_float 187 // 4-channel formats 188 RGBA8, 189 RGBA5551, 190 RGBA4444, 191 RGBA16F, // OES_texture_half_float 192 RGBA32F, // OES_texture_float 193 // DOM element source only formats. 194 RGBX8, 195 BGRX8, 196 BGRA8 197 }; 198 199 enum class WebGLTexImageFunc : uint8_t { 200 TexImage, 201 TexSubImage, 202 CopyTexImage, 203 CopyTexSubImage, 204 CompTexImage, 205 CompTexSubImage, 206 }; 207 208 enum class WebGLTexDimensions : uint8_t { Tex2D, Tex3D }; 209 210 // Please keep extensions in alphabetic order. 211 enum class WebGLExtensionID : uint8_t { 212 ANGLE_instanced_arrays, 213 EXT_blend_minmax, 214 EXT_color_buffer_float, 215 EXT_color_buffer_half_float, 216 EXT_depth_clamp, 217 EXT_disjoint_timer_query, 218 EXT_float_blend, 219 EXT_frag_depth, 220 EXT_shader_texture_lod, 221 EXT_sRGB, 222 EXT_texture_compression_bptc, 223 EXT_texture_compression_rgtc, 224 EXT_texture_filter_anisotropic, 225 EXT_texture_norm16, 226 MOZ_debug, 227 OES_draw_buffers_indexed, 228 OES_element_index_uint, 229 OES_fbo_render_mipmap, 230 OES_standard_derivatives, 231 OES_texture_float, 232 OES_texture_float_linear, 233 OES_texture_half_float, 234 OES_texture_half_float_linear, 235 OES_vertex_array_object, 236 OVR_multiview2, 237 WEBGL_color_buffer_float, 238 WEBGL_compressed_texture_astc, 239 WEBGL_compressed_texture_etc, 240 WEBGL_compressed_texture_etc1, 241 WEBGL_compressed_texture_pvrtc, 242 WEBGL_compressed_texture_s3tc, 243 WEBGL_compressed_texture_s3tc_srgb, 244 WEBGL_debug_renderer_info, 245 WEBGL_debug_shaders, 246 WEBGL_depth_texture, 247 WEBGL_draw_buffers, 248 WEBGL_explicit_present, 249 WEBGL_lose_context, 250 WEBGL_provoking_vertex, 251 Max 252 }; 253 254 class UniqueBuffer final { 255 // Like unique_ptr<>, but for void* and malloc/calloc/free. 256 void* mBuffer = nullptr; 257 258 public: 259 static inline UniqueBuffer Take(void* buffer) { 260 UniqueBuffer ret; 261 ret.mBuffer = buffer; 262 return ret; 263 } 264 265 UniqueBuffer() = default; 266 267 ~UniqueBuffer() { reset(); } 268 269 UniqueBuffer(UniqueBuffer&& rhs) { *this = std::move(rhs); } 270 271 UniqueBuffer& operator=(UniqueBuffer&& rhs) { 272 reset(); 273 this->mBuffer = rhs.mBuffer; 274 rhs.mBuffer = nullptr; 275 return *this; 276 } 277 278 explicit operator bool() const { return bool(mBuffer); } 279 280 void* get() const { return mBuffer; } 281 282 void reset() { 283 // Believe it or not, when `free` unconditional, it was showing up 284 // in profiles, nearly 20% of time spent in MethodDispatcther<UniformData> 285 // on Aquarium. 286 if (mBuffer) { 287 free(mBuffer); 288 mBuffer = nullptr; 289 } 290 } 291 }; 292 293 namespace webgl { 294 struct FormatUsageInfo; 295 296 static constexpr GLenum kErrorPerfWarning = 0x10001; 297 298 struct SampleableInfo final { 299 const char* incompleteReason = nullptr; 300 uint32_t levels = 0; 301 const webgl::FormatUsageInfo* usage = nullptr; 302 bool isDepthTexCompare = false; 303 304 bool IsComplete() const { return bool(levels); } 305 }; 306 307 enum class AttribBaseType : uint8_t { 308 Boolean, // Can convert from anything. 309 Float, // Also includes NormU?Int 310 Int, 311 Uint, 312 }; 313 } // namespace webgl 314 template <> 315 inline constexpr bool IsEnumCase<webgl::AttribBaseType>( 316 const webgl::AttribBaseType v) { 317 switch (v) { 318 case webgl::AttribBaseType::Boolean: 319 case webgl::AttribBaseType::Float: 320 case webgl::AttribBaseType::Int: 321 case webgl::AttribBaseType::Uint: 322 return true; 323 } 324 return false; 325 } 326 namespace webgl { 327 webgl::AttribBaseType ToAttribBaseType(GLenum); 328 const char* ToString(AttribBaseType); 329 330 enum class UniformBaseType : uint8_t { 331 Float, 332 Int, 333 Uint, 334 }; 335 const char* ToString(UniformBaseType); 336 337 using ObjectId = uint64_t; 338 339 enum class BufferKind : uint8_t { 340 Undefined, 341 Index, 342 NonIndex, 343 }; 344 345 } // namespace webgl 346 347 // - 348 349 struct FloatOrInt final // For TexParameter[fi] and friends. 350 { 351 bool isFloat = false; 352 uint8_t padding[3] = {}; 353 GLfloat f = 0; 354 GLint i = 0; 355 356 explicit FloatOrInt(GLint x = 0) : isFloat(false), f(x), i(x) {} 357 358 explicit FloatOrInt(GLfloat x) : isFloat(true), f(x), i(roundf(x)) {} 359 360 auto MutTiedFields() { return std::tie(isFloat, padding, f, i); } 361 }; 362 363 // - 364 365 struct WebGLContextOptions final { 366 bool alpha = true; 367 bool depth = true; 368 bool stencil = false; 369 bool premultipliedAlpha = true; 370 371 bool antialias = true; 372 bool preserveDrawingBuffer = false; 373 bool failIfMajorPerformanceCaveat = false; 374 bool xrCompatible = false; 375 376 dom::WebGLPowerPreference powerPreference = 377 dom::WebGLPowerPreference::Default; 378 bool forceSoftwareRendering = false; 379 bool shouldResistFingerprinting = true; 380 bool enableDebugRendererInfo = false; 381 382 auto MutTiedFields() { 383 // clang-format off 384 return std::tie( 385 alpha, 386 depth, 387 stencil, 388 premultipliedAlpha, 389 390 antialias, 391 preserveDrawingBuffer, 392 failIfMajorPerformanceCaveat, 393 xrCompatible, 394 395 powerPreference, 396 forceSoftwareRendering, 397 shouldResistFingerprinting, 398 enableDebugRendererInfo); 399 // clang-format on 400 } 401 402 // - 403 404 WebGLContextOptions(); 405 WebGLContextOptions(const WebGLContextOptions&) = default; 406 407 using Self = WebGLContextOptions; 408 friend bool operator==(const Self& a, const Self& b) { 409 return TiedFields(a) == TiedFields(b); 410 } 411 friend bool operator!=(const Self& a, const Self& b) { return !(a == b); } 412 }; 413 414 namespace gfx { 415 416 inline ColorSpace2 ToColorSpace2(const dom::PredefinedColorSpace cs) { 417 switch (cs) { 418 case dom::PredefinedColorSpace::Srgb: 419 return ColorSpace2::SRGB; 420 case dom::PredefinedColorSpace::Display_p3: 421 return ColorSpace2::DISPLAY_P3; 422 } 423 MOZ_CRASH("Exhaustive switch"); 424 } 425 426 } // namespace gfx 427 428 // - 429 430 template <typename _T> 431 struct avec2 { 432 using T = _T; 433 434 T x = T(); 435 T y = T(); 436 437 auto MutTiedFields() { return std::tie(x, y); } 438 439 template <typename U, typename V> 440 static Maybe<avec2> From(const U _x, const V _y) { 441 const auto x = CheckedInt<T>(_x); 442 const auto y = CheckedInt<T>(_y); 443 if (!x.isValid() || !y.isValid()) return {}; 444 return Some(avec2(x.value(), y.value())); 445 } 446 447 template <typename U> 448 static auto From(const U& val) { 449 return From(val.x, val.y); 450 } 451 template <typename U> 452 static auto FromSize(const U& val) { 453 return From(val.width, val.height); 454 } 455 456 avec2() = default; 457 avec2(const T _x, const T _y) : x(_x), y(_y) {} 458 459 bool operator==(const avec2& rhs) const { return x == rhs.x && y == rhs.y; } 460 bool operator!=(const avec2& rhs) const { return !(*this == rhs); } 461 462 #define _(OP) \ 463 avec2 operator OP(const avec2& rhs) const { \ 464 return {x OP rhs.x, y OP rhs.y}; \ 465 } \ 466 avec2 operator OP(const T rhs) const { return {x OP rhs, y OP rhs}; } 467 468 _(+) 469 _(-) 470 _(*) 471 _(/) 472 473 #undef _ 474 475 avec2 Clamp(const avec2& min, const avec2& max) const { 476 return {std::clamp(x, min.x, max.x), std::clamp(y, min.y, max.y)}; 477 } 478 479 template <typename U> 480 U StaticCast() const { 481 return {static_cast<typename U::T>(x), static_cast<typename U::T>(y)}; 482 } 483 }; 484 485 template <typename T> 486 avec2<T> MinExtents(const avec2<T>& a, const avec2<T>& b) { 487 return {std::min(a.x, b.x), std::min(a.y, b.y)}; 488 } 489 490 template <typename T> 491 avec2<T> MaxExtents(const avec2<T>& a, const avec2<T>& b) { 492 return {std::max(a.x, b.x), std::max(a.y, b.y)}; 493 } 494 495 // - 496 497 template <typename _T> 498 struct avec3 { 499 using T = _T; 500 501 T x = T(); 502 T y = T(); 503 T z = T(); 504 505 auto MutTiedFields() { return std::tie(x, y, z); } 506 507 template <typename U, typename V> 508 static Maybe<avec3> From(const U _x, const V _y, const V _z) { 509 const auto x = CheckedInt<T>(_x); 510 const auto y = CheckedInt<T>(_y); 511 const auto z = CheckedInt<T>(_z); 512 if (!x.isValid() || !y.isValid() || !z.isValid()) return {}; 513 return Some(avec3(x.value(), y.value(), z.value())); 514 } 515 516 template <typename U> 517 static auto From(const U& val) { 518 return From(val.x, val.y, val.z); 519 } 520 521 avec3() = default; 522 avec3(const T _x, const T _y, const T _z) : x(_x), y(_y), z(_z) {} 523 524 bool operator==(const avec3& rhs) const { 525 return x == rhs.x && y == rhs.y && z == rhs.z; 526 } 527 bool operator!=(const avec3& rhs) const { return !(*this == rhs); } 528 }; 529 530 using ivec2 = avec2<int32_t>; 531 using ivec3 = avec3<int32_t>; 532 using uvec2 = avec2<uint32_t>; 533 using uvec3 = avec3<uint32_t>; 534 535 inline ivec2 AsVec(const gfx::IntSize& s) { return {s.width, s.height}; } 536 537 // - 538 539 namespace webgl { 540 541 struct PackingInfo final { 542 GLenum format = 0; 543 GLenum type = 0; 544 545 auto MutTiedFields() { return std::tie(format, type); } 546 547 using Self = PackingInfo; 548 friend bool operator<(const Self& a, const Self& b) { 549 return TiedFields(a) < TiedFields(b); 550 } 551 friend bool operator==(const Self& a, const Self& b) { 552 return TiedFields(a) == TiedFields(b); 553 } 554 friend bool operator!=(const Self& a, const Self& b) { 555 return TiedFields(a) != TiedFields(b); 556 } 557 558 template <class T> 559 friend T& operator<<(T& s, const PackingInfo& pi) { 560 s << "PackingInfo{format: " << EnumString(pi.format) 561 << ", type: " << EnumString(pi.type) << "}"; 562 return s; 563 } 564 }; 565 std::string format_as(const PackingInfo& pi); 566 567 struct DriverUnpackInfo final { 568 using Self = DriverUnpackInfo; 569 570 GLenum internalFormat = 0; 571 GLenum unpackFormat = 0; 572 GLenum unpackType = 0; 573 574 PackingInfo ToPacking() const { return {unpackFormat, unpackType}; } 575 576 template <class ConstOrMutSelf> 577 static constexpr auto Fields(ConstOrMutSelf& self) { 578 return std::tie(self.internalFormat, self.unpackFormat, self.unpackType); 579 } 580 581 constexpr bool operator==(const Self& rhs) const { 582 return Fields(*this) == Fields(rhs); 583 } 584 constexpr bool operator!=(const Self& rhs) const { 585 return Fields(*this) != Fields(rhs); 586 } 587 }; 588 589 // - 590 591 template <typename E> 592 class EnumMask { 593 public: 594 uint64_t mBits = 0; 595 596 private: 597 struct BitRef final { 598 EnumMask& bits; 599 const uint64_t mask; 600 601 explicit operator bool() const { return bits.mBits & mask; } 602 603 auto& operator=(const bool val) { 604 if (val) { 605 bits.mBits |= mask; 606 } else { 607 bits.mBits &= ~mask; 608 } 609 return *this; 610 } 611 }; 612 613 uint64_t Mask(const E i) const { 614 return uint64_t{1} << static_cast<uint64_t>(i); 615 } 616 617 public: 618 BitRef operator[](const E i) { return {*this, Mask(i)}; } 619 bool operator[](const E i) const { return mBits & Mask(i); } 620 621 // - 622 623 auto MutTiedFields() { return std::tie(mBits); } 624 }; 625 626 using ExtensionBits = EnumMask<WebGLExtensionID>; 627 628 // - 629 630 enum class ContextLossReason : uint8_t { 631 None, 632 Manual, 633 Guilty, 634 }; 635 636 inline bool ReadContextLossReason(const uint8_t val, 637 ContextLossReason* const out) { 638 if (val > static_cast<uint8_t>(ContextLossReason::Guilty)) { 639 return false; 640 } 641 *out = static_cast<ContextLossReason>(val); 642 return true; 643 } 644 645 // - 646 647 struct InitContextDesc final { 648 bool isWebgl2 = false; 649 bool resistFingerprinting = false; 650 std::array<uint8_t, 2> _padding; 651 uint32_t principalKey = 0; 652 uvec2 size = {}; 653 WebGLContextOptions options; 654 655 auto MutTiedFields() { 656 return std::tie(isWebgl2, resistFingerprinting, _padding, principalKey, 657 size, options); 658 } 659 }; 660 661 constexpr uint32_t kMaxTransformFeedbackSeparateAttribs = 4; 662 663 struct Limits final { 664 ExtensionBits supportedExtensions; 665 666 // WebGL 1 667 uint32_t maxTexUnits = 0; 668 uint32_t maxTex2dSize = 0; 669 uint32_t maxTexCubeSize = 0; 670 uint32_t maxVertexAttribs = 0; 671 uint32_t maxViewportDim = 0; 672 std::array<float, 2> pointSizeRange = {{1, 1}}; 673 std::array<float, 2> lineWidthRange = {{1, 1}}; 674 675 // WebGL 2 676 uint32_t maxTexArrayLayers = 0; 677 uint32_t maxTex3dSize = 0; 678 uint32_t maxUniformBufferBindings = 0; 679 uint32_t uniformBufferOffsetAlignment = 0; 680 681 // Exts 682 bool astcHdr = false; 683 std::array<uint8_t, 3> _padding; 684 uint32_t maxColorDrawBuffers = 1; 685 uint32_t maxMultiviewLayers = 0; 686 uint64_t queryCounterBitsTimeElapsed = 0; 687 uint64_t queryCounterBitsTimestamp = 0; 688 689 auto MutTiedFields() { 690 return std::tie(supportedExtensions, 691 692 maxTexUnits, maxTex2dSize, maxTexCubeSize, maxVertexAttribs, 693 maxViewportDim, pointSizeRange, lineWidthRange, 694 695 maxTexArrayLayers, maxTex3dSize, maxUniformBufferBindings, 696 uniformBufferOffsetAlignment, 697 698 astcHdr, _padding, maxColorDrawBuffers, maxMultiviewLayers, 699 queryCounterBitsTimeElapsed, queryCounterBitsTimestamp); 700 } 701 }; 702 703 // - 704 namespace details { 705 template <class T, size_t Padding> 706 struct PaddedBase { 707 protected: 708 T val = {}; 709 710 private: 711 uint8_t padding[Padding] = {}; 712 }; 713 714 template <class T> 715 struct PaddedBase<T, 0> { 716 protected: 717 T val = {}; 718 }; 719 } // namespace details 720 721 template <class T, size_t PaddedSize> 722 struct Padded : details::PaddedBase<T, PaddedSize - sizeof(T)> { 723 static_assert(PaddedSize >= sizeof(T)); 724 725 // Try to be invisible: 726 operator T&() { return this->val; } 727 operator const T&() const { return this->val; } 728 729 auto& operator=(const T& rhs) { return this->val = rhs; } 730 auto& operator=(T&& rhs) { return this->val = std::move(rhs); } 731 732 auto& operator*() { return this->val; } 733 auto& operator*() const { return this->val; } 734 auto operator->() { return &this->val; } 735 auto operator->() const { return &this->val; } 736 }; 737 738 // - 739 740 enum class OptionalRenderableFormatBits : uint8_t { 741 RGB8 = (1 << 0), 742 SRGB8 = (1 << 1), 743 }; 744 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OptionalRenderableFormatBits) 745 746 } // namespace webgl 747 template <> 748 inline constexpr bool IsEnumCase<webgl::OptionalRenderableFormatBits>( 749 const webgl::OptionalRenderableFormatBits raw) { 750 auto rawWithoutValidBits = UnderlyingValue(raw); 751 auto bit = decltype(rawWithoutValidBits){1}; 752 while (bit) { 753 switch (webgl::OptionalRenderableFormatBits{bit}) { 754 // -Werror=switch ensures exhaustive. 755 case webgl::OptionalRenderableFormatBits::RGB8: 756 case webgl::OptionalRenderableFormatBits::SRGB8: 757 rawWithoutValidBits &= ~bit; 758 break; 759 } 760 bit <<= 1; 761 } 762 return rawWithoutValidBits == 0; 763 } 764 namespace webgl { 765 766 // - 767 768 using GetShaderPrecisionFormatArgs = std::tuple<GLenum, GLenum>; 769 770 template <class Tuple> 771 struct TupleStdHash { 772 size_t operator()(const Tuple& t) const { 773 size_t ret = 0; 774 mozilla::MapTuple(t, [&](const auto& field) { 775 using FieldT = std::remove_cv_t<std::remove_reference_t<decltype(field)>>; 776 ret ^= std::hash<FieldT>{}(field); 777 return true; // ignored 778 }); 779 return ret; 780 } 781 }; 782 783 struct ShaderPrecisionFormat final { 784 // highp float: [127, 127, 23] 785 // highp int: [31, 30, 0] 786 uint8_t rangeMin = 0; // highp float: +127 (meaning 2^-127) 787 uint8_t rangeMax = 0; 788 uint8_t precision = 0; 789 uint8_t _padding = 0; 790 791 auto MutTiedFields() { 792 return std::tie(rangeMin, rangeMax, precision, _padding); 793 } 794 }; 795 796 // - 797 798 struct InitContextResult final { 799 Padded<std::string, 32> error; // MINGW 32-bit needs this padding. 800 WebGLContextOptions options; 801 gl::GLVendor vendor; 802 OptionalRenderableFormatBits optionalRenderableFormatBits; 803 std::array<uint8_t, 2> _padding = {}; 804 Limits limits; 805 EnumMask<layers::SurfaceDescriptor::Type> uploadableSdTypes; 806 // Padded because of "Android 5.0 ARMv7" builds: 807 Padded<std::unordered_map<GetShaderPrecisionFormatArgs, ShaderPrecisionFormat, 808 TupleStdHash<GetShaderPrecisionFormatArgs>>, 809 64> 810 shaderPrecisions; 811 812 auto MutTiedFields() { 813 return std::tie(error, options, vendor, optionalRenderableFormatBits, 814 _padding, limits, uploadableSdTypes, shaderPrecisions); 815 } 816 }; 817 818 // - 819 820 struct ErrorInfo final { 821 GLenum type; 822 std::string info; 823 }; 824 825 // - 826 827 enum class LossStatus { 828 Ready, 829 830 Lost, 831 LostForever, 832 LostManually, 833 }; 834 835 // - 836 837 struct CompileResult final { 838 bool pending = true; 839 nsCString log; 840 nsCString translatedSource; 841 bool success = false; 842 }; 843 844 // - 845 846 struct OpaqueFramebufferOptions final { 847 bool depthStencil = true; 848 bool antialias = true; 849 std::array<uint8_t, 2> _padding; 850 uint32_t width = 0; 851 uint32_t height = 0; 852 853 auto MutTiedFields() { 854 return std::tie(depthStencil, antialias, _padding, width, height); 855 } 856 }; 857 858 // - 859 860 struct SwapChainOptions final { 861 layers::RemoteTextureId remoteTextureId; 862 layers::RemoteTextureOwnerId remoteTextureOwnerId; 863 bool bgra = false; 864 bool forceAsyncPresent = false; 865 // Pad to sizeof(u64): 866 uint16_t padding1 = 0; 867 uint32_t padding2 = 0; 868 869 auto MutTiedFields() { 870 return std::tie(remoteTextureId, remoteTextureOwnerId, bgra, 871 forceAsyncPresent, padding1, padding2); 872 } 873 }; 874 875 // - 876 877 struct ActiveInfo { 878 GLenum elemType = 0; // `type` 879 uint32_t elemCount = 0; // `size` 880 std::string name; 881 }; 882 883 struct ActiveAttribInfo final : public ActiveInfo { 884 int32_t location = -1; 885 AttribBaseType baseType = AttribBaseType::Float; 886 }; 887 888 struct ActiveUniformInfo final : public ActiveInfo { 889 std::unordered_map<uint32_t, uint32_t> 890 locByIndex; // Uniform array locations are sparse. 891 int32_t block_index = -1; 892 int32_t block_offset = -1; // In block, offset. 893 int32_t block_arrayStride = -1; 894 int32_t block_matrixStride = -1; 895 bool block_isRowMajor = false; 896 }; 897 898 struct ActiveUniformBlockInfo final { 899 std::string name; 900 // BLOCK_BINDING is dynamic state 901 uint32_t dataSize = 0; 902 std::vector<uint32_t> activeUniformIndices; 903 bool referencedByVertexShader = false; 904 bool referencedByFragmentShader = false; 905 }; 906 907 struct LinkActiveInfo final { 908 std::vector<ActiveAttribInfo> activeAttribs; 909 std::vector<ActiveUniformInfo> activeUniforms; 910 std::vector<ActiveUniformBlockInfo> activeUniformBlocks; 911 std::vector<ActiveInfo> activeTfVaryings; 912 }; 913 914 struct LinkResult final : public SupportsWeakPtr { 915 LinkResult() {} 916 ~LinkResult() = default; 917 918 bool pending = true; 919 nsCString log; 920 bool success = false; 921 LinkActiveInfo active; 922 GLenum tfBufferMode = 0; 923 }; 924 925 // - 926 927 /// 4x32-bit primitives, with a type tag. 928 struct TypedQuad final { 929 alignas(alignof(float)) std::array<uint8_t, 4 * sizeof(float)> data = {}; 930 webgl::AttribBaseType type = webgl::AttribBaseType::Float; 931 uint8_t padding[3] = {}; 932 933 constexpr auto MutTiedFields() { return std::tie(data, type, padding); } 934 }; 935 936 /// [1-16]x32-bit primitives, with a type tag. 937 struct GetUniformData final { 938 alignas(alignof(float)) uint8_t data[4 * 4 * sizeof(float)] = {}; 939 GLenum type = 0; 940 }; 941 942 struct FrontBufferSnapshotIpc final { 943 uvec2 surfSize = {}; 944 size_t byteStride = 0; 945 Maybe<mozilla::ipc::Shmem> shmem = {}; 946 }; 947 948 struct ReadPixelsResult { 949 gfx::IntRect subrect = {}; 950 size_t byteStride = 0; 951 }; 952 953 struct ReadPixelsResultIpc final : public ReadPixelsResult { 954 Maybe<mozilla::ipc::Shmem> shmem = {}; 955 }; 956 957 struct VertAttribPointerDesc final { 958 bool intFunc = false; 959 uint8_t channels = 4; 960 bool normalized = false; 961 uint8_t byteStrideOrZero = 0; 962 GLenum type = LOCAL_GL_FLOAT; 963 uint64_t byteOffset = 0; 964 965 auto MutTiedFields() { 966 return std::tie(intFunc, channels, normalized, byteStrideOrZero, type, 967 byteOffset); 968 } 969 }; 970 971 struct VertAttribPointerCalculated final { 972 uint8_t byteSize = 4 * 4; 973 uint8_t byteStride = 4 * 4; // at-most 255 974 webgl::AttribBaseType baseType = webgl::AttribBaseType::Float; 975 }; 976 977 } // namespace webgl 978 979 template <class T> 980 inline Range<T> ShmemRange(const mozilla::ipc::Shmem& shmem) { 981 return {shmem.get<T>(), shmem.Size<T>()}; 982 } 983 984 // - 985 986 template <typename C, typename K> 987 inline auto MaybeFind(C& container, const K& key) 988 -> decltype(&(container.find(key)->second)) { 989 const auto itr = container.find(key); 990 if (itr == container.end()) return nullptr; 991 return &(itr->second); 992 } 993 994 template <typename C, typename K> 995 inline typename C::mapped_type Find( 996 const C& container, const K& key, 997 const typename C::mapped_type notFound = {}) { 998 const auto itr = container.find(key); 999 if (itr == container.end()) return notFound; 1000 return itr->second; 1001 } 1002 1003 // - 1004 1005 template <typename T, typename U> 1006 inline Maybe<T> MaybeAs(const U val) { 1007 const auto checked = CheckedInt<T>(val); 1008 if (!checked.isValid()) return {}; 1009 return Some(checked.value()); 1010 } 1011 1012 // - 1013 1014 inline GLenum IsTexMipmapFilter(const GLenum texFilter) { 1015 switch (texFilter) { 1016 case LOCAL_GL_NEAREST_MIPMAP_NEAREST: 1017 case LOCAL_GL_LINEAR_MIPMAP_NEAREST: 1018 case LOCAL_GL_NEAREST_MIPMAP_LINEAR: 1019 case LOCAL_GL_LINEAR_MIPMAP_LINEAR: 1020 return true; 1021 } 1022 return false; 1023 } 1024 1025 inline GLenum IsTexImageTarget(const GLenum imageTarget) { 1026 switch (imageTarget) { 1027 case LOCAL_GL_TEXTURE_2D: 1028 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 1029 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: 1030 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 1031 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 1032 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 1033 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 1034 case LOCAL_GL_TEXTURE_3D: 1035 case LOCAL_GL_TEXTURE_2D_ARRAY: 1036 return true; 1037 } 1038 return false; 1039 } 1040 1041 inline GLenum ImageToTexTarget(const GLenum imageTarget) { 1042 switch (imageTarget) { 1043 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 1044 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: 1045 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 1046 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 1047 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 1048 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 1049 return LOCAL_GL_TEXTURE_CUBE_MAP; 1050 } 1051 if (IsTexImageTarget(imageTarget)) { 1052 return imageTarget; 1053 } 1054 return 0; 1055 } 1056 1057 inline bool IsTexTarget3D(const GLenum texTarget) { 1058 switch (texTarget) { 1059 case LOCAL_GL_TEXTURE_2D_ARRAY: 1060 case LOCAL_GL_TEXTURE_3D: 1061 return true; 1062 1063 default: 1064 return false; 1065 } 1066 } 1067 1068 // - 1069 1070 namespace dom { 1071 class Element; 1072 class ImageBitmap; 1073 class ImageData; 1074 class OffscreenCanvas; 1075 class VideoFrame; 1076 } // namespace dom 1077 1078 struct TexImageSource { 1079 const dom::ArrayBufferView* mView = nullptr; 1080 GLuint mViewElemOffset = 0; 1081 GLuint mViewElemLengthOverride = 0; 1082 1083 const WebGLintptr* mPboOffset = nullptr; 1084 1085 const dom::ImageBitmap* mImageBitmap = nullptr; 1086 const dom::ImageData* mImageData = nullptr; 1087 1088 const dom::OffscreenCanvas* mOffscreenCanvas = nullptr; 1089 1090 const dom::VideoFrame* mVideoFrame = nullptr; 1091 1092 const dom::Element* mDomElem = nullptr; 1093 ErrorResult* mOut_error = nullptr; 1094 }; 1095 1096 namespace webgl { 1097 1098 template <class DerivedT> 1099 struct DeriveNotEq { 1100 bool operator!=(const DerivedT& rhs) const { 1101 const auto self = reinterpret_cast<const DerivedT*>(this); 1102 return !(*self == rhs); 1103 } 1104 }; 1105 1106 struct PixelPackingState : public DeriveNotEq<PixelPackingState> { 1107 uint32_t alignmentInTypeElems = 4; // ALIGNMENT isn't naive byte alignment! 1108 uint32_t rowLength = 0; 1109 uint32_t imageHeight = 0; 1110 uint32_t skipPixels = 0; 1111 uint32_t skipRows = 0; 1112 uint32_t skipImages = 0; 1113 1114 auto MutTiedFields() { 1115 return std::tie(alignmentInTypeElems, rowLength, imageHeight, skipPixels, 1116 skipRows, skipImages); 1117 } 1118 1119 using Self = PixelPackingState; 1120 friend bool operator==(const Self& a, const Self& b) { 1121 return TiedFields(a) == TiedFields(b); 1122 } 1123 1124 static void AssertDefaultUnpack(gl::GLContext& gl, const bool isWebgl2) { 1125 PixelPackingState{}.AssertCurrentUnpack(gl, isWebgl2); 1126 } 1127 1128 void ApplyUnpack(gl::GLContext&, bool isWebgl2, 1129 const uvec3& uploadSize) const; 1130 bool AssertCurrentUnpack(gl::GLContext&, bool isWebgl2) const; 1131 }; 1132 1133 struct PixelUnpackStateWebgl final : public PixelPackingState { 1134 GLenum colorspaceConversion = 1135 dom::WebGLRenderingContext_Binding::BROWSER_DEFAULT_WEBGL; 1136 bool flipY = false; 1137 bool premultiplyAlpha = false; 1138 bool requireFastPath = false; 1139 uint8_t padding = {}; 1140 1141 auto MutTiedFields() { 1142 return std::tuple_cat(PixelPackingState::MutTiedFields(), 1143 std::tie(colorspaceConversion, flipY, 1144 premultiplyAlpha, requireFastPath, padding)); 1145 } 1146 }; 1147 1148 struct ExplicitPixelPackingState final { 1149 struct Metrics final { 1150 uvec3 usedSize = {}; 1151 size_t bytesPerPixel = 0; 1152 1153 // (srcStrideAndRowOverride.x, otherwise ROW_LENGTH != 0, otherwise size.x) 1154 // ...aligned to ALIGNMENT. 1155 size_t bytesPerRowStride = 0; 1156 1157 // structuredSrcSize.y, otherwise IMAGE_HEIGHT*(SKIP_IMAGES+size.z) 1158 size_t totalRows = 0; 1159 1160 // This ensures that no one else needs to do CheckedInt math. 1161 size_t totalBytesUsed = 0; 1162 size_t totalBytesStrided = 0; 1163 }; 1164 1165 // It's so important that these aren't modified once evaluated. 1166 const PixelPackingState state; 1167 const Metrics metrics; 1168 1169 static Result<ExplicitPixelPackingState, std::string> ForUseWith( 1170 const PixelPackingState&, GLenum target, const uvec3& subrectSize, 1171 const webgl::PackingInfo&, const Maybe<size_t> bytesPerRowStrideOverride); 1172 }; 1173 1174 struct ReadPixelsDesc final { 1175 ivec2 srcOffset; 1176 uvec2 size; 1177 PackingInfo pi = {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE}; 1178 PixelPackingState packState; 1179 1180 auto MutTiedFields() { return std::tie(srcOffset, size, pi, packState); } 1181 }; 1182 1183 } // namespace webgl 1184 1185 namespace webgl { 1186 1187 struct TexUnpackBlobDesc final { 1188 GLenum imageTarget = LOCAL_GL_TEXTURE_2D; 1189 uvec3 size; 1190 gfxAlphaType srcAlphaType = gfxAlphaType::NonPremult; 1191 1192 Maybe<Span<const uint8_t>> cpuData; 1193 Maybe<uint64_t> pboOffset; 1194 1195 Maybe<uvec2> structuredSrcSize; 1196 RefPtr<layers::Image> image; 1197 Maybe<layers::SurfaceDescriptor> sd; 1198 RefPtr<gfx::SourceSurface> sourceSurf; 1199 1200 webgl::PixelUnpackStateWebgl unpacking; 1201 bool applyUnpackTransforms = true; 1202 1203 // - 1204 1205 auto ExplicitUnpacking(const webgl::PackingInfo& pi, 1206 const Maybe<size_t> bytesPerRowStrideOverride) const { 1207 return ExplicitPixelPackingState::ForUseWith(this->unpacking, 1208 this->imageTarget, this->size, 1209 pi, bytesPerRowStrideOverride); 1210 } 1211 1212 void Shrink(const webgl::PackingInfo&); 1213 }; 1214 1215 } // namespace webgl 1216 1217 // --------------------------------------- 1218 // MakeRange 1219 1220 template <typename T, size_t N> 1221 inline Range<const T> MakeRange(T (&arr)[N]) { 1222 return {arr, N}; 1223 } 1224 1225 template <typename T> 1226 inline Range<const T> MakeRange(const dom::Sequence<T>& seq) { 1227 return {seq.Elements(), seq.Length()}; 1228 } 1229 1230 // - 1231 1232 constexpr auto kUniversalAlignment = alignof(std::max_align_t); 1233 1234 template <typename T> 1235 inline size_t AlignmentOffset(const size_t alignment, const T posOrPtr) { 1236 MOZ_ASSERT(alignment); 1237 const auto begin = reinterpret_cast<uintptr_t>(posOrPtr); 1238 const auto wholeMultiples = (begin + (alignment - 1)) / alignment; 1239 const auto aligned = wholeMultiples * alignment; 1240 return aligned - begin; 1241 } 1242 1243 template <typename T> 1244 inline size_t ByteSize(const Range<T>& range) { 1245 return range.length() * sizeof(T); 1246 } 1247 1248 // - 1249 1250 Maybe<webgl::ErrorInfo> CheckBindBufferRange( 1251 const GLenum target, const GLuint index, const bool isBuffer, 1252 const uint64_t offset, const uint64_t size, const webgl::Limits& limits); 1253 1254 Maybe<webgl::ErrorInfo> CheckFramebufferAttach(const GLenum bindImageTarget, 1255 const GLenum curTexTarget, 1256 const uint32_t mipLevel, 1257 const uint32_t zLayerBase, 1258 const uint32_t zLayerCount, 1259 const webgl::Limits& limits); 1260 1261 Result<webgl::VertAttribPointerCalculated, webgl::ErrorInfo> 1262 CheckVertexAttribPointer(bool isWebgl2, const webgl::VertAttribPointerDesc&); 1263 1264 uint8_t ElemTypeComponents(GLenum elemType); 1265 1266 inline std::string ToString(const nsACString& text) { 1267 return {text.BeginReading(), text.Length()}; 1268 } 1269 1270 inline void Memcpy(const RangedPtr<uint8_t>& destBytes, 1271 const RangedPtr<const uint8_t>& srcBytes, 1272 const size_t byteSize) { 1273 // Trigger range asserts 1274 (void)(srcBytes + byteSize); 1275 (void)(destBytes + byteSize); 1276 1277 memcpy(destBytes.get(), srcBytes.get(), byteSize); 1278 } 1279 1280 template <class T, class U> 1281 inline void Memcpy(const Range<T>* const destRange, 1282 const RangedPtr<U>& srcBegin) { 1283 Memcpy(destRange->begin(), srcBegin, destRange->length()); 1284 } 1285 template <class T, class U> 1286 inline void Memcpy(const RangedPtr<T>* const destBegin, 1287 const Range<U>& srcRange) { 1288 Memcpy(destBegin, srcRange->begin(), srcRange->length()); 1289 } 1290 1291 template <typename Dst, typename Src> 1292 inline void Memcpy(const Span<Dst>* const dest, const Span<Src>& src) { 1293 MOZ_RELEASE_ASSERT(src.size_bytes() >= dest->size_bytes()); 1294 MOZ_ASSERT(src.size_bytes() == dest->size_bytes()); 1295 memcpy(dest->data(), src.data(), dest->size_bytes()); 1296 } 1297 1298 // - 1299 1300 inline bool StartsWith(const std::string_view str, 1301 const std::string_view part) { 1302 return str.find(part) == 0; 1303 } 1304 1305 // - 1306 1307 namespace webgl { 1308 1309 // In theory, this number can be unbounded based on the driver. However, no 1310 // driver appears to expose more than 8. We might as well stop there too, for 1311 // now. 1312 // (http://opengl.gpuinfo.org/gl_stats_caps_single.php?listreportsbycap=GL_MAX_COLOR_ATTACHMENTS) 1313 inline constexpr size_t kMaxDrawBuffers = 8; 1314 1315 union UniformDataVal { 1316 float f32; 1317 int32_t i32; 1318 uint32_t u32; 1319 }; 1320 1321 enum class ProvokingVertex : GLenum { 1322 FirstVertex = LOCAL_GL_FIRST_VERTEX_CONVENTION, 1323 LastVertex = LOCAL_GL_LAST_VERTEX_CONVENTION, 1324 }; 1325 1326 } // namespace webgl 1327 1328 template <> 1329 inline constexpr bool IsEnumCase<webgl::ProvokingVertex>( 1330 const webgl::ProvokingVertex raw) { 1331 switch (raw) { 1332 case webgl::ProvokingVertex::FirstVertex: 1333 case webgl::ProvokingVertex::LastVertex: 1334 return true; 1335 } 1336 return false; 1337 } 1338 1339 namespace webgl { 1340 1341 // - 1342 1343 struct BufferAndIndex final { 1344 const WebGLBuffer* buffer = nullptr; 1345 uint32_t id = -1; 1346 }; 1347 1348 } // namespace webgl 1349 1350 struct IndexedBufferBinding final { 1351 RefPtr<WebGLBuffer> mBufferBinding; 1352 uint64_t mRangeStart = 0; 1353 uint64_t mRangeSize = 0; 1354 1355 IndexedBufferBinding(); 1356 ~IndexedBufferBinding(); 1357 1358 uint64_t ByteCount() const; 1359 }; 1360 1361 // - 1362 1363 template <class... Args> 1364 inline std::string PrintfStdString(const char* const format, 1365 const Args&... args) { 1366 const auto nsStr = nsPrintfCString(format, args...); 1367 return ToString(nsStr); 1368 } 1369 1370 inline const char* ToChars(const bool val) { 1371 if (val) return "true"; 1372 return "false"; 1373 } 1374 1375 template <class To> 1376 struct ReinterpretToSpan { 1377 template <class FromT> 1378 static inline constexpr Span<To> From(const Span<FromT>& from) { 1379 static_assert(sizeof(FromT) == sizeof(To)); 1380 return {reinterpret_cast<To*>(from.data()), from.size()}; 1381 } 1382 }; 1383 1384 // - 1385 1386 inline std::string Join(Span<const std::string> ss, 1387 const std::string_view& delim) { 1388 if (!ss.size()) return ""; 1389 auto ret = std::string(); 1390 { 1391 auto chars = delim.size() * (ss.size() - 1); 1392 for (const auto& s : ss) { 1393 chars += s.size(); 1394 } 1395 ret.reserve(chars); 1396 } 1397 1398 ret = ss[0]; 1399 ss = ss.subspan(1); 1400 for (const auto& s : ss) { 1401 ret += delim; 1402 ret += s; 1403 } 1404 return ret; 1405 } 1406 1407 inline std::string ToStringWithCommas(uint64_t v) { 1408 if (!v) return "0"; 1409 std::vector<std::string> chunks; 1410 while (v) { 1411 const auto chunk = v % 1000; 1412 v /= 1000; 1413 chunks.insert(chunks.begin(), std::to_string(chunk)); 1414 } 1415 return Join(chunks, ","); 1416 } 1417 1418 // - 1419 // C++17 polyfill implementation from: 1420 // https://en.cppreference.com/w/cpp/container/array/to_array 1421 1422 namespace detail { 1423 template <class T, size_t N, size_t... I> 1424 constexpr std::array<std::remove_cv_t<T>, N> to_array_impl( 1425 T (&a)[N], std::index_sequence<I...>) { 1426 return {{a[I]...}}; 1427 } 1428 1429 template <class T, size_t N, size_t... I> 1430 constexpr std::array<std::remove_cv_t<T>, N> to_array_impl( 1431 T (&&a)[N], std::index_sequence<I...>) { 1432 return {{std::move(a[I])...}}; 1433 } 1434 } // namespace detail 1435 1436 template <class T, size_t N> 1437 constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]) { 1438 return detail::to_array_impl(a, std::make_index_sequence<N>{}); 1439 } 1440 1441 template <class T, size_t N> 1442 constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&&a)[N]) { 1443 return detail::to_array_impl(std::move(a), std::make_index_sequence<N>{}); 1444 } 1445 1446 // - 1447 1448 namespace webgl { 1449 1450 std::unordered_map<GLenum, bool> MakeIsEnabledMap(bool webgl2); 1451 1452 static constexpr uint32_t kMaxClientWaitSyncTimeoutNS = 1453 1000 * 1000 * 1000; // 1000ms in ns. 1454 1455 } // namespace webgl 1456 } // namespace mozilla 1457 1458 #endif