WebGLContextGL.cpp (46668B)
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 #include <fmt/format.h> 7 8 #include <algorithm> 9 10 #include "CanvasUtils.h" 11 #include "GLContext.h" 12 #include "MozFramebuffer.h" 13 #include "WebGL2Context.h" 14 #include "WebGLBuffer.h" 15 #include "WebGLContext.h" 16 #include "WebGLContextUtils.h" 17 #include "WebGLFormats.h" 18 #include "WebGLFramebuffer.h" 19 #include "WebGLProgram.h" 20 #include "WebGLQuery.h" 21 #include "WebGLRenderbuffer.h" 22 #include "WebGLShader.h" 23 #include "WebGLTexelConversions.h" 24 #include "WebGLTexture.h" 25 #include "WebGLValidateStrings.h" 26 #include "WebGLVertexArray.h" 27 #include "gfxContext.h" 28 #include "gfxPlatform.h" 29 #include "gfxUtils.h" 30 #include "jsfriendapi.h" 31 #include "mozilla/RefPtr.h" 32 #include "mozilla/StaticPrefs_webgl.h" 33 #include "mozilla/dom/BindingUtils.h" 34 #include "mozilla/dom/ImageData.h" 35 #include "mozilla/dom/WebGLRenderingContextBinding.h" 36 #include "nsContentUtils.h" 37 #include "nsDebug.h" 38 #include "nsError.h" 39 #include "nsLayoutUtils.h" 40 #include "nsReadableUtils.h" 41 #include "nsString.h" 42 43 namespace mozilla { 44 45 using namespace mozilla::dom; 46 using namespace mozilla::gfx; 47 using namespace mozilla::gl; 48 49 // 50 // WebGL API 51 // 52 53 void WebGLContext::ActiveTexture(uint32_t texUnit) { 54 FuncScope funcScope(*this, "activeTexture"); 55 if (IsContextLost()) return; 56 funcScope.mBindFailureGuard = true; 57 58 if (texUnit >= Limits().maxTexUnits) { 59 return ErrorInvalidEnum("Texture unit %u out of range (%u).", texUnit, 60 Limits().maxTexUnits); 61 } 62 63 mActiveTexture = texUnit; 64 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit); 65 66 funcScope.mBindFailureGuard = false; 67 } 68 69 void WebGLContext::AttachShader(WebGLProgram& prog, WebGLShader& shader) { 70 FuncScope funcScope(*this, "attachShader"); 71 if (IsContextLost()) return; 72 funcScope.mBindFailureGuard = true; 73 74 prog.AttachShader(shader); 75 76 funcScope.mBindFailureGuard = false; 77 } 78 79 void WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location, 80 const std::string& name) const { 81 const FuncScope funcScope(*this, "bindAttribLocation"); 82 if (IsContextLost()) return; 83 84 prog.BindAttribLocation(location, name); 85 } 86 87 void WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb) { 88 FuncScope funcScope(*this, "bindFramebuffer"); 89 if (IsContextLost()) return; 90 funcScope.mBindFailureGuard = true; 91 92 if (!ValidateFramebufferTarget(target)) return; 93 94 if (!wfb) { 95 gl->fBindFramebuffer(target, 0); 96 } else { 97 GLuint framebuffername = wfb->mGLName; 98 gl->fBindFramebuffer(target, framebuffername); 99 wfb->mHasBeenBound = true; 100 } 101 102 switch (target) { 103 case LOCAL_GL_FRAMEBUFFER: 104 mBoundDrawFramebuffer = wfb; 105 mBoundReadFramebuffer = wfb; 106 break; 107 case LOCAL_GL_DRAW_FRAMEBUFFER: 108 mBoundDrawFramebuffer = wfb; 109 break; 110 case LOCAL_GL_READ_FRAMEBUFFER: 111 mBoundReadFramebuffer = wfb; 112 break; 113 default: 114 return; 115 } 116 funcScope.mBindFailureGuard = false; 117 } 118 119 void WebGLContext::BlendEquationSeparate(Maybe<GLuint> i, GLenum modeRGB, 120 GLenum modeAlpha) { 121 const FuncScope funcScope(*this, "blendEquationSeparate"); 122 if (IsContextLost()) return; 123 124 if (!ValidateBlendEquationEnum(modeRGB, "modeRGB") || 125 !ValidateBlendEquationEnum(modeAlpha, "modeAlpha")) { 126 return; 127 } 128 129 if (i) { 130 MOZ_RELEASE_ASSERT( 131 IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed)); 132 const auto limit = MaxValidDrawBuffers(); 133 if (*i >= limit) { 134 ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i, 135 "MAX_DRAW_BUFFERS", limit); 136 return; 137 } 138 139 gl->fBlendEquationSeparatei(*i, modeRGB, modeAlpha); 140 } else { 141 gl->fBlendEquationSeparate(modeRGB, modeAlpha); 142 } 143 } 144 145 static bool ValidateBlendFuncEnum(WebGLContext* webgl, GLenum factor, 146 const char* varName) { 147 switch (factor) { 148 case LOCAL_GL_ZERO: 149 case LOCAL_GL_ONE: 150 case LOCAL_GL_SRC_COLOR: 151 case LOCAL_GL_ONE_MINUS_SRC_COLOR: 152 case LOCAL_GL_DST_COLOR: 153 case LOCAL_GL_ONE_MINUS_DST_COLOR: 154 case LOCAL_GL_SRC_ALPHA: 155 case LOCAL_GL_ONE_MINUS_SRC_ALPHA: 156 case LOCAL_GL_DST_ALPHA: 157 case LOCAL_GL_ONE_MINUS_DST_ALPHA: 158 case LOCAL_GL_CONSTANT_COLOR: 159 case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR: 160 case LOCAL_GL_CONSTANT_ALPHA: 161 case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA: 162 case LOCAL_GL_SRC_ALPHA_SATURATE: 163 return true; 164 165 default: 166 webgl->ErrorInvalidEnumInfo(varName, factor); 167 return false; 168 } 169 } 170 171 static bool ValidateBlendFuncEnums(WebGLContext* webgl, GLenum srcRGB, 172 GLenum srcAlpha, GLenum dstRGB, 173 GLenum dstAlpha) { 174 if (!webgl->IsWebGL2()) { 175 if (dstRGB == LOCAL_GL_SRC_ALPHA_SATURATE || 176 dstAlpha == LOCAL_GL_SRC_ALPHA_SATURATE) { 177 webgl->ErrorInvalidEnum( 178 "LOCAL_GL_SRC_ALPHA_SATURATE as a destination" 179 " blend function is disallowed in WebGL 1 (dstRGB =" 180 " 0x%04x, dstAlpha = 0x%04x).", 181 dstRGB, dstAlpha); 182 return false; 183 } 184 } 185 186 if (!ValidateBlendFuncEnum(webgl, srcRGB, "srcRGB") || 187 !ValidateBlendFuncEnum(webgl, srcAlpha, "srcAlpha") || 188 !ValidateBlendFuncEnum(webgl, dstRGB, "dstRGB") || 189 !ValidateBlendFuncEnum(webgl, dstAlpha, "dstAlpha")) { 190 return false; 191 } 192 193 return true; 194 } 195 196 void WebGLContext::BlendFuncSeparate(Maybe<GLuint> i, GLenum srcRGB, 197 GLenum dstRGB, GLenum srcAlpha, 198 GLenum dstAlpha) { 199 const FuncScope funcScope(*this, "blendFuncSeparate"); 200 if (IsContextLost()) return; 201 202 if (!ValidateBlendFuncEnums(this, srcRGB, srcAlpha, dstRGB, dstAlpha)) return; 203 204 // note that we only check compatibity for the RGB enums, no need to for the 205 // Alpha enums, see "Section 6.8 forgetting to mention alpha factors?" thread 206 // on the public_webgl mailing list 207 if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "srcRGB and dstRGB")) 208 return; 209 210 if (i) { 211 MOZ_RELEASE_ASSERT( 212 IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed)); 213 const auto limit = MaxValidDrawBuffers(); 214 if (*i >= limit) { 215 ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i, 216 "MAX_DRAW_BUFFERS", limit); 217 return; 218 } 219 220 gl->fBlendFuncSeparatei(*i, srcRGB, dstRGB, srcAlpha, dstAlpha); 221 } else { 222 gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); 223 } 224 } 225 226 GLenum WebGLContext::CheckFramebufferStatus(GLenum target) { 227 const FuncScope funcScope(*this, "checkFramebufferStatus"); 228 if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; 229 230 if (!ValidateFramebufferTarget(target)) return 0; 231 232 WebGLFramebuffer* fb; 233 switch (target) { 234 case LOCAL_GL_FRAMEBUFFER: 235 case LOCAL_GL_DRAW_FRAMEBUFFER: 236 fb = mBoundDrawFramebuffer; 237 break; 238 239 case LOCAL_GL_READ_FRAMEBUFFER: 240 fb = mBoundReadFramebuffer; 241 break; 242 243 default: 244 MOZ_CRASH("GFX: Bad target."); 245 } 246 247 if (!fb) return LOCAL_GL_FRAMEBUFFER_COMPLETE; 248 249 return fb->CheckFramebufferStatus().get(); 250 } 251 252 RefPtr<WebGLProgram> WebGLContext::CreateProgram() { 253 const FuncScope funcScope(*this, "createProgram"); 254 if (IsContextLost()) return nullptr; 255 256 return new WebGLProgram(this); 257 } 258 259 RefPtr<WebGLShader> WebGLContext::CreateShader(GLenum type) { 260 const FuncScope funcScope(*this, "createShader"); 261 if (IsContextLost()) return nullptr; 262 263 if (type != LOCAL_GL_VERTEX_SHADER && type != LOCAL_GL_FRAGMENT_SHADER) { 264 ErrorInvalidEnumInfo("type", type); 265 return nullptr; 266 } 267 268 return new WebGLShader(this, type); 269 } 270 271 void WebGLContext::CullFace(GLenum face) { 272 const FuncScope funcScope(*this, "cullFace"); 273 if (IsContextLost()) return; 274 275 if (!ValidateFaceEnum(face)) return; 276 277 gl->fCullFace(face); 278 } 279 280 void WebGLContext::DetachShader(WebGLProgram& prog, const WebGLShader& shader) { 281 FuncScope funcScope(*this, "detachShader"); 282 if (IsContextLost()) return; 283 funcScope.mBindFailureGuard = true; 284 285 prog.DetachShader(shader); 286 287 funcScope.mBindFailureGuard = false; 288 } 289 290 static bool ValidateComparisonEnum(WebGLContext& webgl, const GLenum func) { 291 switch (func) { 292 case LOCAL_GL_NEVER: 293 case LOCAL_GL_LESS: 294 case LOCAL_GL_LEQUAL: 295 case LOCAL_GL_GREATER: 296 case LOCAL_GL_GEQUAL: 297 case LOCAL_GL_EQUAL: 298 case LOCAL_GL_NOTEQUAL: 299 case LOCAL_GL_ALWAYS: 300 return true; 301 302 default: 303 webgl.ErrorInvalidEnumInfo("func", func); 304 return false; 305 } 306 } 307 308 void WebGLContext::DepthFunc(GLenum func) { 309 const FuncScope funcScope(*this, "depthFunc"); 310 if (IsContextLost()) return; 311 312 if (!ValidateComparisonEnum(*this, func)) return; 313 314 gl->fDepthFunc(func); 315 } 316 317 void WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar) { 318 const FuncScope funcScope(*this, "depthRange"); 319 if (IsContextLost()) return; 320 321 if (zNear > zFar) 322 return ErrorInvalidOperation( 323 "the near value is greater than the far value!"); 324 325 gl->fDepthRange(zNear, zFar); 326 } 327 328 // - 329 330 void WebGLContext::FramebufferAttach(const GLenum target, 331 const GLenum attachSlot, 332 const GLenum bindImageTarget, 333 const webgl::FbAttachInfo& toAttach) { 334 FuncScope funcScope(*this, "framebufferAttach"); 335 funcScope.mBindFailureGuard = true; 336 const auto& limits = *mLimits; 337 338 if (!ValidateFramebufferTarget(target)) return; 339 340 auto fb = mBoundDrawFramebuffer; 341 if (target == LOCAL_GL_READ_FRAMEBUFFER) { 342 fb = mBoundReadFramebuffer; 343 } 344 if (!fb) return; 345 346 // `rb` needs no validation. 347 348 // `tex` 349 const auto& tex = toAttach.tex; 350 if (tex) { 351 const auto err = CheckFramebufferAttach(bindImageTarget, tex->mTarget.get(), 352 toAttach.mipLevel, toAttach.zLayer, 353 toAttach.zLayerCount, limits); 354 if (err) return; 355 } 356 357 auto safeToAttach = toAttach; 358 if (!toAttach.rb && !toAttach.tex) { 359 safeToAttach = {}; 360 } 361 if (!IsWebGL2() && 362 !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap)) { 363 safeToAttach.mipLevel = 0; 364 } 365 if (!IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) { 366 safeToAttach.isMultiview = false; 367 } 368 369 if (!fb->FramebufferAttach(attachSlot, safeToAttach)) return; 370 371 funcScope.mBindFailureGuard = false; 372 } 373 374 // - 375 376 void WebGLContext::FrontFace(GLenum mode) { 377 const FuncScope funcScope(*this, "frontFace"); 378 if (IsContextLost()) return; 379 380 switch (mode) { 381 case LOCAL_GL_CW: 382 case LOCAL_GL_CCW: 383 break; 384 default: 385 return ErrorInvalidEnumInfo("mode", mode); 386 } 387 388 gl->fFrontFace(mode); 389 } 390 391 Maybe<double> WebGLContext::GetBufferParameter(GLenum target, GLenum pname) { 392 const FuncScope funcScope(*this, "getBufferParameter"); 393 if (IsContextLost()) return Nothing(); 394 395 const auto& slot = ValidateBufferSlot(target); 396 if (!slot) return Nothing(); 397 const auto& buffer = *slot; 398 399 if (!buffer) { 400 ErrorInvalidOperation("Buffer for `target` is null."); 401 return Nothing(); 402 } 403 404 switch (pname) { 405 case LOCAL_GL_BUFFER_SIZE: 406 return Some(buffer->ByteLength()); 407 408 case LOCAL_GL_BUFFER_USAGE: 409 return Some(buffer->Usage()); 410 411 default: 412 ErrorInvalidEnumInfo("pname", pname); 413 return Nothing(); 414 } 415 } 416 417 Maybe<double> WebGLContext::GetFramebufferAttachmentParameter( 418 WebGLFramebuffer* const fb, GLenum attachment, GLenum pname) const { 419 const FuncScope funcScope(*this, "getFramebufferAttachmentParameter"); 420 if (IsContextLost()) return Nothing(); 421 422 if (fb) return fb->GetAttachmentParameter(attachment, pname); 423 424 //////////////////////////////////// 425 426 if (!IsWebGL2()) { 427 ErrorInvalidOperation( 428 "Querying against the default framebuffer is not" 429 " allowed in WebGL 1."); 430 return Nothing(); 431 } 432 433 switch (attachment) { 434 case LOCAL_GL_BACK: 435 case LOCAL_GL_DEPTH: 436 case LOCAL_GL_STENCIL: 437 break; 438 439 default: 440 ErrorInvalidEnum( 441 "For the default framebuffer, can only query COLOR, DEPTH," 442 " or STENCIL."); 443 return Nothing(); 444 } 445 446 switch (pname) { 447 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 448 switch (attachment) { 449 case LOCAL_GL_BACK: 450 break; 451 case LOCAL_GL_DEPTH: 452 if (!mOptions.depth) { 453 return Some(LOCAL_GL_NONE); 454 } 455 break; 456 case LOCAL_GL_STENCIL: 457 if (!mOptions.stencil) { 458 return Some(LOCAL_GL_NONE); 459 } 460 break; 461 default: 462 ErrorInvalidEnum( 463 "With the default framebuffer, can only query COLOR, DEPTH," 464 " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE"); 465 return Nothing(); 466 } 467 return Some(LOCAL_GL_FRAMEBUFFER_DEFAULT); 468 469 //////////////// 470 471 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: 472 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: 473 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: 474 if (attachment == LOCAL_GL_BACK) return Some(8); 475 return Some(0); 476 477 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: 478 if (attachment == LOCAL_GL_BACK) { 479 if (mOptions.alpha) { 480 return Some(8); 481 } 482 ErrorInvalidOperation( 483 "The default framebuffer doesn't contain an alpha buffer"); 484 return Nothing(); 485 } 486 return Some(0); 487 488 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: 489 if (attachment == LOCAL_GL_DEPTH) { 490 if (mOptions.depth) { 491 return Some(24); 492 } 493 ErrorInvalidOperation( 494 "The default framebuffer doesn't contain an depth buffer"); 495 return Nothing(); 496 } 497 return Some(0); 498 499 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: 500 if (attachment == LOCAL_GL_STENCIL) { 501 if (mOptions.stencil) { 502 return Some(8); 503 } 504 ErrorInvalidOperation( 505 "The default framebuffer doesn't contain an stencil buffer"); 506 return Nothing(); 507 } 508 return Some(0); 509 510 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: 511 if (attachment == LOCAL_GL_STENCIL) { 512 if (mOptions.stencil) { 513 return Some(LOCAL_GL_UNSIGNED_INT); 514 } 515 ErrorInvalidOperation( 516 "The default framebuffer doesn't contain an stencil buffer"); 517 } else if (attachment == LOCAL_GL_DEPTH) { 518 if (mOptions.depth) { 519 return Some(LOCAL_GL_UNSIGNED_NORMALIZED); 520 } 521 ErrorInvalidOperation( 522 "The default framebuffer doesn't contain an depth buffer"); 523 } else { // LOCAL_GL_BACK 524 return Some(LOCAL_GL_UNSIGNED_NORMALIZED); 525 } 526 return Nothing(); 527 528 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: 529 if (attachment == LOCAL_GL_STENCIL) { 530 if (!mOptions.stencil) { 531 ErrorInvalidOperation( 532 "The default framebuffer doesn't contain an stencil buffer"); 533 return Nothing(); 534 } 535 } else if (attachment == LOCAL_GL_DEPTH) { 536 if (!mOptions.depth) { 537 ErrorInvalidOperation( 538 "The default framebuffer doesn't contain an depth buffer"); 539 return Nothing(); 540 } 541 } 542 return Some(LOCAL_GL_LINEAR); 543 } 544 545 ErrorInvalidEnumInfo("pname", pname); 546 return Nothing(); 547 } 548 549 Maybe<double> WebGLContext::GetRenderbufferParameter( 550 const WebGLRenderbuffer& rb, GLenum pname) const { 551 const FuncScope funcScope(*this, "getRenderbufferParameter"); 552 if (IsContextLost()) return Nothing(); 553 554 switch (pname) { 555 case LOCAL_GL_RENDERBUFFER_SAMPLES: 556 if (!IsWebGL2()) break; 557 [[fallthrough]]; 558 559 case LOCAL_GL_RENDERBUFFER_WIDTH: 560 case LOCAL_GL_RENDERBUFFER_HEIGHT: 561 case LOCAL_GL_RENDERBUFFER_RED_SIZE: 562 case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: 563 case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: 564 case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: 565 case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: 566 case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: 567 case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: { 568 // RB emulation means we have to ask the RB itself. 569 GLint i = rb.GetRenderbufferParameter(pname); 570 return Some(i); 571 } 572 573 default: 574 break; 575 } 576 577 ErrorInvalidEnumInfo("pname", pname); 578 return Nothing(); 579 } 580 581 RefPtr<WebGLTexture> WebGLContext::CreateTexture() { 582 const FuncScope funcScope(*this, "createTexture"); 583 if (IsContextLost()) return nullptr; 584 585 GLuint tex = 0; 586 gl->fGenTextures(1, &tex); 587 588 return new WebGLTexture(this, tex); 589 } 590 591 GLenum WebGLContext::GetError() { 592 const FuncScope funcScope(*this, "getError"); 593 594 /* WebGL 1.0: Section 5.14.3: Setting and getting state: 595 * If the context's webgl context lost flag is set, returns 596 * CONTEXT_LOST_WEBGL the first time this method is called. 597 * Afterward, returns NO_ERROR until the context has been 598 * restored. 599 * 600 * WEBGL_lose_context: 601 * [When this extension is enabled: ] loseContext and 602 * restoreContext are allowed to generate INVALID_OPERATION errors 603 * even when the context is lost. 604 */ 605 606 auto err = mWebGLError; 607 mWebGLError = 0; 608 if (IsContextLost() || err) // Must check IsContextLost in all flow paths. 609 return err; 610 611 // Either no WebGL-side error, or it's already been cleared. 612 // UnderlyingGL-side errors, now. 613 err = gl->fGetError(); 614 if (gl->IsContextLost()) { 615 CheckForContextLoss(); 616 return GetError(); 617 } 618 MOZ_ASSERT(err != LOCAL_GL_CONTEXT_LOST); 619 620 if (err) { 621 GenerateWarning("Driver error unexpected by WebGL: 0x%04x", err); 622 // This might be: 623 // - INVALID_OPERATION from ANGLE due to incomplete RBAB implementation for 624 // DrawElements 625 // with DYNAMIC_DRAW index buffer. 626 } 627 return err; 628 } 629 630 webgl::GetUniformData WebGLContext::GetUniform(const WebGLProgram& prog, 631 const uint32_t loc) const { 632 const FuncScope funcScope(*this, "getUniform"); 633 webgl::GetUniformData ret; 634 [&]() { 635 if (IsContextLost()) return; 636 637 const auto& info = prog.LinkInfo(); 638 if (!info) return; 639 640 const auto locInfo = MaybeFind(info->locationMap, loc); 641 if (!locInfo) return; 642 643 ret.type = locInfo->info.info.elemType; 644 switch (ret.type) { 645 case LOCAL_GL_FLOAT: 646 case LOCAL_GL_FLOAT_VEC2: 647 case LOCAL_GL_FLOAT_VEC3: 648 case LOCAL_GL_FLOAT_VEC4: 649 case LOCAL_GL_FLOAT_MAT2: 650 case LOCAL_GL_FLOAT_MAT3: 651 case LOCAL_GL_FLOAT_MAT4: 652 case LOCAL_GL_FLOAT_MAT2x3: 653 case LOCAL_GL_FLOAT_MAT2x4: 654 case LOCAL_GL_FLOAT_MAT3x2: 655 case LOCAL_GL_FLOAT_MAT3x4: 656 case LOCAL_GL_FLOAT_MAT4x2: 657 case LOCAL_GL_FLOAT_MAT4x3: 658 gl->fGetUniformfv(prog.mGLName, loc, 659 reinterpret_cast<float*>(ret.data)); 660 break; 661 662 case LOCAL_GL_INT: 663 case LOCAL_GL_INT_VEC2: 664 case LOCAL_GL_INT_VEC3: 665 case LOCAL_GL_INT_VEC4: 666 case LOCAL_GL_SAMPLER_2D: 667 case LOCAL_GL_SAMPLER_3D: 668 case LOCAL_GL_SAMPLER_CUBE: 669 case LOCAL_GL_SAMPLER_2D_SHADOW: 670 case LOCAL_GL_SAMPLER_2D_ARRAY: 671 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 672 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 673 case LOCAL_GL_INT_SAMPLER_2D: 674 case LOCAL_GL_INT_SAMPLER_3D: 675 case LOCAL_GL_INT_SAMPLER_CUBE: 676 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 677 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 678 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 679 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 680 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 681 case LOCAL_GL_BOOL: 682 case LOCAL_GL_BOOL_VEC2: 683 case LOCAL_GL_BOOL_VEC3: 684 case LOCAL_GL_BOOL_VEC4: 685 gl->fGetUniformiv(prog.mGLName, loc, 686 reinterpret_cast<int32_t*>(ret.data)); 687 break; 688 689 case LOCAL_GL_UNSIGNED_INT: 690 case LOCAL_GL_UNSIGNED_INT_VEC2: 691 case LOCAL_GL_UNSIGNED_INT_VEC3: 692 case LOCAL_GL_UNSIGNED_INT_VEC4: 693 gl->fGetUniformuiv(prog.mGLName, loc, 694 reinterpret_cast<uint32_t*>(ret.data)); 695 break; 696 697 default: 698 MOZ_CRASH("GFX: Invalid elemType."); 699 } 700 }(); 701 return ret; 702 } 703 704 void WebGLContext::Hint(GLenum target, GLenum mode) { 705 const FuncScope funcScope(*this, "hint"); 706 if (IsContextLost()) return; 707 708 switch (mode) { 709 case LOCAL_GL_FASTEST: 710 case LOCAL_GL_NICEST: 711 case LOCAL_GL_DONT_CARE: 712 break; 713 default: 714 return ErrorInvalidEnumArg("mode", mode); 715 } 716 717 // - 718 719 bool isValid = false; 720 721 switch (target) { 722 case LOCAL_GL_GENERATE_MIPMAP_HINT: 723 mGenerateMipmapHint = mode; 724 isValid = true; 725 726 // Deprecated and removed in desktop GL Core profiles. 727 if (gl->IsCoreProfile()) return; 728 729 break; 730 731 case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: 732 if (IsWebGL2() || 733 IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) { 734 isValid = true; 735 } 736 break; 737 } 738 if (!isValid) return ErrorInvalidEnumInfo("target", target); 739 740 // - 741 742 gl->fHint(target, mode); 743 } 744 745 // - 746 747 void WebGLContext::LinkProgram(WebGLProgram& prog) { 748 const FuncScope funcScope(*this, "linkProgram"); 749 if (IsContextLost()) return; 750 751 prog.LinkProgram(); 752 753 if (&prog == mCurrentProgram) { 754 if (!prog.IsLinked()) { 755 // We use to simply early-out here, and preserve the GL behavior that 756 // failed relink doesn't invalidate the current active program link info. 757 // The new behavior was changed for WebGL here: 758 // https://github.com/KhronosGroup/WebGL/pull/3371 759 mActiveProgramLinkInfo = nullptr; 760 gl->fUseProgram(0); // Shouldn't be needed, but let's be safe. 761 return; 762 } 763 mActiveProgramLinkInfo = prog.LinkInfo(); 764 gl->fUseProgram(prog.mGLName); // Uncontionally re-use. 765 // Previously, we needed this re-use on nvidia as a driver workaround, 766 // but we might as well do it unconditionally. 767 } 768 } 769 770 Maybe<webgl::ErrorInfo> SetPixelUnpack( 771 const bool isWebgl2, webgl::PixelUnpackStateWebgl* const unpacking, 772 const GLenum pname, const GLint param) { 773 if (isWebgl2) { 774 uint32_t* pValueSlot = nullptr; 775 switch (pname) { 776 case LOCAL_GL_UNPACK_IMAGE_HEIGHT: 777 pValueSlot = &unpacking->imageHeight; 778 break; 779 780 case LOCAL_GL_UNPACK_SKIP_IMAGES: 781 pValueSlot = &unpacking->skipImages; 782 break; 783 784 case LOCAL_GL_UNPACK_ROW_LENGTH: 785 pValueSlot = &unpacking->rowLength; 786 break; 787 788 case LOCAL_GL_UNPACK_SKIP_ROWS: 789 pValueSlot = &unpacking->skipRows; 790 break; 791 792 case LOCAL_GL_UNPACK_SKIP_PIXELS: 793 pValueSlot = &unpacking->skipPixels; 794 break; 795 } 796 797 if (pValueSlot) { 798 *pValueSlot = static_cast<uint32_t>(param); 799 return {}; 800 } 801 } 802 803 switch (pname) { 804 case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL: 805 unpacking->flipY = bool(param); 806 return {}; 807 808 case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL: 809 unpacking->premultiplyAlpha = bool(param); 810 return {}; 811 812 case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL: 813 switch (param) { 814 case LOCAL_GL_NONE: 815 case dom::WebGLRenderingContext_Binding::BROWSER_DEFAULT_WEBGL: 816 break; 817 818 default: { 819 const nsPrintfCString text("Bad UNPACK_COLORSPACE_CONVERSION: %s", 820 EnumString(param).c_str()); 821 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, ToString(text)}); 822 } 823 } 824 unpacking->colorspaceConversion = param; 825 return {}; 826 827 case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH: 828 unpacking->requireFastPath = bool(param); 829 return {}; 830 831 case LOCAL_GL_UNPACK_ALIGNMENT: 832 switch (param) { 833 case 1: 834 case 2: 835 case 4: 836 case 8: 837 break; 838 839 default: { 840 const nsPrintfCString text( 841 "UNPACK_ALIGNMENT must be [1,2,4,8], was %i", param); 842 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, ToString(text)}); 843 } 844 } 845 unpacking->alignmentInTypeElems = param; 846 return {}; 847 848 default: 849 break; 850 } 851 const nsPrintfCString text("Bad `pname`: %s", EnumString(pname).c_str()); 852 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_ENUM, ToString(text)}); 853 } 854 855 bool WebGLContext::DoReadPixelsAndConvert( 856 const webgl::FormatInfo* const srcFormat, const webgl::ReadPixelsDesc& desc, 857 const uintptr_t dest, const uint64_t destSize, const uint32_t rowStride) { 858 const auto& x = desc.srcOffset.x; 859 const auto& y = desc.srcOffset.y; 860 const auto size = *ivec2::From(desc.size); 861 const auto& pi = desc.pi; 862 863 // On at least Win+NV, we'll get PBO errors if we don't have at least 864 // `rowStride * height` bytes available to read into. 865 const auto naiveBytesNeeded = CheckedInt<uint64_t>(rowStride) * size.y; 866 const bool isDangerCloseToEdge = 867 (!naiveBytesNeeded.isValid() || naiveBytesNeeded.value() > destSize); 868 const bool useParanoidHandling = 869 (gl->WorkAroundDriverBugs() && isDangerCloseToEdge && 870 mBoundPixelPackBuffer); 871 if (!useParanoidHandling) { 872 gl->fReadPixels(x, y, size.x, size.y, pi.format, pi.type, 873 reinterpret_cast<void*>(dest)); 874 return true; 875 } 876 877 // Read everything but the last row. 878 const auto bodyHeight = size.y - 1; 879 if (bodyHeight) { 880 gl->fReadPixels(x, y, size.x, bodyHeight, pi.format, pi.type, 881 reinterpret_cast<void*>(dest)); 882 } 883 884 // Now read the last row. 885 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1); 886 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0); 887 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0); 888 889 const auto tailRowOffset = 890 reinterpret_cast<uint8_t*>(dest) + rowStride * bodyHeight; 891 gl->fReadPixels(x, y + bodyHeight, size.x, 1, pi.format, pi.type, 892 tailRowOffset); 893 894 return true; 895 } 896 897 webgl::ReadPixelsResult WebGLContext::ReadPixelsInto( 898 const webgl::ReadPixelsDesc& desc, const Range<uint8_t>& dest) { 899 const FuncScope funcScope(*this, "readPixels"); 900 if (IsContextLost()) return {}; 901 902 if (mBoundPixelPackBuffer) { 903 ErrorInvalidOperation("PIXEL_PACK_BUFFER must be null."); 904 return {}; 905 } 906 907 return ReadPixelsImpl(desc, reinterpret_cast<uintptr_t>(dest.begin().get()), 908 dest.length()); 909 } 910 911 void WebGLContext::ReadPixelsPbo(const webgl::ReadPixelsDesc& desc, 912 const uint64_t offset) { 913 const FuncScope funcScope(*this, "readPixels"); 914 if (IsContextLost()) return; 915 916 const auto& buffer = ValidateBufferSelection(LOCAL_GL_PIXEL_PACK_BUFFER); 917 if (!buffer) return; 918 919 ////// 920 921 { 922 const auto pii = webgl::PackingInfoInfo::For(desc.pi); 923 if (!pii) { 924 GLenum err = LOCAL_GL_INVALID_OPERATION; 925 if (!desc.pi.format || !desc.pi.type) { 926 err = LOCAL_GL_INVALID_ENUM; 927 } 928 GenerateError(err, "`format` (%s) and/or `type` (%s) not acceptable.", 929 EnumString(desc.pi.format).c_str(), 930 EnumString(desc.pi.type).c_str()); 931 return; 932 } 933 934 if (offset % pii->bytesPerElement != 0) { 935 ErrorInvalidOperation( 936 "`offset` must be divisible by the size of `type`" 937 " in bytes."); 938 return; 939 } 940 } 941 942 ////// 943 944 auto bytesAvailable = buffer->ByteLength(); 945 if (offset > bytesAvailable) { 946 ErrorInvalidOperation("`offset` too large for bound PIXEL_PACK_BUFFER."); 947 return; 948 } 949 bytesAvailable -= offset; 950 951 // - 952 953 const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer); 954 955 ReadPixelsImpl(desc, offset, bytesAvailable); 956 957 buffer->ResetLastUpdateFenceId(); 958 } 959 960 static webgl::PackingInfo DefaultReadPixelPI( 961 const webgl::FormatUsageInfo* usage) { 962 MOZ_ASSERT(usage->IsRenderable()); 963 const auto& format = *usage->format; 964 switch (format.componentType) { 965 case webgl::ComponentType::NormUInt: 966 if (format.r == 16) { 967 return {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT}; 968 } 969 return {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE}; 970 971 case webgl::ComponentType::Int: 972 return {LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT}; 973 974 case webgl::ComponentType::UInt: 975 return {LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT}; 976 977 case webgl::ComponentType::Float: 978 return {LOCAL_GL_RGBA, LOCAL_GL_FLOAT}; 979 980 case webgl::ComponentType::NormInt: 981 MOZ_RELEASE_ASSERT(false, "SNORM formats are never color-renderable!"); 982 break; 983 } 984 MOZ_CRASH("bad webgl::ComponentType"); 985 } 986 987 static bool ArePossiblePackEnums(const webgl::PackingInfo& pi) { 988 // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid 989 // combination for glReadPixels()... 990 991 // Only valid when pulled from: 992 // * GLES 2.0.25 p105: 993 // "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA." 994 // * GLES 3.0.4 p193: 995 // "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL." 996 switch (pi.format) { 997 case LOCAL_GL_LUMINANCE: 998 case LOCAL_GL_LUMINANCE_ALPHA: 999 case LOCAL_GL_DEPTH_COMPONENT: 1000 case LOCAL_GL_DEPTH_STENCIL: 1001 return false; 1002 } 1003 1004 if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) return false; 1005 1006 const auto pii = webgl::PackingInfoInfo::For(pi); 1007 if (!pii) return false; 1008 1009 return true; 1010 } 1011 1012 webgl::PackingInfo WebGLContext::ValidImplementationColorReadPI( 1013 const webgl::FormatUsageInfo* usage) const { 1014 if (const auto implPI = usage->implReadPiCache) return *implPI; 1015 1016 const auto defaultPI = DefaultReadPixelPI(usage); 1017 usage->implReadPiCache = [&]() { 1018 if (StaticPrefs::webgl_porting_strict_readpixels_formats()) 1019 return defaultPI; 1020 auto implPI = defaultPI; 1021 // ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual 1022 // IsGLES(). Also OSX+NV generates an error here. 1023 if (gl->IsGLES()) { 1024 gl->GetInt(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &implPI.format); 1025 gl->GetInt(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, &implPI.type); 1026 } else { 1027 if (StaticPrefs::webgl_porting_strict_readpixels_formats_non_es()) 1028 return defaultPI; 1029 // Non-ES GL is way more open, and basically supports reading anything. 1030 // (Sucks to be their driver team!) 1031 if (usage->idealUnpack) { 1032 for (const auto& [validPi, validDui] : usage->validUnpacks) { 1033 if (validDui != *usage->idealUnpack) continue; 1034 implPI = validPi; 1035 break; 1036 } 1037 } 1038 } 1039 // Normalize HALF_FLOAT_OES to HALF_FLOAT internally. 1040 if (implPI.type == LOCAL_GL_HALF_FLOAT_OES) { 1041 implPI.type = LOCAL_GL_HALF_FLOAT; 1042 } 1043 if (!ArePossiblePackEnums(implPI)) return defaultPI; 1044 return implPI; 1045 }(); 1046 return *usage->implReadPiCache; 1047 } 1048 1049 std::string webgl::format_as(const PackingInfo& pi) { 1050 return fmt::format(FMT_STRING("{}/{}"), pi.format, pi.type); 1051 } 1052 1053 static bool ValidateReadPixelsFormatAndType( 1054 const webgl::FormatUsageInfo* srcUsage, const webgl::PackingInfo& pi, 1055 WebGLContext* webgl) { 1056 if (!ArePossiblePackEnums(pi)) { 1057 webgl->ErrorInvalidEnum("Unexpected format or type."); 1058 return false; 1059 } 1060 1061 const auto defaultPI = DefaultReadPixelPI(srcUsage); 1062 if (pi == defaultPI) return true; 1063 1064 //// 1065 1066 // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is 1067 // RGB10_A2, a third combination of format RGBA and type 1068 // UNSIGNED_INT_2_10_10_10_REV is accepted. 1069 std::optional<webgl::PackingInfo> bonusValidPi; 1070 if (srcUsage->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2) { 1071 bonusValidPi = 1072 webgl::PackingInfo{LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV}; 1073 } 1074 if (bonusValidPi && pi == *bonusValidPi) return true; 1075 1076 //// 1077 1078 const auto implPI = webgl->ValidImplementationColorReadPI(srcUsage); 1079 MOZ_ASSERT(pi.type != LOCAL_GL_HALF_FLOAT_OES); // HALF_FLOAT-only 1080 MOZ_ASSERT(implPI.type != LOCAL_GL_HALF_FLOAT_OES); // HALF_FLOAT-only 1081 if (pi == implPI) return true; 1082 1083 //// 1084 1085 // Map HALF_FLOAT to HALF_FLOAT_OES for error messages in webgl1. 1086 webgl::PackingInfo clientImplPI = implPI; 1087 if (clientImplPI.type == LOCAL_GL_HALF_FLOAT && !webgl->IsWebGL2()) { 1088 clientImplPI.type = LOCAL_GL_HALF_FLOAT_OES; 1089 } 1090 1091 auto validPiStr = 1092 fmt::format(FMT_STRING("{} (spec-required baseline for format {})"), 1093 defaultPI, srcUsage->format->name); 1094 if (implPI != defaultPI) { 1095 validPiStr += fmt::format( 1096 FMT_STRING( 1097 ", or {} (spec-optional implementation-chosen format-dependant" 1098 " IMPLEMENTATION_COLOR_READ_FORMAT/_TYPE)"), 1099 clientImplPI); 1100 } 1101 if (bonusValidPi) { 1102 validPiStr += 1103 fmt::format(FMT_STRING(", or {} (spec-required bonus for format {})"), 1104 *bonusValidPi, srcUsage->format->name); 1105 } 1106 1107 webgl->ErrorInvalidOperation( 1108 "Format/type %s/%s incompatible with this %s framebuffer. Must use: %s.", 1109 EnumString(pi.format).c_str(), EnumString(pi.type).c_str(), 1110 srcUsage->format->name, validPiStr.c_str()); 1111 return false; 1112 } 1113 1114 webgl::ReadPixelsResult WebGLContext::ReadPixelsImpl( 1115 const webgl::ReadPixelsDesc& desc, const uintptr_t dest, 1116 const uint64_t availBytes) { 1117 const webgl::FormatUsageInfo* srcFormat; 1118 uint32_t srcWidth; 1119 uint32_t srcHeight; 1120 if (!BindCurFBForColorRead(&srcFormat, &srcWidth, &srcHeight)) return {}; 1121 1122 ////// 1123 1124 if (!ValidateReadPixelsFormatAndType(srcFormat, desc.pi, this)) return {}; 1125 1126 ////// 1127 1128 const auto& srcOffset = desc.srcOffset; 1129 const auto& size = desc.size; 1130 1131 if (!ivec2::From(size)) { 1132 ErrorInvalidValue("width and height must be non-negative."); 1133 return {}; 1134 } 1135 1136 const auto& packing = desc.packState; 1137 const auto explicitPackingRes = webgl::ExplicitPixelPackingState::ForUseWith( 1138 packing, LOCAL_GL_TEXTURE_2D, {size.x, size.y, 1}, desc.pi, {}); 1139 if (!explicitPackingRes.isOk()) { 1140 ErrorInvalidOperation("%s", explicitPackingRes.inspectErr().c_str()); 1141 return {}; 1142 } 1143 const auto& explicitPacking = explicitPackingRes.inspect(); 1144 const auto& rowStride = explicitPacking.metrics.bytesPerRowStride; 1145 const auto& bytesNeeded = explicitPacking.metrics.totalBytesUsed; 1146 if (bytesNeeded > availBytes) { 1147 ErrorInvalidOperation("buffer too small"); 1148 return {}; 1149 } 1150 1151 //// 1152 1153 int32_t readX, readY; 1154 int32_t writeX, writeY; 1155 int32_t rwWidth, rwHeight; 1156 if (!Intersect(srcWidth, srcOffset.x, size.x, &readX, &writeX, &rwWidth) || 1157 !Intersect(srcHeight, srcOffset.y, size.y, &readY, &writeY, &rwHeight)) { 1158 ErrorOutOfMemory("Bad subrect selection."); 1159 return {}; 1160 } 1161 1162 //////////////// 1163 // Now that the errors are out of the way, on to actually reading! 1164 1165 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, packing.alignmentInTypeElems); 1166 if (IsWebGL2()) { 1167 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, packing.rowLength); 1168 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, packing.skipPixels); 1169 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, packing.skipRows); 1170 } 1171 1172 if (!rwWidth || !rwHeight) { 1173 // Disjoint rects, so we're done already. 1174 DummyReadFramebufferOperation(); 1175 return {}; 1176 } 1177 const auto rwSize = *uvec2::From(rwWidth, rwHeight); 1178 1179 const auto res = webgl::ReadPixelsResult{ 1180 {{writeX, writeY}, {rwSize.x, rwSize.y}}, rowStride}; 1181 1182 if (rwSize == size) { 1183 DoReadPixelsAndConvert(srcFormat->format, desc, dest, bytesNeeded, 1184 rowStride); 1185 return res; 1186 } 1187 1188 // Read request contains out-of-bounds pixels. Unfortunately: 1189 // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer": 1190 // "If any of these pixels lies outside of the window allocated to the current 1191 // GL context, or outside of the image attached to the currently bound 1192 // framebuffer object, then the values obtained for those pixels are 1193 // undefined." 1194 1195 // This is a slow-path, so warn people away! 1196 GenerateWarning( 1197 "Out-of-bounds reads with readPixels are deprecated, and" 1198 " may be slow."); 1199 1200 //////////////////////////////////// 1201 // Read only the in-bounds pixels. 1202 1203 if (IsWebGL2()) { 1204 if (!packing.rowLength) { 1205 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, packing.skipPixels + size.x); 1206 } 1207 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, packing.skipPixels + writeX); 1208 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, packing.skipRows + writeY); 1209 1210 auto desc2 = desc; 1211 desc2.srcOffset = {readX, readY}; 1212 desc2.size = rwSize; 1213 1214 DoReadPixelsAndConvert(srcFormat->format, desc2, dest, bytesNeeded, 1215 rowStride); 1216 } else { 1217 // I *did* say "hilariously slow". 1218 1219 auto desc2 = desc; 1220 desc2.srcOffset = {readX, readY}; 1221 desc2.size = {rwSize.x, 1}; 1222 1223 const auto skipBytes = writeX * explicitPacking.metrics.bytesPerPixel; 1224 const auto usedRowBytes = rwSize.x * explicitPacking.metrics.bytesPerPixel; 1225 for (const auto j : IntegerRange(rwSize.y)) { 1226 desc2.srcOffset.y = readY + j; 1227 const auto destWriteBegin = dest + skipBytes + (writeY + j) * rowStride; 1228 MOZ_RELEASE_ASSERT(dest <= destWriteBegin); 1229 MOZ_RELEASE_ASSERT(destWriteBegin <= dest + availBytes); 1230 1231 const auto destWriteEnd = destWriteBegin + usedRowBytes; 1232 MOZ_RELEASE_ASSERT(dest <= destWriteEnd); 1233 MOZ_RELEASE_ASSERT(destWriteEnd <= dest + availBytes); 1234 1235 DoReadPixelsAndConvert(srcFormat->format, desc2, destWriteBegin, 1236 destWriteEnd - destWriteBegin, rowStride); 1237 } 1238 } 1239 1240 return res; 1241 } 1242 1243 void WebGLContext::RenderbufferStorageMultisample(WebGLRenderbuffer& rb, 1244 uint32_t samples, 1245 GLenum internalFormat, 1246 uint32_t width, 1247 uint32_t height) const { 1248 const FuncScope funcScope(*this, "renderbufferStorage(Multisample)?"); 1249 if (IsContextLost()) return; 1250 1251 rb.RenderbufferStorage(samples, internalFormat, width, height); 1252 } 1253 1254 void WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height) { 1255 const FuncScope funcScope(*this, "scissor"); 1256 if (IsContextLost()) return; 1257 1258 if (!ValidateNonNegative("width", width) || 1259 !ValidateNonNegative("height", height)) { 1260 return; 1261 } 1262 1263 mScissorRect = {x, y, width, height}; 1264 mScissorRect.Apply(*gl); 1265 } 1266 1267 void WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, 1268 GLuint mask) { 1269 const FuncScope funcScope(*this, "stencilFuncSeparate"); 1270 if (IsContextLost()) return; 1271 1272 if (!ValidateFaceEnum(face) || !ValidateComparisonEnum(*this, func)) { 1273 return; 1274 } 1275 1276 switch (face) { 1277 case LOCAL_GL_FRONT_AND_BACK: 1278 mStencilRefFront = ref; 1279 mStencilRefBack = ref; 1280 mStencilValueMaskFront = mask; 1281 mStencilValueMaskBack = mask; 1282 break; 1283 case LOCAL_GL_FRONT: 1284 mStencilRefFront = ref; 1285 mStencilValueMaskFront = mask; 1286 break; 1287 case LOCAL_GL_BACK: 1288 mStencilRefBack = ref; 1289 mStencilValueMaskBack = mask; 1290 break; 1291 } 1292 1293 gl->fStencilFuncSeparate(face, func, ref, mask); 1294 } 1295 1296 void WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, 1297 GLenum dppass) { 1298 const FuncScope funcScope(*this, "stencilOpSeparate"); 1299 if (IsContextLost()) return; 1300 1301 if (!ValidateFaceEnum(face) || !ValidateStencilOpEnum(sfail, "sfail") || 1302 !ValidateStencilOpEnum(dpfail, "dpfail") || 1303 !ValidateStencilOpEnum(dppass, "dppass")) 1304 return; 1305 1306 gl->fStencilOpSeparate(face, sfail, dpfail, dppass); 1307 } 1308 1309 //////////////////////////////////////////////////////////////////////////////// 1310 // Uniform setters. 1311 1312 void WebGLContext::UniformData( 1313 const uint32_t loc, const bool transpose, 1314 const Span<const webgl::UniformDataVal>& data) const { 1315 const FuncScope funcScope(*this, "uniform setter"); 1316 1317 if (!IsWebGL2() && transpose) { 1318 GenerateError(LOCAL_GL_INVALID_VALUE, "`transpose`:true requires WebGL 2."); 1319 return; 1320 } 1321 1322 // - 1323 1324 const auto& link = mActiveProgramLinkInfo; 1325 if (!link) { 1326 GenerateError(LOCAL_GL_INVALID_OPERATION, "Active program is not linked."); 1327 return; 1328 } 1329 1330 const auto locInfo = MaybeFind(link->locationMap, loc); 1331 if (!locInfo) { 1332 // Null WebGLUniformLocations become -1, which will end up here. 1333 return; 1334 } 1335 1336 const auto& validationInfo = locInfo->info; 1337 const auto& activeInfo = validationInfo.info; 1338 const auto& channels = validationInfo.channelsPerElem; 1339 const auto& pfn = validationInfo.pfn; 1340 1341 // - 1342 1343 const auto lengthInType = data.size(); 1344 const auto elemCount = lengthInType / channels; 1345 if (elemCount > 1 && !validationInfo.isArray) { 1346 GenerateError( 1347 LOCAL_GL_INVALID_OPERATION, 1348 "(uniform %s) `values` length (%u) must exactly match size of %s.", 1349 activeInfo.name.c_str(), (uint32_t)lengthInType, 1350 EnumString(activeInfo.elemType).c_str()); 1351 return; 1352 } 1353 1354 // - 1355 1356 const auto& samplerInfo = locInfo->samplerInfo; 1357 if (samplerInfo) { 1358 const auto idata = ReinterpretToSpan<const uint32_t>::From(data); 1359 const auto maxTexUnits = GLMaxTextureUnits(); 1360 for (const auto& val : idata) { 1361 if (val >= maxTexUnits) { 1362 ErrorInvalidValue( 1363 "This uniform location is a sampler, but %d" 1364 " is not a valid texture unit.", 1365 val); 1366 return; 1367 } 1368 } 1369 } 1370 1371 // - 1372 1373 // This is a little galaxy-brain, sorry! 1374 const auto ptr = static_cast<const void*>(data.data()); 1375 (*pfn)(*gl, static_cast<GLint>(loc), elemCount, transpose, ptr); 1376 1377 // - 1378 1379 if (samplerInfo) { 1380 auto& texUnits = samplerInfo->texUnits; 1381 1382 const auto srcBegin = reinterpret_cast<const uint32_t*>(data.data()); 1383 auto destIndex = locInfo->indexIntoUniform; 1384 if (destIndex < texUnits.length()) { 1385 // Only sample as many indexes as available tex units allow. 1386 const auto destCount = std::min(elemCount, texUnits.length() - destIndex); 1387 for (const auto& val : Span<const uint32_t>(srcBegin, destCount)) { 1388 texUnits[destIndex] = AssertedCast<uint8_t>(val); 1389 destIndex += 1; 1390 } 1391 } 1392 } 1393 } 1394 1395 //////////////////////////////////////////////////////////////////////////////// 1396 1397 void WebGLContext::UseProgram(WebGLProgram* prog) { 1398 FuncScope funcScope(*this, "useProgram"); 1399 if (IsContextLost()) return; 1400 funcScope.mBindFailureGuard = true; 1401 1402 if (!prog) { 1403 mCurrentProgram = nullptr; 1404 mActiveProgramLinkInfo = nullptr; 1405 funcScope.mBindFailureGuard = false; 1406 return; 1407 } 1408 1409 if (!ValidateObject("prog", *prog)) return; 1410 1411 if (!prog->UseProgram()) return; 1412 1413 mCurrentProgram = prog; 1414 mActiveProgramLinkInfo = mCurrentProgram->LinkInfo(); 1415 1416 funcScope.mBindFailureGuard = false; 1417 } 1418 1419 bool WebGLContext::ValidateProgram(const WebGLProgram& prog) const { 1420 const FuncScope funcScope(*this, "validateProgram"); 1421 if (IsContextLost()) return false; 1422 1423 return prog.ValidateProgram(); 1424 } 1425 1426 RefPtr<WebGLFramebuffer> WebGLContext::CreateFramebuffer() { 1427 const FuncScope funcScope(*this, "createFramebuffer"); 1428 if (IsContextLost()) return nullptr; 1429 1430 GLuint fbo = 0; 1431 gl->fGenFramebuffers(1, &fbo); 1432 1433 return new WebGLFramebuffer(this, fbo); 1434 } 1435 1436 RefPtr<WebGLFramebuffer> WebGLContext::CreateOpaqueFramebuffer( 1437 const webgl::OpaqueFramebufferOptions& options) { 1438 const FuncScope funcScope(*this, "createOpaqueFramebuffer"); 1439 if (IsContextLost()) return nullptr; 1440 1441 uint32_t samples = options.antialias ? StaticPrefs::webgl_msaa_samples() : 0; 1442 samples = std::min(samples, gl->MaxSamples()); 1443 const gfx::IntSize size = {options.width, options.height}; 1444 1445 auto fbo = 1446 gl::MozFramebuffer::Create(gl, size, samples, options.depthStencil); 1447 if (!fbo) { 1448 return nullptr; 1449 } 1450 1451 return new WebGLFramebuffer(this, std::move(fbo)); 1452 } 1453 1454 RefPtr<WebGLRenderbuffer> WebGLContext::CreateRenderbuffer() { 1455 const FuncScope funcScope(*this, "createRenderbuffer"); 1456 if (IsContextLost()) return nullptr; 1457 1458 return new WebGLRenderbuffer(this); 1459 } 1460 1461 void WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height) { 1462 const FuncScope funcScope(*this, "viewport"); 1463 if (IsContextLost()) return; 1464 1465 if (!ValidateNonNegative("width", width) || 1466 !ValidateNonNegative("height", height)) { 1467 return; 1468 } 1469 1470 const auto& limits = Limits(); 1471 width = std::min(width, static_cast<GLsizei>(limits.maxViewportDim)); 1472 height = std::min(height, static_cast<GLsizei>(limits.maxViewportDim)); 1473 1474 gl->fViewport(x, y, width, height); 1475 1476 mViewportX = x; 1477 mViewportY = y; 1478 mViewportWidth = width; 1479 mViewportHeight = height; 1480 } 1481 1482 void WebGLContext::CompileShader(WebGLShader& shader) { 1483 const FuncScope funcScope(*this, "compileShader"); 1484 if (IsContextLost()) return; 1485 1486 if (!ValidateObject("shader", shader)) return; 1487 1488 shader.CompileShader(); 1489 } 1490 1491 void WebGLContext::ShaderSource(WebGLShader& shader, 1492 const std::string& source) const { 1493 const FuncScope funcScope(*this, "shaderSource"); 1494 if (IsContextLost()) return; 1495 1496 shader.ShaderSource(source); 1497 } 1498 1499 void WebGLContext::BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { 1500 const FuncScope funcScope(*this, "blendColor"); 1501 if (IsContextLost()) return; 1502 1503 gl->fBlendColor(r, g, b, a); 1504 } 1505 1506 void WebGLContext::Flush() { 1507 const FuncScope funcScope(*this, "flush"); 1508 if (IsContextLost()) return; 1509 1510 gl->fFlush(); 1511 } 1512 1513 void WebGLContext::Finish() { 1514 const FuncScope funcScope(*this, "finish"); 1515 if (IsContextLost()) return; 1516 1517 gl->fFinish(); 1518 1519 mCompletedFenceId = mNextFenceId; 1520 mNextFenceId += 1; 1521 } 1522 1523 void WebGLContext::LineWidth(GLfloat width) { 1524 const FuncScope funcScope(*this, "lineWidth"); 1525 if (IsContextLost()) return; 1526 1527 // Doing it this way instead of `if (width <= 0.0)` handles NaNs. 1528 const bool isValid = width > 0.0; 1529 if (!isValid) { 1530 ErrorInvalidValue("`width` must be positive and non-zero."); 1531 return; 1532 } 1533 1534 mLineWidth = width; 1535 1536 if (gl->IsCoreProfile() && width > 1.0) { 1537 width = 1.0; 1538 } 1539 1540 gl->fLineWidth(width); 1541 } 1542 1543 void WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) { 1544 const FuncScope funcScope(*this, "polygonOffset"); 1545 if (IsContextLost()) return; 1546 1547 gl->fPolygonOffset(factor, units); 1548 } 1549 1550 void WebGLContext::ProvokingVertex(const webgl::ProvokingVertex mode) const { 1551 const FuncScope funcScope(*this, "provokingVertex"); 1552 if (IsContextLost()) return; 1553 MOZ_RELEASE_ASSERT( 1554 IsExtensionEnabled(WebGLExtensionID::WEBGL_provoking_vertex)); 1555 1556 gl->fProvokingVertex(UnderlyingValue(mode)); 1557 } 1558 1559 void WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) { 1560 const FuncScope funcScope(*this, "sampleCoverage"); 1561 if (IsContextLost()) return; 1562 1563 gl->fSampleCoverage(value, invert); 1564 } 1565 1566 } // namespace mozilla