ClientWebGLContext.cpp (230137B)
1 /* -*- Mode: C++; tab-width: 20; 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 "ClientWebGLContext.h" 7 8 #include <fmt/format.h> 9 10 #include <bitset> 11 12 #include "ClientWebGLExtensions.h" 13 #include "HostWebGLContext.h" 14 #include "TexUnpackBlob.h" 15 #include "WebGLChild.h" 16 #include "WebGLFormats.h" 17 #include "WebGLMethodDispatcher.h" 18 #include "WebGLTextureUpload.h" 19 #include "WebGLValidateStrings.h" 20 #include "gfxCrashReporterUtils.h" 21 #include "js/PropertyAndElement.h" // JS_DefineElement 22 #include "js/ScalarType.h" // js::Scalar::Type 23 #include "mozilla/Base64.h" 24 #include "mozilla/EnumeratedRange.h" 25 #include "mozilla/RandomNum.h" 26 #include "mozilla/ResultVariant.h" 27 #include "mozilla/ScopeExit.h" 28 #include "mozilla/StaticPrefs_webgl.h" 29 #include "mozilla/dom/BufferSourceBinding.h" 30 #include "mozilla/dom/Document.h" 31 #include "mozilla/dom/GeneratePlaceholderCanvasData.h" 32 #include "mozilla/dom/ToJSValue.h" 33 #include "mozilla/dom/TypedArray.h" 34 #include "mozilla/dom/WebGLContextEvent.h" 35 #include "mozilla/dom/WorkerCommon.h" 36 #include "mozilla/gfx/CanvasManagerChild.h" 37 #include "mozilla/gfx/Swizzle.h" 38 #include "mozilla/gfx/gfxVars.h" 39 #include "mozilla/ipc/Shmem.h" 40 #include "mozilla/layers/CompositableForwarder.h" 41 #include "mozilla/layers/CompositorBridgeChild.h" 42 #include "mozilla/layers/ImageBridgeChild.h" 43 #include "mozilla/layers/OOPCanvasRenderer.h" 44 #include "mozilla/layers/TextureClientSharedSurface.h" 45 #include "mozilla/layers/WebRenderCanvasRenderer.h" 46 #include "mozilla/layers/WebRenderUserData.h" 47 #include "nsContentUtils.h" 48 #include "nsDisplayList.h" 49 50 namespace mozilla { 51 52 namespace webgl { 53 std::string SanitizeRenderer(const std::string&); 54 std::string SanitizeVendor(const std::string&); 55 } // namespace webgl 56 57 // - 58 59 webgl::NotLostData::NotLostData(ClientWebGLContext& _context) 60 : context(_context) {} 61 62 webgl::NotLostData::~NotLostData() { 63 if (outOfProcess) { 64 outOfProcess->Destroy(); 65 } 66 } 67 68 // - 69 70 bool webgl::ObjectJS::ValidateForContext( 71 const ClientWebGLContext& targetContext, const char* const argName) const { 72 if (!IsForContext(targetContext)) { 73 targetContext.EnqueueError( 74 LOCAL_GL_INVALID_OPERATION, 75 "`%s` is from a different (or lost) WebGL context.", argName); 76 return false; 77 } 78 return true; 79 } 80 81 void webgl::ObjectJS::WarnInvalidUse(const ClientWebGLContext& targetContext, 82 const char* const argName) const { 83 if (!ValidateForContext(targetContext, argName)) return; 84 85 const auto errEnum = ErrorOnDeleted(); 86 targetContext.EnqueueError(errEnum, "Object `%s` is already deleted.", 87 argName); 88 } 89 90 // - 91 92 WebGLBufferJS::~WebGLBufferJS() { 93 const auto webgl = Context(); 94 if (webgl) { 95 webgl->DeleteBuffer(this); 96 } 97 } 98 99 WebGLFramebufferJS::~WebGLFramebufferJS() { 100 const auto webgl = Context(); 101 if (webgl) { 102 webgl->DeleteFramebuffer(this); 103 } 104 } 105 106 WebGLQueryJS::~WebGLQueryJS() { 107 const auto webgl = Context(); 108 if (webgl) { 109 webgl->DeleteQuery(this); 110 } 111 } 112 113 WebGLRenderbufferJS::~WebGLRenderbufferJS() { 114 const auto webgl = Context(); 115 if (webgl) { 116 webgl->DeleteRenderbuffer(this); 117 } 118 } 119 120 WebGLSamplerJS::~WebGLSamplerJS() { 121 const auto webgl = Context(); 122 if (webgl) { 123 webgl->DeleteSampler(this); 124 } 125 } 126 127 WebGLSyncJS::~WebGLSyncJS() { 128 const auto webgl = Context(); 129 if (webgl) { 130 webgl->DeleteSync(this); 131 } 132 } 133 134 WebGLTextureJS::~WebGLTextureJS() { 135 const auto webgl = Context(); 136 if (webgl) { 137 webgl->DeleteTexture(this); 138 } 139 } 140 141 WebGLTransformFeedbackJS::~WebGLTransformFeedbackJS() { 142 const auto webgl = Context(); 143 if (webgl) { 144 webgl->DeleteTransformFeedback(this); 145 } 146 } 147 148 WebGLVertexArrayJS::~WebGLVertexArrayJS() { 149 const auto webgl = Context(); 150 if (webgl) { 151 webgl->DeleteVertexArray(this); 152 } 153 } 154 155 // - 156 157 static bool GetJSScalarFromGLType(GLenum type, 158 js::Scalar::Type* const out_scalarType) { 159 switch (type) { 160 case LOCAL_GL_BYTE: 161 *out_scalarType = js::Scalar::Int8; 162 return true; 163 164 case LOCAL_GL_UNSIGNED_BYTE: 165 *out_scalarType = js::Scalar::Uint8; 166 return true; 167 168 case LOCAL_GL_SHORT: 169 *out_scalarType = js::Scalar::Int16; 170 return true; 171 172 case LOCAL_GL_HALF_FLOAT: 173 case LOCAL_GL_HALF_FLOAT_OES: 174 case LOCAL_GL_UNSIGNED_SHORT: 175 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: 176 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: 177 case LOCAL_GL_UNSIGNED_SHORT_5_6_5: 178 *out_scalarType = js::Scalar::Uint16; 179 return true; 180 181 case LOCAL_GL_UNSIGNED_INT: 182 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: 183 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: 184 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: 185 case LOCAL_GL_UNSIGNED_INT_24_8: 186 *out_scalarType = js::Scalar::Uint32; 187 return true; 188 case LOCAL_GL_INT: 189 *out_scalarType = js::Scalar::Int32; 190 return true; 191 192 case LOCAL_GL_FLOAT: 193 *out_scalarType = js::Scalar::Float32; 194 return true; 195 196 default: 197 return false; 198 } 199 } 200 201 ClientWebGLContext::ClientWebGLContext(const bool webgl2) 202 : mIsWebGL2(webgl2), 203 mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {} 204 205 ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); } 206 207 void ClientWebGLContext::JsWarning(const std::string& utf8) const { 208 nsIGlobalObject* global = nullptr; 209 if (mCanvasElement) { 210 mozilla::dom::Document* doc = mCanvasElement->OwnerDoc(); 211 if (doc) { 212 global = doc->GetScopeObject(); 213 } 214 } else if (mOffscreenCanvas) { 215 global = mOffscreenCanvas->GetOwnerGlobal(); 216 } 217 218 dom::AutoJSAPI api; 219 if (!api.Init(global)) { 220 return; 221 } 222 const auto& cx = api.cx(); 223 JS::WarnUTF8(cx, "%s", utf8.c_str()); 224 } 225 226 void AutoJsWarning(const std::string& utf8) { 227 if (NS_IsMainThread()) { 228 const AutoJSContext cx; 229 JS::WarnUTF8(cx, "%s", utf8.c_str()); 230 return; 231 } 232 233 JSContext* cx = dom::GetCurrentWorkerThreadJSContext(); 234 if (NS_WARN_IF(!cx)) { 235 return; 236 } 237 238 JS::WarnUTF8(cx, "%s", utf8.c_str()); 239 } 240 241 // --------- 242 243 bool ClientWebGLContext::DispatchEvent(const nsAString& eventName) const { 244 const auto kCanBubble = CanBubble::eYes; 245 const auto kIsCancelable = Cancelable::eYes; 246 bool useDefaultHandler = true; 247 248 if (mCanvasElement) { 249 nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(), 250 mCanvasElement, eventName, kCanBubble, 251 kIsCancelable, &useDefaultHandler); 252 } else if (mOffscreenCanvas) { 253 // OffscreenCanvas case 254 RefPtr<dom::Event> event = 255 new dom::Event(mOffscreenCanvas, nullptr, nullptr); 256 event->InitEvent(eventName, kCanBubble, kIsCancelable); 257 event->SetTrusted(true); 258 useDefaultHandler = mOffscreenCanvas->DispatchEvent( 259 *event, dom::CallerType::System, IgnoreErrors()); 260 } 261 return useDefaultHandler; 262 } 263 264 // - 265 266 void ClientWebGLContext::EmulateLoseContext() const { 267 const FuncScope funcScope(*this, "loseContext"); 268 if (mLossStatus != webgl::LossStatus::Ready) { 269 JsWarning("loseContext: Already lost."); 270 if (!mNextError) { 271 mNextError = LOCAL_GL_INVALID_OPERATION; 272 } 273 return; 274 } 275 OnContextLoss(webgl::ContextLossReason::Manual); 276 } 277 278 void ClientWebGLContext::OnContextLoss( 279 const webgl::ContextLossReason reason) const { 280 JsWarning("WebGL context was lost."); 281 282 RefPtr<webgl::NotLostData> notLost(mNotLost); 283 if (notLost) { 284 for (const auto& ext : notLost->extensions) { 285 if (!ext) continue; 286 ext->mContext = nullptr; // Detach. 287 } 288 mNotLost = nullptr; // Lost now! 289 mNextError = LOCAL_GL_CONTEXT_LOST_WEBGL; 290 } 291 292 switch (reason) { 293 case webgl::ContextLossReason::Guilty: 294 mLossStatus = webgl::LossStatus::LostForever; 295 break; 296 297 case webgl::ContextLossReason::None: 298 mLossStatus = webgl::LossStatus::Lost; 299 break; 300 301 case webgl::ContextLossReason::Manual: 302 mLossStatus = webgl::LossStatus::LostManually; 303 break; 304 } 305 306 const auto weak = WeakPtr<const ClientWebGLContext>(this); 307 const auto fnRun = [weak]() { 308 const auto strong = RefPtr<const ClientWebGLContext>(weak); 309 if (!strong) return; 310 strong->Event_webglcontextlost(); 311 }; 312 already_AddRefed<mozilla::CancelableRunnable> runnable = 313 NS_NewCancelableRunnableFunction("enqueue Event_webglcontextlost", fnRun); 314 NS_DispatchToCurrentThread(std::move(runnable)); 315 } 316 317 void ClientWebGLContext::Event_webglcontextlost() const { 318 const bool useDefaultHandler = DispatchEvent(u"webglcontextlost"_ns); 319 if (useDefaultHandler) { 320 mLossStatus = webgl::LossStatus::LostForever; 321 } 322 323 if (mLossStatus == webgl::LossStatus::Lost) { 324 RestoreContext(webgl::LossStatus::Lost); 325 } 326 } 327 328 void ClientWebGLContext::RestoreContext( 329 const webgl::LossStatus requiredStatus) const { 330 if (requiredStatus != mLossStatus) { 331 JsWarning( 332 "restoreContext: Only valid iff context lost with loseContext()."); 333 if (!mNextError) { 334 mNextError = LOCAL_GL_INVALID_OPERATION; 335 } 336 return; 337 } 338 MOZ_RELEASE_ASSERT(mLossStatus == webgl::LossStatus::Lost || 339 mLossStatus == webgl::LossStatus::LostManually); 340 341 if (mAwaitingRestore) return; 342 mAwaitingRestore = true; 343 344 const auto weak = WeakPtr<const ClientWebGLContext>(this); 345 const auto fnRun = [weak]() { 346 const auto strong = RefPtr<const ClientWebGLContext>(weak); 347 if (!strong) return; 348 strong->Event_webglcontextrestored(); 349 }; 350 already_AddRefed<mozilla::CancelableRunnable> runnable = 351 NS_NewCancelableRunnableFunction("enqueue Event_webglcontextrestored", 352 fnRun); 353 NS_DispatchToCurrentThread(std::move(runnable)); 354 } 355 356 void ClientWebGLContext::Event_webglcontextrestored() const { 357 mAwaitingRestore = false; 358 mLossStatus = webgl::LossStatus::Ready; 359 mNextError = 0; 360 361 uvec2 requestSize; 362 if (mCanvasElement) { 363 requestSize = {mCanvasElement->Width(), mCanvasElement->Height()}; 364 } else if (mOffscreenCanvas) { 365 requestSize = {mOffscreenCanvas->Width(), mOffscreenCanvas->Height()}; 366 } else { 367 MOZ_ASSERT_UNREACHABLE("no HTMLCanvasElement or OffscreenCanvas!"); 368 return; 369 } 370 371 if (!requestSize.x) { 372 requestSize.x = 1; 373 } 374 if (!requestSize.y) { 375 requestSize.y = 1; 376 } 377 378 const auto mutThis = const_cast<ClientWebGLContext*>( 379 this); // TODO: Make context loss non-mutable. 380 if (!mutThis->CreateHostContext(requestSize)) { 381 mLossStatus = webgl::LossStatus::LostForever; 382 return; 383 } 384 385 mResetLayer = true; 386 387 (void)DispatchEvent(u"webglcontextrestored"_ns); 388 } 389 390 // --------- 391 392 void ClientWebGLContext::ThrowEvent_WebGLContextCreationError( 393 const std::string& text) const { 394 nsCString msg; 395 msg.AppendPrintf("Failed to create WebGL context: %s", text.c_str()); 396 JsWarning(msg.BeginReading()); 397 398 RefPtr<dom::EventTarget> target = mCanvasElement; 399 if (!target && mOffscreenCanvas) { 400 target = mOffscreenCanvas; 401 } else if (!target) { 402 return; 403 } 404 405 const auto kEventName = u"webglcontextcreationerror"_ns; 406 407 dom::WebGLContextEventInit eventInit; 408 // eventInit.mCancelable = true; // The spec says this, but it's silly. 409 eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text.c_str()); 410 411 const RefPtr<dom::WebGLContextEvent> event = 412 dom::WebGLContextEvent::Constructor(target, kEventName, eventInit); 413 event->SetTrusted(true); 414 415 target->DispatchEvent(*event); 416 } 417 418 // ------------------------------------------------------------------------- 419 // Client-side helper methods. Dispatch to a Host method. 420 // ------------------------------------------------------------------------- 421 422 // If we are running WebGL in this process then call the HostWebGLContext 423 // method directly. Otherwise, dispatch over IPC. 424 template <typename MethodT, typename... Args> 425 void ClientWebGLContext::Run_WithDestArgTypes( 426 std::optional<JS::AutoCheckCannotGC>&& noGc, const MethodT method, 427 const size_t id, const Args&... args) const { 428 // `AutoCheckCannotGC` must be reset after the GC data is done being used but 429 // *before* the `notLost` destructor runs, since the latter can GC. 430 const auto cleanup = MakeScopeExit([&]() { noGc.reset(); }); 431 432 RefPtr<webgl::NotLostData> notLost(mNotLost); 433 if (!notLost) { 434 return; 435 } 436 437 const auto& inProcess = notLost->inProcess; 438 if (inProcess) { 439 (inProcess.get()->*method)(args...); 440 return; 441 } 442 443 const auto& child = notLost->outOfProcess; 444 445 const auto info = webgl::SerializationInfo(id, args...); 446 const auto maybeDest = child->AllocPendingCmdBytes(info.requiredByteCount, 447 info.alignmentOverhead); 448 if (!maybeDest) { 449 noGc.reset(); // Reset early, as GC data will not be used, but JsWarning 450 // can GC. 451 JsWarning("Failed to allocate internal command buffer."); 452 OnContextLoss(webgl::ContextLossReason::None); 453 return; 454 } 455 const auto& destBytes = *maybeDest; 456 webgl::Serialize(destBytes, id, args...); 457 } 458 459 // - 460 461 #define RPROC(_METHOD) \ 462 decltype(&HostWebGLContext::_METHOD), &HostWebGLContext::_METHOD 463 464 // ------------------------- Composition, etc ------------------------- 465 466 void ClientWebGLContext::OnBeforePaintTransaction() { Present(nullptr); } 467 468 void ClientWebGLContext::EndComposition() { 469 // Mark ourselves as no longer invalidated. 470 MarkContextClean(); 471 } 472 473 // - 474 475 layers::TextureType ClientWebGLContext::GetTexTypeForSwapChain() const { 476 const RefPtr<layers::ImageBridgeChild> imageBridge = 477 layers::ImageBridgeChild::GetSingleton(); 478 const bool isOutOfProcess = mNotLost && mNotLost->outOfProcess != nullptr; 479 return layers::TexTypeForWebgl(imageBridge, isOutOfProcess); 480 } 481 482 void ClientWebGLContext::Present(WebGLFramebufferJS* const xrFb, 483 const bool webvr, 484 const webgl::SwapChainOptions& options) { 485 const auto texType = GetTexTypeForSwapChain(); 486 Present(xrFb, texType, webvr, options); 487 } 488 489 // Fill in remote texture ids to SwapChainOptions if async present is enabled. 490 webgl::SwapChainOptions ClientWebGLContext::PrepareAsyncSwapChainOptions( 491 WebGLFramebufferJS* fb, bool webvr, 492 const webgl::SwapChainOptions& options) { 493 // Currently remote texture ids should only be set internally. 494 MOZ_ASSERT(!options.remoteTextureOwnerId.IsValid() && 495 !options.remoteTextureId.IsValid()); 496 // Async present only works when out-of-process. It is not supported in WebVR. 497 // Allow it if it is either forced or if the pref is set. 498 if (fb || webvr) { 499 return options; 500 } 501 if (!IsContextLost() && !mNotLost->inProcess && 502 (options.forceAsyncPresent || 503 StaticPrefs::webgl_out_of_process_async_present())) { 504 if (!mRemoteTextureOwnerId) { 505 mRemoteTextureOwnerId = Some(layers::RemoteTextureOwnerId::GetNext()); 506 } 507 mLastRemoteTextureId = Some(layers::RemoteTextureId::GetNext()); 508 webgl::SwapChainOptions asyncOptions = options; 509 asyncOptions.remoteTextureOwnerId = *mRemoteTextureOwnerId; 510 asyncOptions.remoteTextureId = *mLastRemoteTextureId; 511 return asyncOptions; 512 } 513 // Clear the current remote texture id so that we disable async. 514 mRemoteTextureOwnerId = Nothing(); 515 return options; 516 } 517 518 void ClientWebGLContext::Present(WebGLFramebufferJS* const xrFb, 519 const layers::TextureType type, 520 const bool webvr, 521 const webgl::SwapChainOptions& options) { 522 if (!mIsCanvasDirty && !xrFb) return; 523 if (!xrFb) { 524 mIsCanvasDirty = false; 525 } 526 CancelAutoFlush(); 527 webgl::SwapChainOptions asyncOptions = 528 PrepareAsyncSwapChainOptions(xrFb, webvr, options); 529 Run<RPROC(Present)>(xrFb ? xrFb->mId : 0, type, webvr, asyncOptions); 530 } 531 532 void ClientWebGLContext::CopyToSwapChain( 533 WebGLFramebufferJS* const fb, const webgl::SwapChainOptions& options) { 534 CancelAutoFlush(); 535 const auto texType = GetTexTypeForSwapChain(); 536 webgl::SwapChainOptions asyncOptions = 537 PrepareAsyncSwapChainOptions(fb, false, options); 538 Run<RPROC(CopyToSwapChain)>(fb ? fb->mId : 0, texType, asyncOptions); 539 } 540 541 void ClientWebGLContext::EndOfFrame() { 542 CancelAutoFlush(); 543 Run<RPROC(EndOfFrame)>(); 544 } 545 546 Maybe<layers::SurfaceDescriptor> ClientWebGLContext::GetFrontBuffer( 547 WebGLFramebufferJS* const fb, bool vr) { 548 const FuncScope funcScope(*this, "<GetFrontBuffer>"); 549 if (IsContextLost()) return {}; 550 551 const auto& inProcess = mNotLost->inProcess; 552 if (inProcess) { 553 return inProcess->GetFrontBuffer(fb ? fb->mId : 0, vr); 554 } 555 556 const auto& child = mNotLost->outOfProcess; 557 child->FlushPendingCmds(); 558 559 // Always synchronously get the front buffer if not using a remote texture. 560 bool needsSync = true; 561 Maybe<layers::SurfaceDescriptor> syncDesc; 562 Maybe<layers::SurfaceDescriptor> remoteDesc; 563 auto& info = child->GetFlushedCmdInfo(); 564 565 // If valid remote texture data was set for async present, then use it. 566 if (!fb && !vr && mRemoteTextureOwnerId && mLastRemoteTextureId) { 567 const auto tooManyFlushes = 10; 568 // If there are many flushed cmds, force synchronous IPC to avoid too many 569 // pending ipc messages. Otherwise don't sync for other cases to avoid any 570 // performance penalty. 571 needsSync = XRE_IsParentProcess() || 572 gfx::gfxVars::WebglOopAsyncPresentForceSync() || 573 info.flushesSinceLastCongestionCheck > tooManyFlushes; 574 575 // Only send over a remote texture descriptor if the WebGLChild actor is 576 // alive to ensure the remote texture id is valid. 577 if (child->CanSend()) { 578 remoteDesc = Some(layers::SurfaceDescriptorRemoteTexture( 579 *mLastRemoteTextureId, *mRemoteTextureOwnerId)); 580 } 581 } 582 583 if (needsSync && 584 !child->SendGetFrontBuffer(fb ? fb->mId : 0, vr, &syncDesc)) { 585 return {}; 586 } 587 588 // Reset flushesSinceLastCongestionCheck 589 info.flushesSinceLastCongestionCheck = 0; 590 info.congestionCheckGeneration++; 591 592 // If there is a remote texture descriptor, use that preferentially, as the 593 // sync front buffer descriptor was only created to force a sync first. 594 return remoteDesc ? remoteDesc : syncDesc; 595 } 596 597 Maybe<layers::SurfaceDescriptor> ClientWebGLContext::PresentFrontBuffer( 598 WebGLFramebufferJS* const fb, bool webvr) { 599 const auto texType = GetTexTypeForSwapChain(); 600 Present(fb, texType, webvr); 601 return GetFrontBuffer(fb, webvr); 602 } 603 604 already_AddRefed<layers::FwdTransactionTracker> 605 ClientWebGLContext::UseCompositableForwarder( 606 layers::CompositableForwarder* aForwarder) { 607 if (mRemoteTextureOwnerId) { 608 return layers::FwdTransactionTracker::GetOrCreate(mFwdTransactionTracker); 609 } 610 return nullptr; 611 } 612 613 void ClientWebGLContext::OnDestroyChild(dom::WebGLChild* aChild) { 614 // Since NotLostData may be destructing at this point, the RefPtr to 615 // WebGLChild may be unreliable. Instead, it must be explicitly passed in. 616 if (mRemoteTextureOwnerId && mFwdTransactionTracker && 617 mFwdTransactionTracker->IsUsed()) { 618 (void)aChild->SendWaitForTxn( 619 *mRemoteTextureOwnerId, 620 layers::ToRemoteTextureTxnType(mFwdTransactionTracker), 621 layers::ToRemoteTextureTxnId(mFwdTransactionTracker)); 622 } 623 } 624 625 void ClientWebGLContext::ClearVRSwapChain() { Run<RPROC(ClearVRSwapChain)>(); } 626 627 // - 628 629 bool ClientWebGLContext::UpdateWebRenderCanvasData( 630 nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) { 631 CanvasRenderer* renderer = aCanvasData->GetCanvasRenderer(); 632 633 if (!IsContextLost() && !mResetLayer && renderer) { 634 return true; 635 } 636 637 const auto& size = DrawingBufferSize(); 638 639 if (!IsContextLost() && !renderer && mNotLost->mCanvasRenderer && 640 mNotLost->mCanvasRenderer->GetSize() == gfx::IntSize(size.x, size.y) && 641 aCanvasData->SetCanvasRenderer(mNotLost->mCanvasRenderer)) { 642 mNotLost->mCanvasRenderer->SetDirty(); 643 mResetLayer = false; 644 return true; 645 } 646 647 renderer = aCanvasData->CreateCanvasRenderer(); 648 if (!InitializeCanvasRenderer(aBuilder, renderer)) { 649 // Clear CanvasRenderer of WebRenderCanvasData 650 aCanvasData->ClearCanvasRenderer(); 651 return false; 652 } 653 654 mNotLost->mCanvasRenderer = renderer; 655 656 MOZ_ASSERT(renderer); 657 mResetLayer = false; 658 659 return true; 660 } 661 662 bool ClientWebGLContext::InitializeCanvasRenderer( 663 nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer) { 664 const FuncScope funcScope(*this, "<InitializeCanvasRenderer>"); 665 if (IsContextLost()) return false; 666 667 layers::CanvasRendererData data; 668 data.mContext = this; 669 data.mOriginPos = gl::OriginPos::BottomLeft; 670 671 const auto& options = *mInitialOptions; 672 const auto& size = DrawingBufferSize(); 673 674 if (IsContextLost()) return false; 675 676 data.mIsOpaque = !options.alpha; 677 data.mIsAlphaPremult = !options.alpha || options.premultipliedAlpha; 678 data.mSize = {size.x, size.y}; 679 680 if (aBuilder->IsPaintingToWindow() && mCanvasElement) { 681 data.mDoPaintCallbacks = true; 682 } 683 684 aRenderer->Initialize(data); 685 aRenderer->SetDirty(); 686 return true; 687 } 688 689 void ClientWebGLContext::UpdateCanvasParameters() { 690 if (!mOffscreenCanvas) { 691 return; 692 } 693 694 const auto& options = *mInitialOptions; 695 const auto& size = DrawingBufferSize(); 696 697 mozilla::dom::OffscreenCanvasDisplayData data; 698 data.mOriginPos = gl::OriginPos::BottomLeft; 699 data.mIsOpaque = !options.alpha; 700 data.mIsAlphaPremult = !options.alpha || options.premultipliedAlpha; 701 data.mSize = {size.x, size.y}; 702 data.mDoPaintCallbacks = false; 703 704 mOffscreenCanvas->UpdateDisplayData(data); 705 } 706 707 layers::LayersBackend ClientWebGLContext::GetCompositorBackendType() const { 708 if (mCanvasElement) { 709 return mCanvasElement->GetCompositorBackendType(); 710 } else if (mOffscreenCanvas) { 711 return mOffscreenCanvas->GetCompositorBackendType(); 712 } 713 714 return layers::LayersBackend::LAYERS_NONE; 715 } 716 717 mozilla::dom::Document* ClientWebGLContext::GetOwnerDoc() const { 718 MOZ_ASSERT(mCanvasElement); 719 if (!mCanvasElement) { 720 return nullptr; 721 } 722 return mCanvasElement->OwnerDoc(); 723 } 724 725 void ClientWebGLContext::Commit() { 726 if (mOffscreenCanvas) { 727 mOffscreenCanvas->CommitFrameToCompositor(); 728 } 729 } 730 731 void ClientWebGLContext::GetCanvas( 732 dom::Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval) { 733 if (mCanvasElement) { 734 MOZ_RELEASE_ASSERT(!mOffscreenCanvas, "GFX: Canvas is offscreen."); 735 736 if (mCanvasElement->IsInNativeAnonymousSubtree()) { 737 retval.SetNull(); 738 } else { 739 retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement; 740 } 741 } else if (mOffscreenCanvas) { 742 retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas; 743 } else { 744 retval.SetNull(); 745 } 746 } 747 748 void ClientWebGLContext::SetDrawingBufferColorSpace( 749 const dom::PredefinedColorSpace val) { 750 mDrawingBufferColorSpace = val; 751 Run<RPROC(SetDrawingBufferColorSpace)>(*mDrawingBufferColorSpace); 752 } 753 754 void ClientWebGLContext::SetUnpackColorSpace( 755 const dom::PredefinedColorSpace val) { 756 mUnpackColorSpace = val; 757 Run<RPROC(SetUnpackColorSpace)>(*mUnpackColorSpace); 758 } 759 760 void ClientWebGLContext::GetContextAttributes( 761 dom::Nullable<dom::WebGLContextAttributes>& retval) { 762 retval.SetNull(); 763 const FuncScope funcScope(*this, "getContextAttributes"); 764 if (IsContextLost()) return; 765 766 dom::WebGLContextAttributes& result = retval.SetValue(); 767 768 const auto& options = mNotLost->info.options; 769 770 result.mAlpha.Construct(options.alpha); 771 result.mDepth = options.depth; 772 result.mStencil = options.stencil; 773 result.mAntialias.Construct(options.antialias); 774 result.mPremultipliedAlpha = options.premultipliedAlpha; 775 result.mPreserveDrawingBuffer = options.preserveDrawingBuffer; 776 result.mFailIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat; 777 result.mPowerPreference = options.powerPreference; 778 result.mForceSoftwareRendering = options.forceSoftwareRendering; 779 } 780 781 // ----------------------- 782 783 NS_IMETHODIMP 784 ClientWebGLContext::SetDimensions(const int32_t signedWidth, 785 const int32_t signedHeight) { 786 const FuncScope funcScope(*this, "<SetDimensions>"); 787 MOZ_ASSERT(mInitialOptions); 788 789 if (mLossStatus != webgl::LossStatus::Ready) { 790 // Attempted resize of a lost context. 791 return NS_OK; 792 } 793 794 uvec2 size = {static_cast<uint32_t>(signedWidth), 795 static_cast<uint32_t>(signedHeight)}; 796 if (!size.x) { 797 size.x = 1; 798 } 799 if (!size.y) { 800 size.y = 1; 801 } 802 const auto prevRequestedSize = mRequestedSize; 803 mRequestedSize = size; 804 805 mResetLayer = true; // Always treat this as resize. 806 807 RefPtr<webgl::NotLostData> notLost(mNotLost); 808 if (notLost) { 809 auto& state = State(); 810 811 auto curSize = prevRequestedSize; 812 if (state.mDrawingBufferSize) { 813 curSize = *state.mDrawingBufferSize; 814 } 815 if (size == curSize) return NS_OK; // MUST skip no-op resize 816 817 state.mDrawingBufferSize = Nothing(); 818 Run<RPROC(Resize)>(size); 819 820 UpdateCanvasParameters(); 821 MarkCanvasDirty(); 822 return NS_OK; 823 } 824 825 // - 826 // Context (re-)creation 827 828 if (!CreateHostContext(size)) { 829 return NS_ERROR_FAILURE; 830 } 831 return NS_OK; 832 } 833 834 void ClientWebGLContext::ResetBitmap() { 835 const auto size = DrawingBufferSize(); 836 Run<RPROC(Resize)>(size); // No-change resize still clears/resets everything. 837 } 838 839 static bool IsWebglOutOfProcessEnabled() { 840 if (StaticPrefs::webgl_out_of_process_force()) { 841 return true; 842 } 843 if (!gfx::gfxVars::AllowWebglOop()) { 844 return false; 845 } 846 if (!NS_IsMainThread()) { 847 return StaticPrefs::webgl_out_of_process_worker(); 848 } 849 return StaticPrefs::webgl_out_of_process(); 850 } 851 852 bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { 853 const auto pNotLost = MakeRefPtr<webgl::NotLostData>(*this); 854 auto& notLost = *pNotLost; 855 856 auto res = [&]() -> Result<Ok, std::string> { 857 auto options = *mInitialOptions; 858 if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) { 859 options.failIfMajorPerformanceCaveat = false; 860 } 861 862 if (options.failIfMajorPerformanceCaveat) { 863 const auto backend = GetCompositorBackendType(); 864 bool isCompositorSlow = false; 865 isCompositorSlow |= (backend == layers::LayersBackend::LAYERS_WR && 866 gfx::gfxVars::UseSoftwareWebRender()); 867 868 if (isCompositorSlow) { 869 return Err( 870 "failIfMajorPerformanceCaveat: Compositor is not" 871 " hardware-accelerated."); 872 } 873 } 874 875 const bool resistFingerprinting = 876 ShouldResistFingerprinting(RFPTarget::WebGLRenderCapability); 877 const auto principalKey = GetPrincipalHashValue(); 878 const auto initDesc = webgl::InitContextDesc{ 879 .isWebgl2 = mIsWebGL2, 880 .resistFingerprinting = resistFingerprinting, 881 .principalKey = principalKey, 882 .size = requestedSize, 883 .options = options, 884 }; 885 886 // - 887 888 auto useOop = IsWebglOutOfProcessEnabled(); 889 if (XRE_IsParentProcess()) { 890 useOop = false; 891 } 892 893 if (!useOop) { 894 notLost.inProcess = 895 HostWebGLContext::Create({this, nullptr}, initDesc, ¬Lost.info); 896 return Ok(); 897 } 898 899 // - 900 901 ScopedGfxFeatureReporter reporter("IpcWebGL"); 902 903 auto* const cm = gfx::CanvasManagerChild::Get(); 904 if (NS_WARN_IF(!cm)) { 905 return Err("!CanvasManagerChild::Get()"); 906 } 907 908 RefPtr<dom::WebGLChild> outOfProcess = new dom::WebGLChild(*this); 909 outOfProcess = 910 static_cast<dom::WebGLChild*>(cm->SendPWebGLConstructor(outOfProcess)); 911 if (!outOfProcess) { 912 return Err("SendPWebGLConstructor failed"); 913 } 914 915 // Clear RemoteTextureOwnerId. HostWebGLContext is going to be replaced in 916 // WebGLParent. 917 if (mRemoteTextureOwnerId.isSome()) { 918 mRemoteTextureOwnerId = Nothing(); 919 mFwdTransactionTracker = nullptr; 920 } 921 922 if (!outOfProcess->SendInitialize(initDesc, ¬Lost.info)) { 923 return Err("WebGL actor Initialize failed"); 924 } 925 926 notLost.outOfProcess = outOfProcess; 927 reporter.SetSuccessful(); 928 return Ok(); 929 }(); 930 if (!res.isOk()) { 931 auto str = res.unwrapErr(); 932 if (StartsWith(str, "failIfMajorPerformanceCaveat")) { 933 str += 934 " (about:config override available:" 935 " webgl.disable-fail-if-major-performance-caveat)"; 936 } 937 notLost.info.error = str; 938 } 939 if (!notLost.info.error->empty()) { 940 ThrowEvent_WebGLContextCreationError(notLost.info.error); 941 return false; 942 } 943 mNotLost = pNotLost; 944 UpdateCanvasParameters(); 945 MarkCanvasDirty(); 946 947 // Init state 948 const auto& limits = Limits(); 949 auto& state = State(); 950 state.mIsEnabledMap = webgl::MakeIsEnabledMap(mIsWebGL2); 951 952 state.mDefaultTfo = new WebGLTransformFeedbackJS(*this); 953 state.mDefaultVao = new WebGLVertexArrayJS(this); 954 955 state.mBoundTfo = state.mDefaultTfo; 956 state.mBoundVao = state.mDefaultVao; 957 958 (void)state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER]; 959 960 state.mTexUnits.resize(limits.maxTexUnits); 961 state.mBoundUbos.resize(limits.maxUniformBufferBindings); 962 963 { 964 webgl::TypedQuad initVal; 965 const float fData[4] = {0, 0, 0, 1}; 966 memcpy(initVal.data.data(), fData, initVal.data.size()); 967 state.mGenericVertexAttribs.resize(limits.maxVertexAttribs, initVal); 968 } 969 970 const auto& size = DrawingBufferSize(); 971 state.mViewport = {0, 0, static_cast<int32_t>(size.x), 972 static_cast<int32_t>(size.y)}; 973 state.mScissor = state.mViewport; 974 975 if (mIsWebGL2) { 976 // Insert keys to enable slots: 977 (void)state.mBoundBufferByTarget[LOCAL_GL_COPY_READ_BUFFER]; 978 (void)state.mBoundBufferByTarget[LOCAL_GL_COPY_WRITE_BUFFER]; 979 (void)state.mBoundBufferByTarget[LOCAL_GL_PIXEL_PACK_BUFFER]; 980 (void)state.mBoundBufferByTarget[LOCAL_GL_PIXEL_UNPACK_BUFFER]; 981 (void)state.mBoundBufferByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER]; 982 (void)state.mBoundBufferByTarget[LOCAL_GL_UNIFORM_BUFFER]; 983 984 (void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED]; 985 //(void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE]; 986 //// Same slot as ANY_SAMPLES_PASSED. 987 (void)state 988 .mCurrentQueryByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN]; 989 } 990 991 return true; 992 } 993 994 std::unordered_map<GLenum, bool> webgl::MakeIsEnabledMap(const bool webgl2) { 995 auto ret = std::unordered_map<GLenum, bool>{}; 996 997 ret[LOCAL_GL_BLEND] = false; 998 ret[LOCAL_GL_CULL_FACE] = false; 999 ret[LOCAL_GL_DEPTH_TEST] = false; 1000 ret[LOCAL_GL_DITHER] = true; 1001 ret[LOCAL_GL_POLYGON_OFFSET_FILL] = false; 1002 ret[LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE] = false; 1003 ret[LOCAL_GL_SAMPLE_COVERAGE] = false; 1004 ret[LOCAL_GL_SCISSOR_TEST] = false; 1005 ret[LOCAL_GL_STENCIL_TEST] = false; 1006 1007 if (webgl2) { 1008 ret[LOCAL_GL_RASTERIZER_DISCARD] = false; 1009 } 1010 1011 return ret; 1012 } 1013 1014 // ------- 1015 1016 uvec2 ClientWebGLContext::DrawingBufferSize() { 1017 if (IsContextLost()) return {}; 1018 RefPtr<webgl::NotLostData> notLost(mNotLost); 1019 auto& state = State(); 1020 auto& size = state.mDrawingBufferSize; 1021 1022 if (!size) { 1023 const auto& inProcess = notLost->inProcess; 1024 if (inProcess) { 1025 size = Some(inProcess->DrawingBufferSize()); 1026 } else { 1027 const auto& child = notLost->outOfProcess; 1028 child->FlushPendingCmds(); 1029 uvec2 actual = {}; 1030 if (!child->SendDrawingBufferSize(&actual)) return {}; 1031 size = Some(actual); 1032 } 1033 } 1034 1035 return *size; 1036 } 1037 1038 void ClientWebGLContext::OnMemoryPressure() { 1039 RefPtr<webgl::NotLostData> notLost(mNotLost); 1040 if (!notLost) { 1041 return; 1042 } 1043 1044 const auto& inProcess = notLost->inProcess; 1045 if (inProcess) { 1046 return inProcess->OnMemoryPressure(); 1047 } 1048 const auto& child = notLost->outOfProcess; 1049 if (child) { 1050 (void)child->SendOnMemoryPressure(); 1051 } 1052 } 1053 1054 NS_IMETHODIMP 1055 ClientWebGLContext::SetContextOptions(JSContext* cx, 1056 JS::Handle<JS::Value> options, 1057 ErrorResult& aRvForDictionaryInit) { 1058 if (mInitialOptions && options.isNullOrUndefined()) return NS_OK; 1059 1060 dom::WebGLContextAttributes attributes; 1061 if (!attributes.Init(cx, options)) { 1062 aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED); 1063 return NS_ERROR_UNEXPECTED; 1064 } 1065 1066 WebGLContextOptions newOpts; 1067 1068 newOpts.stencil = attributes.mStencil; 1069 newOpts.depth = attributes.mDepth; 1070 newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha; 1071 newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer; 1072 newOpts.failIfMajorPerformanceCaveat = 1073 attributes.mFailIfMajorPerformanceCaveat; 1074 newOpts.xrCompatible = attributes.mXrCompatible; 1075 newOpts.powerPreference = attributes.mPowerPreference; 1076 newOpts.forceSoftwareRendering = attributes.mForceSoftwareRendering; 1077 newOpts.enableDebugRendererInfo = 1078 StaticPrefs::webgl_enable_debug_renderer_info(); 1079 MOZ_ASSERT(mCanvasElement || mOffscreenCanvas); 1080 newOpts.shouldResistFingerprinting = 1081 ShouldResistFingerprinting(RFPTarget::WebGLRenderCapability); 1082 1083 if (attributes.mAlpha.WasPassed()) { 1084 newOpts.alpha = attributes.mAlpha.Value(); 1085 } 1086 if (attributes.mAntialias.WasPassed()) { 1087 newOpts.antialias = attributes.mAntialias.Value(); 1088 } 1089 1090 // Don't do antialiasing if we've disabled MSAA. 1091 if (!StaticPrefs::webgl_msaa_samples()) { 1092 newOpts.antialias = false; 1093 } 1094 1095 // - 1096 1097 if (mInitialOptions && *mInitialOptions != newOpts) { 1098 // Err if the options asked for aren't the same as what they were 1099 // originally. 1100 return NS_ERROR_FAILURE; 1101 } 1102 1103 mXRCompatible = attributes.mXrCompatible; 1104 1105 mInitialOptions.emplace(newOpts); 1106 return NS_OK; 1107 } 1108 1109 void ClientWebGLContext::DidRefresh() { Run<RPROC(DidRefresh)>(); } 1110 1111 already_AddRefed<gfx::SourceSurface> ClientWebGLContext::GetSurfaceSnapshot( 1112 gfxAlphaType* const out_alphaType) { 1113 const FuncScope funcScope(*this, "<GetSurfaceSnapshot>"); 1114 RefPtr<webgl::NotLostData> notLost(mNotLost); 1115 if (!notLost) { 1116 return nullptr; 1117 } 1118 1119 auto ret = BackBufferSnapshot(); 1120 if (!ret) return nullptr; 1121 1122 // - 1123 1124 const auto& options = notLost->info.options; 1125 1126 auto srcAlphaType = gfxAlphaType::Opaque; 1127 if (options.alpha) { 1128 if (options.premultipliedAlpha) { 1129 srcAlphaType = gfxAlphaType::Premult; 1130 } else { 1131 srcAlphaType = gfxAlphaType::NonPremult; 1132 } 1133 } 1134 1135 if (out_alphaType) { 1136 *out_alphaType = srcAlphaType; 1137 } else { 1138 // Expects Opaque or Premult 1139 if (srcAlphaType == gfxAlphaType::NonPremult) { 1140 const gfx::DataSourceSurface::ScopedMap map( 1141 ret, gfx::DataSourceSurface::READ_WRITE); 1142 MOZ_RELEASE_ASSERT(map.IsMapped(), "Failed to map snapshot surface!"); 1143 1144 const auto& size = ret->GetSize(); 1145 const auto format = ret->GetFormat(); 1146 bool rv = 1147 gfx::PremultiplyData(map.GetData(), map.GetStride(), format, 1148 map.GetData(), map.GetStride(), format, size); 1149 MOZ_RELEASE_ASSERT(rv, "PremultiplyData failed!"); 1150 } 1151 } 1152 1153 return ret.forget(); 1154 } 1155 1156 mozilla::ipc::IProtocol* ClientWebGLContext::SupportsSnapshotExternalCanvas() 1157 const { 1158 return GetChild(); 1159 } 1160 1161 RefPtr<gfx::SourceSurface> ClientWebGLContext::GetFrontBufferSnapshot( 1162 const bool requireAlphaPremult) { 1163 const FuncScope funcScope(*this, "<GetSurfaceSnapshot>"); 1164 RefPtr<webgl::NotLostData> notLost(mNotLost); 1165 if (!notLost) { 1166 return nullptr; 1167 } 1168 1169 const auto& options = notLost->info.options; 1170 1171 const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8 1172 : gfx::SurfaceFormat::B8G8R8X8; 1173 1174 const auto fnNewSurf = [&](const uvec2 size) { 1175 const auto stride = size.x * 4; 1176 return RefPtr<gfx::DataSourceSurface>( 1177 gfx::Factory::CreateDataSourceSurfaceWithStride({size.x, size.y}, 1178 surfFormat, stride, 1179 /*zero=*/true)); 1180 }; 1181 1182 const auto& inProcess = notLost->inProcess; 1183 if (inProcess) { 1184 const auto maybeSize = inProcess->FrontBufferSnapshotInto({}); 1185 if (!maybeSize) return nullptr; 1186 const auto& surfSize = *maybeSize; 1187 const auto stride = surfSize.x * 4; 1188 const auto byteSize = stride * surfSize.y; 1189 const auto surf = fnNewSurf(surfSize); 1190 if (!surf) return nullptr; 1191 { 1192 const gfx::DataSourceSurface::ScopedMap map( 1193 surf, gfx::DataSourceSurface::READ_WRITE); 1194 if (!map.IsMapped()) { 1195 MOZ_ASSERT(false); 1196 return nullptr; 1197 } 1198 MOZ_RELEASE_ASSERT(map.GetStride() == static_cast<int64_t>(stride)); 1199 auto range = Range<uint8_t>{map.GetData(), byteSize}; 1200 if (!inProcess->FrontBufferSnapshotInto(Some(range))) { 1201 gfxCriticalNote << "ClientWebGLContext::GetFrontBufferSnapshot: " 1202 "FrontBufferSnapshotInto(some) failed after " 1203 "FrontBufferSnapshotInto(none)"; 1204 return nullptr; 1205 } 1206 if (requireAlphaPremult && options.alpha && !options.premultipliedAlpha) { 1207 bool rv = gfx::PremultiplyData( 1208 map.GetData(), map.GetStride(), gfx::SurfaceFormat::R8G8B8A8, 1209 map.GetData(), map.GetStride(), gfx::SurfaceFormat::B8G8R8A8, 1210 surf->GetSize()); 1211 MOZ_RELEASE_ASSERT(rv, "PremultiplyData failed!"); 1212 } else { 1213 bool rv = gfx::SwizzleData( 1214 map.GetData(), map.GetStride(), gfx::SurfaceFormat::R8G8B8A8, 1215 map.GetData(), map.GetStride(), gfx::SurfaceFormat::B8G8R8A8, 1216 surf->GetSize()); 1217 MOZ_RELEASE_ASSERT(rv, "SwizzleData failed!"); 1218 } 1219 } 1220 return surf; 1221 } 1222 const auto& child = notLost->outOfProcess; 1223 child->FlushPendingCmds(); 1224 webgl::FrontBufferSnapshotIpc res; 1225 if (!child->SendGetFrontBufferSnapshot(&res)) { 1226 res = {}; 1227 } 1228 if (!res.shmem) return nullptr; 1229 1230 const auto& surfSize = res.surfSize; 1231 const webgl::RaiiShmem shmem{child, res.shmem.ref()}; 1232 if (!shmem) return nullptr; 1233 const auto& shmemBytes = shmem.ByteRange(); 1234 if (!surfSize.x) return nullptr; // Zero means failure. 1235 1236 const auto stride = surfSize.x * 4; 1237 const auto byteSize = stride * surfSize.y; 1238 1239 const auto surf = fnNewSurf(surfSize); 1240 if (!surf) return nullptr; 1241 1242 { 1243 const gfx::DataSourceSurface::ScopedMap map( 1244 surf, gfx::DataSourceSurface::READ_WRITE); 1245 if (!map.IsMapped()) { 1246 MOZ_ASSERT(false); 1247 return nullptr; 1248 } 1249 MOZ_RELEASE_ASSERT(shmemBytes.length() == byteSize); 1250 if (requireAlphaPremult && options.alpha && !options.premultipliedAlpha) { 1251 bool rv = gfx::PremultiplyData( 1252 shmemBytes.begin().get(), stride, gfx::SurfaceFormat::R8G8B8A8, 1253 map.GetData(), map.GetStride(), gfx::SurfaceFormat::B8G8R8A8, 1254 surf->GetSize()); 1255 MOZ_RELEASE_ASSERT(rv, "PremultiplyData failed!"); 1256 } else { 1257 bool rv = gfx::SwizzleData(shmemBytes.begin().get(), stride, 1258 gfx::SurfaceFormat::R8G8B8A8, map.GetData(), 1259 map.GetStride(), gfx::SurfaceFormat::B8G8R8A8, 1260 surf->GetSize()); 1261 MOZ_RELEASE_ASSERT(rv, "SwizzleData failed!"); 1262 } 1263 } 1264 return surf; 1265 } 1266 1267 RefPtr<gfx::DataSourceSurface> ClientWebGLContext::BackBufferSnapshot() { 1268 RefPtr<webgl::NotLostData> notLost(mNotLost); 1269 if (!notLost) { 1270 return nullptr; 1271 } 1272 1273 const auto& options = notLost->info.options; 1274 const auto& state = State(); 1275 1276 const auto drawFbWas = state.mBoundDrawFb; 1277 const auto readFbWas = state.mBoundReadFb; 1278 const auto pboWas = 1279 Find(state.mBoundBufferByTarget, LOCAL_GL_PIXEL_PACK_BUFFER); 1280 1281 const auto size = DrawingBufferSize(); 1282 1283 // - 1284 1285 BindFramebuffer(LOCAL_GL_FRAMEBUFFER, nullptr); 1286 if (pboWas) { 1287 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, nullptr); 1288 } 1289 1290 auto reset = MakeScopeExit([&] { 1291 if (drawFbWas == readFbWas) { 1292 BindFramebuffer(LOCAL_GL_FRAMEBUFFER, drawFbWas); 1293 } else { 1294 BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, drawFbWas); 1295 BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, readFbWas); 1296 } 1297 if (pboWas) { 1298 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pboWas); 1299 } 1300 }); 1301 1302 const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8 1303 : gfx::SurfaceFormat::B8G8R8X8; 1304 const auto stride = size.x * 4; 1305 RefPtr<gfx::DataSourceSurface> surf = 1306 gfx::Factory::CreateDataSourceSurfaceWithStride( 1307 {size.x, size.y}, surfFormat, stride, /*zero=*/true); 1308 if (NS_WARN_IF(!surf)) { 1309 // Was this an OOM or alloc-limit? (500MB is our default resource size 1310 // limit) 1311 surf = gfx::Factory::CreateDataSourceSurfaceWithStride({1, 1}, surfFormat, 1312 4, /*zero=*/true); 1313 if (!surf) { 1314 // Still failed for a 1x1 size. 1315 gfxCriticalError() << "CreateDataSourceSurfaceWithStride(surfFormat=" 1316 << surfFormat << ") failed."; 1317 } 1318 return nullptr; 1319 } 1320 1321 { 1322 const gfx::DataSourceSurface::ScopedMap map( 1323 surf, gfx::DataSourceSurface::READ_WRITE); 1324 if (!map.IsMapped()) { 1325 MOZ_ASSERT(false); 1326 return nullptr; 1327 } 1328 MOZ_ASSERT(static_cast<uint32_t>(map.GetStride()) == stride); 1329 1330 const auto desc = webgl::ReadPixelsDesc{{0, 0}, size}; 1331 const auto pixels = Span<uint8_t>(map.GetData(), stride * size.y); 1332 if (!DoReadPixels(desc, pixels)) return nullptr; 1333 1334 // RGBA->BGRA and flip-y. 1335 MOZ_RELEASE_ASSERT(gfx::SwizzleYFlipData( 1336 pixels.data(), stride, gfx::SurfaceFormat::R8G8B8A8, pixels.data(), 1337 stride, gfx::SurfaceFormat::B8G8R8A8, {size.x, size.y})); 1338 } 1339 1340 return surf; 1341 } 1342 1343 UniquePtr<uint8_t[]> ClientWebGLContext::GetImageBuffer( 1344 mozilla::CanvasUtils::ImageExtraction aExtractionBehavior, 1345 int32_t* out_format, gfx::IntSize* out_imageSize) { 1346 RefPtr<webgl::NotLostData> notLost(mNotLost); 1347 if (!notLost) { 1348 return nullptr; 1349 } 1350 1351 *out_format = 0; 1352 *out_imageSize = {}; 1353 1354 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied 1355 gfxAlphaType any; 1356 RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any); 1357 if (!snapshot) return nullptr; 1358 1359 RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface(); 1360 1361 const auto& premultAlpha = notLost->info.options.premultipliedAlpha; 1362 *out_imageSize = dataSurface->GetSize(); 1363 1364 nsRFPService::PotentiallyDumpImage(PrincipalOrNull(), dataSurface); 1365 if (aExtractionBehavior == CanvasUtils::ImageExtraction::Randomize) { 1366 return gfxUtils::GetImageBufferWithRandomNoise( 1367 dataSurface, premultAlpha, GetCookieJarSettings(), PrincipalOrNull(), 1368 out_format); 1369 } 1370 1371 return gfxUtils::GetImageBuffer(dataSurface, premultAlpha, out_format); 1372 } 1373 1374 NS_IMETHODIMP 1375 ClientWebGLContext::GetInputStream( 1376 const char* mimeType, const nsAString& encoderOptions, 1377 mozilla::CanvasUtils::ImageExtraction extractionBehavior, 1378 const nsACString& randomizationKey, nsIInputStream** out_stream) { 1379 RefPtr<webgl::NotLostData> notLost(mNotLost); 1380 if (!notLost) { 1381 return NS_ERROR_FAILURE; 1382 } 1383 1384 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied 1385 gfxAlphaType any; 1386 RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any); 1387 if (!snapshot) return NS_ERROR_FAILURE; 1388 1389 RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface(); 1390 const auto& premultAlpha = notLost->info.options.premultipliedAlpha; 1391 1392 nsRFPService::PotentiallyDumpImage(PrincipalOrNull(), dataSurface); 1393 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { 1394 return gfxUtils::GetInputStreamWithRandomNoise( 1395 dataSurface, premultAlpha, mimeType, encoderOptions, 1396 GetCookieJarSettings(), PrincipalOrNull(), out_stream); 1397 } 1398 1399 return gfxUtils::GetInputStream(dataSurface, premultAlpha, mimeType, 1400 encoderOptions, out_stream); 1401 } 1402 1403 // ------------------------- Client WebGL Objects ------------------------- 1404 // ------------------------- Create/Destroy/Is ------------------------- 1405 1406 template <typename T> 1407 static already_AddRefed<T> AsAddRefed(T* ptr) { 1408 RefPtr<T> rp = ptr; 1409 return rp.forget(); 1410 } 1411 1412 template <typename T> 1413 static RefPtr<T> AsRefPtr(T* ptr) { 1414 return {ptr}; 1415 } 1416 1417 already_AddRefed<WebGLBufferJS> ClientWebGLContext::CreateBuffer() const { 1418 const FuncScope funcScope(*this, "createBuffer"); 1419 1420 auto ret = AsRefPtr(new WebGLBufferJS(*this)); 1421 Run<RPROC(CreateBuffer)>(ret->mId); 1422 return ret.forget(); 1423 } 1424 1425 already_AddRefed<WebGLFramebufferJS> ClientWebGLContext::CreateFramebuffer() 1426 const { 1427 const FuncScope funcScope(*this, "createFramebuffer"); 1428 1429 auto ret = AsRefPtr(new WebGLFramebufferJS(*this)); 1430 Run<RPROC(CreateFramebuffer)>(ret->mId); 1431 return ret.forget(); 1432 } 1433 1434 already_AddRefed<WebGLFramebufferJS> 1435 ClientWebGLContext::CreateOpaqueFramebuffer( 1436 const webgl::OpaqueFramebufferOptions& options) const { 1437 const FuncScope funcScope(*this, "createOpaqueFramebuffer"); 1438 1439 auto ret = AsRefPtr(new WebGLFramebufferJS(*this, true)); 1440 1441 RefPtr<webgl::NotLostData> notLost(mNotLost); 1442 if (!notLost) { 1443 return ret.forget(); 1444 } 1445 1446 const auto& inProcess = notLost->inProcess; 1447 if (inProcess) { 1448 if (!inProcess->CreateOpaqueFramebuffer(ret->mId, options)) { 1449 ret = nullptr; 1450 } 1451 return ret.forget(); 1452 } 1453 const auto& child = notLost->outOfProcess; 1454 child->FlushPendingCmds(); 1455 bool ok = false; 1456 if (!child->SendCreateOpaqueFramebuffer(ret->mId, options, &ok)) 1457 return nullptr; 1458 if (!ok) return nullptr; 1459 1460 return ret.forget(); 1461 } 1462 1463 already_AddRefed<WebGLProgramJS> ClientWebGLContext::CreateProgram() const { 1464 const FuncScope funcScope(*this, "createProgram"); 1465 1466 auto ret = AsRefPtr(new WebGLProgramJS(*this)); 1467 Run<RPROC(CreateProgram)>(ret->mId); 1468 return ret.forget(); 1469 } 1470 1471 already_AddRefed<WebGLQueryJS> ClientWebGLContext::CreateQuery() const { 1472 const FuncScope funcScope(*this, "createQuery"); 1473 1474 auto ret = AsRefPtr(new WebGLQueryJS(this)); 1475 Run<RPROC(CreateQuery)>(ret->mId); 1476 return ret.forget(); 1477 } 1478 1479 already_AddRefed<WebGLRenderbufferJS> ClientWebGLContext::CreateRenderbuffer() 1480 const { 1481 const FuncScope funcScope(*this, "createRenderbuffer"); 1482 1483 auto ret = AsRefPtr(new WebGLRenderbufferJS(*this)); 1484 Run<RPROC(CreateRenderbuffer)>(ret->mId); 1485 return ret.forget(); 1486 } 1487 1488 already_AddRefed<WebGLSamplerJS> ClientWebGLContext::CreateSampler() const { 1489 const FuncScope funcScope(*this, "createSampler"); 1490 1491 auto ret = AsRefPtr(new WebGLSamplerJS(*this)); 1492 Run<RPROC(CreateSampler)>(ret->mId); 1493 return ret.forget(); 1494 } 1495 1496 already_AddRefed<WebGLShaderJS> ClientWebGLContext::CreateShader( 1497 const GLenum type) const { 1498 const FuncScope funcScope(*this, "createShader"); 1499 1500 switch (type) { 1501 case LOCAL_GL_VERTEX_SHADER: 1502 case LOCAL_GL_FRAGMENT_SHADER: 1503 break; 1504 default: 1505 EnqueueError_ArgEnum("type", type); 1506 return nullptr; 1507 } 1508 1509 auto ret = AsRefPtr(new WebGLShaderJS(*this, type)); 1510 Run<RPROC(CreateShader)>(ret->mId, ret->mType); 1511 return ret.forget(); 1512 } 1513 1514 already_AddRefed<WebGLSyncJS> ClientWebGLContext::FenceSync( 1515 const GLenum condition, const GLbitfield flags) const { 1516 const FuncScope funcScope(*this, "fenceSync"); 1517 1518 if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) { 1519 EnqueueError_ArgEnum("condition", condition); 1520 return nullptr; 1521 } 1522 1523 if (flags) { 1524 EnqueueError(LOCAL_GL_INVALID_VALUE, "`flags` must be 0."); 1525 return nullptr; 1526 } 1527 1528 auto ret = AsRefPtr(new WebGLSyncJS(*this)); 1529 Run<RPROC(CreateSync)>(ret->mId); 1530 1531 auto& availRunnable = EnsureAvailabilityRunnable(); 1532 availRunnable.mSyncs.push_back(ret.get()); 1533 ret->mCanBeAvailable = false; 1534 1535 AutoEnqueueFlush(); 1536 1537 return ret.forget(); 1538 } 1539 1540 already_AddRefed<WebGLTextureJS> ClientWebGLContext::CreateTexture() const { 1541 const FuncScope funcScope(*this, "createTexture"); 1542 1543 auto ret = AsRefPtr(new WebGLTextureJS(*this)); 1544 Run<RPROC(CreateTexture)>(ret->mId); 1545 return ret.forget(); 1546 } 1547 1548 already_AddRefed<WebGLTransformFeedbackJS> 1549 ClientWebGLContext::CreateTransformFeedback() const { 1550 const FuncScope funcScope(*this, "createTransformFeedback"); 1551 1552 auto ret = AsRefPtr(new WebGLTransformFeedbackJS(*this)); 1553 Run<RPROC(CreateTransformFeedback)>(ret->mId); 1554 return ret.forget(); 1555 } 1556 1557 already_AddRefed<WebGLVertexArrayJS> ClientWebGLContext::CreateVertexArray() 1558 const { 1559 const FuncScope funcScope(*this, "createVertexArray"); 1560 1561 auto ret = AsRefPtr(new WebGLVertexArrayJS(this)); 1562 Run<RPROC(CreateVertexArray)>(ret->mId); 1563 return ret.forget(); 1564 } 1565 1566 // - 1567 1568 static bool ValidateOrSkipForDelete(const ClientWebGLContext& context, 1569 const webgl::ObjectJS* const obj) { 1570 if (!obj) return false; 1571 if (!obj->ValidateForContext(context, "obj")) return false; 1572 if (obj->IsDeleted()) return false; 1573 return true; 1574 } 1575 1576 void ClientWebGLContext::DeleteBuffer(WebGLBufferJS* const obj) { 1577 const FuncScope funcScope(*this, "deleteBuffer"); 1578 if (IsContextLost()) return; 1579 if (!ValidateOrSkipForDelete(*this, obj)) return; 1580 auto& state = State(); 1581 1582 // Unbind from all bind points and bound containers 1583 1584 // UBOs 1585 for (const auto i : IntegerRange(state.mBoundUbos.size())) { 1586 if (state.mBoundUbos[i] == obj) { 1587 BindBufferBase(LOCAL_GL_UNIFORM_BUFFER, i, nullptr); 1588 } 1589 } 1590 1591 // TFO only if not active 1592 if (!state.mBoundTfo->mActiveOrPaused) { 1593 const auto& buffers = state.mBoundTfo->mAttribBuffers; 1594 for (const auto i : IntegerRange(buffers.size())) { 1595 if (buffers[i] == obj) { 1596 BindBufferBase(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, i, nullptr); 1597 } 1598 } 1599 } 1600 1601 // Generic/global bind points 1602 for (const auto& pair : state.mBoundBufferByTarget) { 1603 if (pair.second == obj) { 1604 BindBuffer(pair.first, nullptr); 1605 } 1606 } 1607 1608 // VAO attachments 1609 if (state.mBoundVao->mIndexBuffer == obj) { 1610 BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, nullptr); 1611 } 1612 1613 const auto& vaoBuffers = state.mBoundVao->mAttribBuffers; 1614 Maybe<WebGLBufferJS*> toRestore; 1615 for (const auto i : IntegerRange(vaoBuffers.size())) { 1616 if (vaoBuffers[i] == obj) { 1617 if (!toRestore) { 1618 toRestore = 1619 Some(state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER].get()); 1620 if (*toRestore) { 1621 BindBuffer(LOCAL_GL_ARRAY_BUFFER, nullptr); 1622 } 1623 } 1624 VertexAttribPointer(i, 4, LOCAL_GL_FLOAT, false, 0, 0); 1625 } 1626 } 1627 if (toRestore && *toRestore) { 1628 BindBuffer(LOCAL_GL_ARRAY_BUFFER, *toRestore); 1629 } 1630 1631 // - 1632 1633 obj->mDeleteRequested = true; 1634 Run<RPROC(DeleteBuffer)>(obj->mId); 1635 } 1636 1637 void ClientWebGLContext::DeleteFramebuffer(WebGLFramebufferJS* const obj, 1638 bool canDeleteOpaque) { 1639 const FuncScope funcScope(*this, "deleteFramebuffer"); 1640 if (IsContextLost()) return; 1641 if (!ValidateOrSkipForDelete(*this, obj)) return; 1642 if (!canDeleteOpaque && obj->mOpaque) { 1643 EnqueueError( 1644 LOCAL_GL_INVALID_OPERATION, 1645 "An opaque framebuffer's attachments cannot be inspected or changed."); 1646 return; 1647 } 1648 const auto& state = State(); 1649 1650 // Unbind 1651 const auto fnDetach = [&](const GLenum target, 1652 const WebGLFramebufferJS* const fb) { 1653 if (obj == fb) { 1654 BindFramebuffer(target, nullptr); 1655 } 1656 }; 1657 if (state.mBoundDrawFb == state.mBoundReadFb) { 1658 fnDetach(LOCAL_GL_FRAMEBUFFER, state.mBoundDrawFb.get()); 1659 } else { 1660 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER, state.mBoundDrawFb.get()); 1661 fnDetach(LOCAL_GL_READ_FRAMEBUFFER, state.mBoundReadFb.get()); 1662 } 1663 1664 obj->mDeleteRequested = true; 1665 Run<RPROC(DeleteFramebuffer)>(obj->mId); 1666 } 1667 1668 void ClientWebGLContext::DeleteProgram(WebGLProgramJS* const obj) const { 1669 const FuncScope funcScope(*this, "deleteProgram"); 1670 if (IsContextLost()) return; 1671 if (!ValidateOrSkipForDelete(*this, obj)) return; 1672 1673 // Don't unbind 1674 1675 obj->mKeepAlive = nullptr; 1676 } 1677 1678 webgl::ProgramKeepAlive::~ProgramKeepAlive() { 1679 if (!mParent) return; 1680 const auto& context = mParent->Context(); 1681 if (!context) return; 1682 context->DoDeleteProgram(*mParent); 1683 } 1684 1685 void ClientWebGLContext::DoDeleteProgram(WebGLProgramJS& obj) const { 1686 obj.mNextLink_Shaders = {}; 1687 Run<RPROC(DeleteProgram)>(obj.mId); 1688 } 1689 1690 static GLenum QuerySlotTarget(const GLenum specificTarget); 1691 1692 void ClientWebGLContext::DeleteQuery(WebGLQueryJS* const obj) { 1693 const FuncScope funcScope(*this, "deleteQuery"); 1694 if (IsContextLost()) return; 1695 if (!ValidateOrSkipForDelete(*this, obj)) return; 1696 const auto& state = State(); 1697 1698 // Unbind if current 1699 1700 if (obj->mTarget) { 1701 // Despite mTarget being set, we may not have called BeginQuery on this 1702 // object. QueryCounter may also set mTarget. 1703 const auto slotTarget = QuerySlotTarget(obj->mTarget); 1704 const auto curForTarget = 1705 MaybeFind(state.mCurrentQueryByTarget, slotTarget); 1706 1707 if (curForTarget && *curForTarget == obj) { 1708 EndQuery(obj->mTarget); 1709 } 1710 } 1711 1712 obj->mDeleteRequested = true; 1713 Run<RPROC(DeleteQuery)>(obj->mId); 1714 } 1715 1716 void ClientWebGLContext::DeleteRenderbuffer(WebGLRenderbufferJS* const obj) { 1717 const FuncScope funcScope(*this, "deleteRenderbuffer"); 1718 if (IsContextLost()) return; 1719 if (!ValidateOrSkipForDelete(*this, obj)) return; 1720 const auto& state = State(); 1721 1722 // Unbind 1723 if (state.mBoundRb == obj) { 1724 BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr); 1725 } 1726 1727 // Unbind from bound FBs 1728 const auto fnDetach = [&](const GLenum target, 1729 const WebGLFramebufferJS* const fb) { 1730 if (!fb) return; 1731 for (const auto& pair : fb->mAttachments) { 1732 if (pair.second.rb == obj) { 1733 FramebufferRenderbuffer(target, pair.first, LOCAL_GL_RENDERBUFFER, 1734 nullptr); 1735 } 1736 } 1737 }; 1738 if (state.mBoundDrawFb == state.mBoundReadFb) { 1739 fnDetach(LOCAL_GL_FRAMEBUFFER, state.mBoundDrawFb.get()); 1740 } else { 1741 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER, state.mBoundDrawFb.get()); 1742 fnDetach(LOCAL_GL_READ_FRAMEBUFFER, state.mBoundReadFb.get()); 1743 } 1744 1745 obj->mDeleteRequested = true; 1746 Run<RPROC(DeleteRenderbuffer)>(obj->mId); 1747 } 1748 1749 void ClientWebGLContext::DeleteSampler(WebGLSamplerJS* const obj) { 1750 const FuncScope funcScope(*this, "deleteSampler"); 1751 if (IsContextLost()) return; 1752 if (!ValidateOrSkipForDelete(*this, obj)) return; 1753 const auto& state = State(); 1754 1755 // Unbind 1756 for (const auto i : IntegerRange(state.mTexUnits.size())) { 1757 if (state.mTexUnits[i].sampler == obj) { 1758 BindSampler(i, nullptr); 1759 } 1760 } 1761 1762 obj->mDeleteRequested = true; 1763 Run<RPROC(DeleteSampler)>(obj->mId); 1764 } 1765 1766 void ClientWebGLContext::DeleteShader(WebGLShaderJS* const obj) const { 1767 const FuncScope funcScope(*this, "deleteShader"); 1768 if (IsContextLost()) return; 1769 if (!ValidateOrSkipForDelete(*this, obj)) return; 1770 1771 // Don't unbind 1772 1773 obj->mKeepAlive = nullptr; 1774 } 1775 1776 webgl::ShaderKeepAlive::~ShaderKeepAlive() { 1777 if (!mParent) return; 1778 const auto& context = mParent->Context(); 1779 if (!context) return; 1780 context->DoDeleteShader(*mParent); 1781 } 1782 1783 void ClientWebGLContext::DoDeleteShader(const WebGLShaderJS& obj) const { 1784 Run<RPROC(DeleteShader)>(obj.mId); 1785 } 1786 1787 void ClientWebGLContext::DeleteSync(WebGLSyncJS* const obj) const { 1788 const FuncScope funcScope(*this, "deleteSync"); 1789 if (IsContextLost()) return; 1790 if (!ValidateOrSkipForDelete(*this, obj)) return; 1791 1792 // Nothing to unbind 1793 1794 obj->mDeleteRequested = true; 1795 Run<RPROC(DeleteSync)>(obj->mId); 1796 } 1797 1798 void ClientWebGLContext::DeleteTexture(WebGLTextureJS* const obj) { 1799 const FuncScope funcScope(*this, "deleteTexture"); 1800 if (IsContextLost()) return; 1801 if (!ValidateOrSkipForDelete(*this, obj)) return; 1802 auto& state = State(); 1803 1804 // Unbind 1805 const auto& target = obj->mTarget; 1806 if (target) { 1807 // Unbind from tex units 1808 Maybe<uint32_t> restoreTexUnit; 1809 for (const auto i : IntegerRange(state.mTexUnits.size())) { 1810 if (state.mTexUnits[i].texByTarget[target] == obj) { 1811 if (!restoreTexUnit) { 1812 restoreTexUnit = Some(state.mActiveTexUnit); 1813 } 1814 ActiveTexture(LOCAL_GL_TEXTURE0 + i); 1815 BindTexture(target, nullptr); 1816 } 1817 } 1818 if (restoreTexUnit) { 1819 ActiveTexture(LOCAL_GL_TEXTURE0 + *restoreTexUnit); 1820 } 1821 1822 // Unbind from bound FBs 1823 const auto fnDetach = [&](const GLenum target, 1824 const WebGLFramebufferJS* const fb) { 1825 if (!fb) return; 1826 for (const auto& pair : fb->mAttachments) { 1827 if (pair.second.tex == obj) { 1828 FramebufferRenderbuffer(target, pair.first, LOCAL_GL_RENDERBUFFER, 1829 nullptr); 1830 } 1831 } 1832 }; 1833 if (state.mBoundDrawFb == state.mBoundReadFb) { 1834 fnDetach(LOCAL_GL_FRAMEBUFFER, state.mBoundDrawFb.get()); 1835 } else { 1836 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER, state.mBoundDrawFb.get()); 1837 fnDetach(LOCAL_GL_READ_FRAMEBUFFER, state.mBoundReadFb.get()); 1838 } 1839 } 1840 1841 obj->mDeleteRequested = true; 1842 Run<RPROC(DeleteTexture)>(obj->mId); 1843 } 1844 1845 void ClientWebGLContext::DeleteTransformFeedback( 1846 WebGLTransformFeedbackJS* const obj) { 1847 const FuncScope funcScope(*this, "deleteTransformFeedback"); 1848 if (IsContextLost()) return; 1849 if (!ValidateOrSkipForDelete(*this, obj)) return; 1850 const auto& state = State(); 1851 1852 if (obj->mActiveOrPaused) { 1853 EnqueueError(LOCAL_GL_INVALID_OPERATION, 1854 "Transform Feedback object still active or paused."); 1855 return; 1856 } 1857 1858 // Unbind 1859 if (state.mBoundTfo == obj) { 1860 BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr); 1861 } 1862 1863 obj->mDeleteRequested = true; 1864 Run<RPROC(DeleteTransformFeedback)>(obj->mId); 1865 } 1866 1867 void ClientWebGLContext::DeleteVertexArray(WebGLVertexArrayJS* const obj) { 1868 const FuncScope funcScope(*this, "deleteVertexArray"); 1869 if (IsContextLost()) return; 1870 if (!ValidateOrSkipForDelete(*this, obj)) return; 1871 const auto& state = State(); 1872 1873 // Unbind 1874 if (state.mBoundVao == obj) { 1875 BindVertexArray(nullptr); 1876 } 1877 1878 obj->mDeleteRequested = true; 1879 Run<RPROC(DeleteVertexArray)>(obj->mId); 1880 } 1881 1882 // - 1883 1884 bool ClientWebGLContext::IsBuffer(const WebGLBufferJS* const obj) const { 1885 const FuncScope funcScope(*this, "isBuffer"); 1886 if (IsContextLost()) return false; 1887 1888 return obj && obj->IsUsable(*this) && 1889 obj->mKind != webgl::BufferKind::Undefined; 1890 } 1891 1892 bool ClientWebGLContext::IsFramebuffer( 1893 const WebGLFramebufferJS* const obj) const { 1894 const FuncScope funcScope(*this, "isFramebuffer"); 1895 if (IsContextLost()) return false; 1896 1897 return obj && obj->IsUsable(*this) && obj->mHasBeenBound; 1898 } 1899 1900 bool ClientWebGLContext::IsProgram(const WebGLProgramJS* const obj) const { 1901 const FuncScope funcScope(*this, "isProgram"); 1902 if (IsContextLost()) return false; 1903 1904 return obj && obj->IsUsable(*this); 1905 } 1906 1907 bool ClientWebGLContext::IsQuery(const WebGLQueryJS* const obj) const { 1908 const FuncScope funcScope(*this, "isQuery"); 1909 if (IsContextLost()) return false; 1910 1911 return obj && obj->IsUsable(*this) && obj->mTarget; 1912 } 1913 1914 bool ClientWebGLContext::IsRenderbuffer( 1915 const WebGLRenderbufferJS* const obj) const { 1916 const FuncScope funcScope(*this, "isRenderbuffer"); 1917 if (IsContextLost()) return false; 1918 1919 return obj && obj->IsUsable(*this) && obj->mHasBeenBound; 1920 } 1921 1922 bool ClientWebGLContext::IsSampler(const WebGLSamplerJS* const obj) const { 1923 const FuncScope funcScope(*this, "isSampler"); 1924 if (IsContextLost()) return false; 1925 1926 return obj && obj->IsUsable(*this); 1927 } 1928 1929 bool ClientWebGLContext::IsShader(const WebGLShaderJS* const obj) const { 1930 const FuncScope funcScope(*this, "isShader"); 1931 if (IsContextLost()) return false; 1932 1933 return obj && obj->IsUsable(*this); 1934 } 1935 1936 bool ClientWebGLContext::IsSync(const WebGLSyncJS* const obj) const { 1937 const FuncScope funcScope(*this, "isSync"); 1938 if (IsContextLost()) return false; 1939 1940 return obj && obj->IsUsable(*this); 1941 } 1942 1943 bool ClientWebGLContext::IsTexture(const WebGLTextureJS* const obj) const { 1944 const FuncScope funcScope(*this, "isTexture"); 1945 if (IsContextLost()) return false; 1946 1947 return obj && obj->IsUsable(*this) && obj->mTarget; 1948 } 1949 1950 bool ClientWebGLContext::IsTransformFeedback( 1951 const WebGLTransformFeedbackJS* const obj) const { 1952 const FuncScope funcScope(*this, "isTransformFeedback"); 1953 if (IsContextLost()) return false; 1954 1955 return obj && obj->IsUsable(*this) && obj->mHasBeenBound; 1956 } 1957 1958 bool ClientWebGLContext::IsVertexArray( 1959 const WebGLVertexArrayJS* const obj) const { 1960 const FuncScope funcScope(*this, "isVertexArray"); 1961 if (IsContextLost()) return false; 1962 1963 return obj && obj->IsUsable(*this) && obj->mHasBeenBound; 1964 } 1965 1966 // ------------------------- GL State ------------------------- 1967 1968 void ClientWebGLContext::SetEnabledI(const GLenum cap, const Maybe<GLuint> i, 1969 const bool val) const { 1970 const FuncScope funcScope(*this, "enable/disable"); 1971 RefPtr<webgl::NotLostData> notLost(mNotLost); 1972 if (!notLost) { 1973 return; 1974 } 1975 1976 auto& map = notLost->state.mIsEnabledMap; 1977 auto slot = MaybeFind(map, cap); 1978 if (i && cap != LOCAL_GL_BLEND) { 1979 slot = nullptr; 1980 } 1981 if (!slot) { 1982 EnqueueError_ArgEnum("cap", cap); 1983 return; 1984 } 1985 1986 Run<RPROC(SetEnabled)>(cap, i, val); 1987 1988 if (!i || *i == 0) { 1989 *slot = val; 1990 } 1991 } 1992 1993 bool ClientWebGLContext::IsEnabled(const GLenum cap) const { 1994 const FuncScope funcScope(*this, "isEnabled"); 1995 RefPtr<webgl::NotLostData> notLost(mNotLost); 1996 if (!notLost) { 1997 return false; 1998 } 1999 2000 const auto& map = notLost->state.mIsEnabledMap; 2001 const auto slot = MaybeFind(map, cap); 2002 if (!slot) { 2003 EnqueueError_ArgEnum("cap", cap); 2004 return false; 2005 } 2006 2007 return *slot; 2008 } 2009 2010 template <typename T, typename S> 2011 static JS::Value Create(JSContext* cx, nsWrapperCache* creator, const S& src, 2012 ErrorResult& rv) { 2013 return JS::ObjectOrNullValue(T::Create(cx, creator, src, rv)); 2014 } 2015 2016 void ClientWebGLContext::GetInternalformatParameter( 2017 JSContext* cx, GLenum target, GLenum internalformat, GLenum pname, 2018 JS::MutableHandle<JS::Value> retval, ErrorResult& rv) { 2019 const FuncScope funcScope(*this, "getInternalformatParameter"); 2020 retval.set(JS::NullValue()); 2021 RefPtr<webgl::NotLostData> notLost(mNotLost); 2022 if (!notLost) { 2023 return; 2024 } 2025 const auto& inProcessContext = notLost->inProcess; 2026 Maybe<std::vector<int32_t>> maybe; 2027 if (inProcessContext) { 2028 maybe = inProcessContext->GetInternalformatParameter(target, internalformat, 2029 pname); 2030 } else { 2031 const auto& child = notLost->outOfProcess; 2032 child->FlushPendingCmds(); 2033 if (!child->SendGetInternalformatParameter(target, internalformat, pname, 2034 &maybe)) { 2035 return; 2036 } 2037 } 2038 2039 if (!maybe) { 2040 return; 2041 } 2042 2043 retval.set(Create<dom::Int32Array>(cx, this, *maybe, rv)); 2044 } 2045 2046 static JS::Value StringValue(JSContext* cx, const std::string& str, 2047 ErrorResult& er) { 2048 JSString* jsStr = JS_NewStringCopyN(cx, str.data(), str.size()); 2049 if (!jsStr) { 2050 er.Throw(NS_ERROR_OUT_OF_MEMORY); 2051 return JS::NullValue(); 2052 } 2053 2054 return JS::StringValue(jsStr); 2055 } 2056 2057 template <typename T> 2058 bool ToJSValueOrNull(JSContext* const cx, const RefPtr<T>& ptr, 2059 JS::MutableHandle<JS::Value> retval) { 2060 if (!ptr) { 2061 retval.set(JS::NullValue()); 2062 return true; 2063 } 2064 return dom::ToJSValue(cx, ptr, retval); 2065 } 2066 2067 Maybe<double> ClientWebGLContext::GetNumber(const GLenum pname) { 2068 RefPtr<webgl::NotLostData> notLost(mNotLost); 2069 if (!notLost) { 2070 return Nothing(); 2071 } 2072 2073 const auto& inProcess = notLost->inProcess; 2074 if (inProcess) { 2075 return inProcess->GetNumber(pname); 2076 } 2077 2078 const auto& child = notLost->outOfProcess; 2079 child->FlushPendingCmds(); 2080 2081 Maybe<double> ret; 2082 if (!child->SendGetNumber(pname, &ret)) { 2083 ret.reset(); 2084 } 2085 return ret; 2086 } 2087 2088 Maybe<std::string> ClientWebGLContext::GetString(const GLenum pname) { 2089 RefPtr<webgl::NotLostData> notLost(mNotLost); 2090 if (!notLost) { 2091 return Nothing(); 2092 } 2093 2094 const auto& inProcess = notLost->inProcess; 2095 if (inProcess) { 2096 return inProcess->GetString(pname); 2097 } 2098 2099 const auto& child = notLost->outOfProcess; 2100 child->FlushPendingCmds(); 2101 2102 Maybe<std::string> ret; 2103 if (!child->SendGetString(pname, &ret)) { 2104 ret.reset(); 2105 } 2106 return ret; 2107 } 2108 2109 void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, 2110 JS::MutableHandle<JS::Value> retval, 2111 ErrorResult& rv, const bool debug) { 2112 const FuncScope funcScope(*this, "getParameter"); 2113 retval.set(JS::NullValue()); 2114 RefPtr<webgl::NotLostData> notLost(mNotLost); 2115 if (!notLost) { 2116 return; 2117 } 2118 const auto& limits = Limits(); 2119 const auto& state = State(); 2120 2121 // - 2122 2123 const auto fnSetRetval_Buffer = [&](const GLenum target) { 2124 const auto buffer = *MaybeFind(state.mBoundBufferByTarget, target); 2125 (void)ToJSValueOrNull(cx, buffer, retval); 2126 }; 2127 const auto fnSetRetval_Tex = [&](const GLenum texTarget) { 2128 const auto& texUnit = state.mTexUnits[state.mActiveTexUnit]; 2129 const auto tex = Find(texUnit.texByTarget, texTarget, nullptr); 2130 (void)ToJSValueOrNull(cx, tex, retval); 2131 }; 2132 2133 switch (pname) { 2134 case LOCAL_GL_ARRAY_BUFFER_BINDING: 2135 fnSetRetval_Buffer(LOCAL_GL_ARRAY_BUFFER); 2136 return; 2137 2138 case LOCAL_GL_CURRENT_PROGRAM: 2139 (void)ToJSValueOrNull(cx, state.mCurrentProgram, retval); 2140 return; 2141 2142 case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: 2143 (void)ToJSValueOrNull(cx, state.mBoundVao->mIndexBuffer, retval); 2144 return; 2145 2146 case LOCAL_GL_FRAMEBUFFER_BINDING: 2147 (void)ToJSValueOrNull(cx, state.mBoundDrawFb, retval); 2148 return; 2149 2150 case LOCAL_GL_RENDERBUFFER_BINDING: 2151 (void)ToJSValueOrNull(cx, state.mBoundRb, retval); 2152 return; 2153 2154 case LOCAL_GL_TEXTURE_BINDING_2D: 2155 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D); 2156 return; 2157 2158 case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: 2159 fnSetRetval_Tex(LOCAL_GL_TEXTURE_CUBE_MAP); 2160 return; 2161 2162 case LOCAL_GL_VERTEX_ARRAY_BINDING: { 2163 if (!mIsWebGL2 && 2164 !IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) 2165 break; 2166 2167 auto ret = state.mBoundVao; 2168 if (ret == state.mDefaultVao) { 2169 ret = nullptr; 2170 } 2171 (void)ToJSValueOrNull(cx, ret, retval); 2172 return; 2173 } 2174 2175 case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: 2176 retval.set(JS::NumberValue(limits.maxTexUnits)); 2177 return; 2178 case LOCAL_GL_MAX_TEXTURE_SIZE: 2179 retval.set(JS::NumberValue(limits.maxTex2dSize)); 2180 return; 2181 case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: 2182 retval.set(JS::NumberValue(limits.maxTexCubeSize)); 2183 return; 2184 case LOCAL_GL_MAX_VERTEX_ATTRIBS: 2185 retval.set(JS::NumberValue(limits.maxVertexAttribs)); 2186 return; 2187 2188 case LOCAL_GL_MAX_VIEWS_OVR: 2189 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) { 2190 retval.set(JS::NumberValue(limits.maxMultiviewLayers)); 2191 return; 2192 } 2193 break; 2194 2195 case LOCAL_GL_PACK_ALIGNMENT: 2196 retval.set(JS::NumberValue(state.mPixelPackState.alignmentInTypeElems)); 2197 return; 2198 case LOCAL_GL_UNPACK_ALIGNMENT: 2199 retval.set(JS::NumberValue(state.mPixelUnpackState.alignmentInTypeElems)); 2200 return; 2201 2202 case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL: 2203 retval.set(JS::BooleanValue(state.mPixelUnpackState.flipY)); 2204 return; 2205 case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL: 2206 retval.set(JS::BooleanValue(state.mPixelUnpackState.premultiplyAlpha)); 2207 return; 2208 case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL: 2209 retval.set(JS::NumberValue(state.mPixelUnpackState.colorspaceConversion)); 2210 return; 2211 2212 case dom::WEBGL_provoking_vertex_Binding::PROVOKING_VERTEX_WEBGL: 2213 if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_provoking_vertex)) break; 2214 retval.set(JS::NumberValue(UnderlyingValue(state.mProvokingVertex))); 2215 return; 2216 2217 case LOCAL_GL_DEPTH_CLAMP: 2218 if (!IsExtensionEnabled(WebGLExtensionID::EXT_depth_clamp)) break; 2219 retval.set(JS::BooleanValue(state.mIsEnabledMap[LOCAL_GL_DEPTH_CLAMP])); 2220 return; 2221 2222 // - 2223 // Array returns 2224 2225 // 2 floats 2226 case LOCAL_GL_DEPTH_RANGE: 2227 retval.set(Create<dom::Float32Array>(cx, this, state.mDepthRange, rv)); 2228 return; 2229 2230 case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: 2231 retval.set( 2232 Create<dom::Float32Array>(cx, this, limits.pointSizeRange, rv)); 2233 return; 2234 2235 case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: 2236 retval.set( 2237 Create<dom::Float32Array>(cx, this, limits.lineWidthRange, rv)); 2238 return; 2239 2240 // 4 floats 2241 case LOCAL_GL_COLOR_CLEAR_VALUE: 2242 retval.set(Create<dom::Float32Array>(cx, this, state.mClearColor, rv)); 2243 return; 2244 2245 case LOCAL_GL_BLEND_COLOR: 2246 retval.set(Create<dom::Float32Array>(cx, this, state.mBlendColor, rv)); 2247 return; 2248 2249 // 2 ints 2250 case LOCAL_GL_MAX_VIEWPORT_DIMS: { 2251 auto maxViewportDim = BitwiseCast<int32_t>(limits.maxViewportDim); 2252 const auto dims = std::array<int32_t, 2>{maxViewportDim, maxViewportDim}; 2253 retval.set(Create<dom::Int32Array>(cx, this, dims, rv)); 2254 return; 2255 } 2256 2257 // 4 ints 2258 case LOCAL_GL_SCISSOR_BOX: 2259 retval.set(Create<dom::Int32Array>(cx, this, state.mScissor, rv)); 2260 return; 2261 2262 case LOCAL_GL_VIEWPORT: 2263 retval.set(Create<dom::Int32Array>(cx, this, state.mViewport, rv)); 2264 return; 2265 2266 // any 2267 case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: 2268 retval.set(Create<dom::Uint32Array>(cx, this, 2269 state.mCompressedTextureFormats, rv)); 2270 return; 2271 } 2272 2273 if (mIsWebGL2) { 2274 switch (pname) { 2275 case LOCAL_GL_COPY_READ_BUFFER_BINDING: 2276 fnSetRetval_Buffer(LOCAL_GL_COPY_READ_BUFFER); 2277 return; 2278 2279 case LOCAL_GL_COPY_WRITE_BUFFER_BINDING: 2280 fnSetRetval_Buffer(LOCAL_GL_COPY_WRITE_BUFFER); 2281 return; 2282 2283 case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING: 2284 (void)ToJSValueOrNull(cx, state.mBoundDrawFb, retval); 2285 return; 2286 2287 case LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL: 2288 retval.set(JS::NumberValue(webgl::kMaxClientWaitSyncTimeoutNS)); 2289 return; 2290 2291 case LOCAL_GL_PIXEL_PACK_BUFFER_BINDING: 2292 fnSetRetval_Buffer(LOCAL_GL_PIXEL_PACK_BUFFER); 2293 return; 2294 2295 case LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING: 2296 fnSetRetval_Buffer(LOCAL_GL_PIXEL_UNPACK_BUFFER); 2297 return; 2298 2299 case LOCAL_GL_READ_FRAMEBUFFER_BINDING: 2300 (void)ToJSValueOrNull(cx, state.mBoundReadFb, retval); 2301 return; 2302 2303 case LOCAL_GL_SAMPLER_BINDING: { 2304 const auto& texUnit = state.mTexUnits[state.mActiveTexUnit]; 2305 (void)ToJSValueOrNull(cx, texUnit.sampler, retval); 2306 return; 2307 } 2308 2309 case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY: 2310 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D_ARRAY); 2311 return; 2312 2313 case LOCAL_GL_TEXTURE_BINDING_3D: 2314 fnSetRetval_Tex(LOCAL_GL_TEXTURE_3D); 2315 return; 2316 2317 case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING: { 2318 auto ret = state.mBoundTfo; 2319 if (ret == state.mDefaultTfo) { 2320 ret = nullptr; 2321 } 2322 (void)ToJSValueOrNull(cx, ret, retval); 2323 return; 2324 } 2325 2326 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: 2327 fnSetRetval_Buffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER); 2328 return; 2329 2330 case LOCAL_GL_UNIFORM_BUFFER_BINDING: 2331 fnSetRetval_Buffer(LOCAL_GL_UNIFORM_BUFFER); 2332 return; 2333 2334 case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: 2335 retval.set( 2336 JS::NumberValue(webgl::kMaxTransformFeedbackSeparateAttribs)); 2337 return; 2338 case LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS: 2339 retval.set(JS::NumberValue(limits.maxUniformBufferBindings)); 2340 return; 2341 case LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: 2342 retval.set(JS::NumberValue(limits.uniformBufferOffsetAlignment)); 2343 return; 2344 case LOCAL_GL_MAX_3D_TEXTURE_SIZE: 2345 retval.set(JS::NumberValue(limits.maxTex3dSize)); 2346 return; 2347 case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS: 2348 retval.set(JS::NumberValue(limits.maxTexArrayLayers)); 2349 return; 2350 2351 case LOCAL_GL_PACK_ROW_LENGTH: 2352 retval.set(JS::NumberValue(state.mPixelPackState.rowLength)); 2353 return; 2354 case LOCAL_GL_PACK_SKIP_PIXELS: 2355 retval.set(JS::NumberValue(state.mPixelPackState.skipPixels)); 2356 return; 2357 case LOCAL_GL_PACK_SKIP_ROWS: 2358 retval.set(JS::NumberValue(state.mPixelPackState.skipRows)); 2359 return; 2360 2361 case LOCAL_GL_UNPACK_IMAGE_HEIGHT: 2362 retval.set(JS::NumberValue(state.mPixelUnpackState.imageHeight)); 2363 return; 2364 case LOCAL_GL_UNPACK_ROW_LENGTH: 2365 retval.set(JS::NumberValue(state.mPixelUnpackState.rowLength)); 2366 return; 2367 case LOCAL_GL_UNPACK_SKIP_IMAGES: 2368 retval.set(JS::NumberValue(state.mPixelUnpackState.skipImages)); 2369 return; 2370 case LOCAL_GL_UNPACK_SKIP_PIXELS: 2371 retval.set(JS::NumberValue(state.mPixelUnpackState.skipPixels)); 2372 return; 2373 case LOCAL_GL_UNPACK_SKIP_ROWS: 2374 retval.set(JS::NumberValue(state.mPixelUnpackState.skipRows)); 2375 return; 2376 } // switch pname 2377 } // if webgl2 2378 2379 // - 2380 2381 if (!debug) { 2382 const auto GetUnmaskedRenderer = [&]() { 2383 const auto prefLock = StaticPrefs::webgl_override_unmasked_renderer(); 2384 if (!prefLock->IsEmpty()) { 2385 return Some(ToString(*prefLock)); 2386 } 2387 return GetString(LOCAL_GL_RENDERER); 2388 }; 2389 2390 const auto GetUnmaskedVendor = [&]() { 2391 const auto prefLock = StaticPrefs::webgl_override_unmasked_vendor(); 2392 if (!prefLock->IsEmpty()) { 2393 return Some(ToString(*prefLock)); 2394 } 2395 return GetString(LOCAL_GL_VENDOR); 2396 }; 2397 2398 // - 2399 2400 Maybe<std::string> ret; 2401 2402 switch (pname) { 2403 case LOCAL_GL_VENDOR: 2404 ret = Some(std::string{"Mozilla"}); 2405 break; 2406 2407 case LOCAL_GL_RENDERER: { 2408 bool allowRenderer = StaticPrefs::webgl_enable_renderer_query(); 2409 if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo) || 2410 ShouldResistFingerprinting(RFPTarget::WebGLRendererConstant)) { 2411 allowRenderer = false; 2412 } 2413 if (allowRenderer) { 2414 ret = GetUnmaskedRenderer(); 2415 if (ret) { 2416 ret = Some(webgl::SanitizeRenderer(*ret)); 2417 } 2418 } 2419 if (!ret) { 2420 ret = Some(std::string{"Mozilla"}); 2421 } 2422 break; 2423 } 2424 2425 case LOCAL_GL_VERSION: 2426 if (mIsWebGL2) { 2427 ret = Some(std::string{"WebGL 2.0"}); 2428 } else { 2429 ret = Some(std::string{"WebGL 1.0"}); 2430 } 2431 break; 2432 2433 case LOCAL_GL_SHADING_LANGUAGE_VERSION: 2434 if (mIsWebGL2) { 2435 ret = Some(std::string{"WebGL GLSL ES 3.00"}); 2436 } else { 2437 ret = Some(std::string{"WebGL GLSL ES 1.0"}); 2438 } 2439 break; 2440 2441 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL: 2442 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: { 2443 if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) { 2444 EnqueueError_ArgEnum("pname", pname); 2445 return; 2446 } 2447 2448 switch (pname) { 2449 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: 2450 if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo) || 2451 ShouldResistFingerprinting(RFPTarget::WebGLRendererConstant)) { 2452 ret = Some("Mozilla"_ns); 2453 } else { 2454 ret = GetUnmaskedRenderer(); 2455 if (ret && StaticPrefs::webgl_sanitize_unmasked_renderer()) { 2456 ret = Some(webgl::SanitizeRenderer(*ret)); 2457 } 2458 } 2459 break; 2460 2461 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL: 2462 if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo)) { 2463 ret = Some("Mozilla"_ns); 2464 } else if (ShouldResistFingerprinting( 2465 RFPTarget::WebGLVendorRandomize)) { 2466 // Generate "Mozilla <Base64(uint64)>" 2467 auto randomValue = RandomUint64(); 2468 if (randomValue.isSome()) { 2469 uint64_t value = randomValue.value(); 2470 nsCString base64; 2471 nsresult rv = 2472 Base64Encode(reinterpret_cast<const char*>(&value), 2473 sizeof(value), base64); 2474 if (NS_SUCCEEDED(rv)) { 2475 ret = Some(std::string("Mozilla ") + base64.get()); 2476 } else { 2477 ret = Some("Mozilla"_ns); 2478 } 2479 } else { 2480 ret = Some("Mozilla"_ns); 2481 } 2482 } else if (ShouldResistFingerprinting( 2483 RFPTarget::WebGLVendorConstant)) { 2484 ret = Some("Mozilla"_ns); 2485 } else { 2486 ret = GetUnmaskedVendor(); 2487 if (ret && 2488 ShouldResistFingerprinting(RFPTarget::WebGLVendorSanitize)) { 2489 ret = Some(webgl::SanitizeVendor(*ret)); 2490 } 2491 } 2492 break; 2493 2494 default: 2495 MOZ_CRASH(); 2496 } 2497 break; 2498 } 2499 2500 default: 2501 break; 2502 } 2503 2504 if (ret) { 2505 retval.set(StringValue(cx, *ret, rv)); 2506 return; 2507 } 2508 } // if (!debug) 2509 2510 // - 2511 2512 bool debugOnly = false; 2513 bool asString = false; 2514 2515 switch (pname) { 2516 case LOCAL_GL_EXTENSIONS: 2517 case LOCAL_GL_RENDERER: 2518 case LOCAL_GL_VENDOR: 2519 case LOCAL_GL_VERSION: 2520 case dom::MOZ_debug_Binding::CONTEXT_TYPE: 2521 case dom::MOZ_debug_Binding::WSI_INFO: 2522 debugOnly = true; 2523 asString = true; 2524 break; 2525 2526 case dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION: 2527 debugOnly = true; 2528 break; 2529 2530 default: 2531 break; 2532 } 2533 2534 if (debugOnly && !debug) { 2535 EnqueueError_ArgEnum("pname", pname); 2536 return; 2537 } 2538 2539 // - 2540 2541 if (asString) { 2542 const auto maybe = GetString(pname); 2543 if (maybe) { 2544 auto str = std::string{}; 2545 if (pname == dom::MOZ_debug_Binding::WSI_INFO) { 2546 const auto& outOfProcess = notLost->outOfProcess; 2547 const auto& inProcess = notLost->inProcess; 2548 str += PrintfStdString("outOfProcess: %s\ninProcess: %s\n", 2549 ToChars(bool(outOfProcess)), 2550 ToChars(bool(inProcess))); 2551 } 2552 str += *maybe; 2553 retval.set(StringValue(cx, str.c_str(), rv)); 2554 } 2555 } else { 2556 const auto maybe = GetNumber(pname); 2557 if (maybe) { 2558 switch (pname) { 2559 // WebGL 1: 2560 case LOCAL_GL_BLEND: 2561 case LOCAL_GL_CULL_FACE: 2562 case LOCAL_GL_DEPTH_TEST: 2563 case LOCAL_GL_DEPTH_WRITEMASK: 2564 case LOCAL_GL_DITHER: 2565 case LOCAL_GL_POLYGON_OFFSET_FILL: 2566 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE: 2567 case LOCAL_GL_SAMPLE_COVERAGE: 2568 case LOCAL_GL_SAMPLE_COVERAGE_INVERT: 2569 case LOCAL_GL_SCISSOR_TEST: 2570 case LOCAL_GL_STENCIL_TEST: 2571 // WebGL 2: 2572 case LOCAL_GL_RASTERIZER_DISCARD: 2573 case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE: 2574 case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED: 2575 retval.set(JS::BooleanValue(*maybe)); 2576 return; 2577 2578 // 4 bools 2579 case LOCAL_GL_COLOR_WRITEMASK: { 2580 const auto mask = uint8_t(*maybe); 2581 const auto bs = std::bitset<4>(mask); 2582 const auto src = std::array<bool, 4>{bs[0], bs[1], bs[2], bs[3]}; 2583 JS::Rooted<JS::Value> arr(cx); 2584 if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) { 2585 rv = NS_ERROR_OUT_OF_MEMORY; 2586 } 2587 retval.set(arr); 2588 return; 2589 } 2590 2591 case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: { 2592 auto readType = (GLenum)*maybe; 2593 // Map HALF_FLOAT to HALF_FLOAT_OES for webgl 1 clients. 2594 if (readType == LOCAL_GL_HALF_FLOAT && !mIsWebGL2) { 2595 readType = LOCAL_GL_HALF_FLOAT_OES; 2596 } 2597 retval.set(JS::NumberValue(readType)); 2598 return; 2599 } 2600 2601 default: 2602 retval.set(JS::NumberValue(*maybe)); 2603 return; 2604 } 2605 } 2606 } 2607 } 2608 2609 void ClientWebGLContext::GetBufferParameter( 2610 JSContext* cx, GLenum target, GLenum pname, 2611 JS::MutableHandle<JS::Value> retval) const { 2612 retval.set(JS::NullValue()); 2613 RefPtr<webgl::NotLostData> notLost(mNotLost); 2614 if (!notLost) { 2615 return; 2616 } 2617 2618 const auto maybe = [&]() { 2619 const auto& inProcess = notLost->inProcess; 2620 if (inProcess) { 2621 return inProcess->GetBufferParameter(target, pname); 2622 } 2623 const auto& child = notLost->outOfProcess; 2624 child->FlushPendingCmds(); 2625 Maybe<double> ret; 2626 if (!child->SendGetBufferParameter(target, pname, &ret)) { 2627 ret.reset(); 2628 } 2629 return ret; 2630 }(); 2631 if (maybe) { 2632 retval.set(JS::NumberValue(*maybe)); 2633 } 2634 } 2635 2636 bool IsFramebufferTarget(const bool isWebgl2, const GLenum target) { 2637 switch (target) { 2638 case LOCAL_GL_FRAMEBUFFER: 2639 return true; 2640 2641 case LOCAL_GL_DRAW_FRAMEBUFFER: 2642 case LOCAL_GL_READ_FRAMEBUFFER: 2643 return isWebgl2; 2644 2645 default: 2646 return false; 2647 } 2648 } 2649 2650 void ClientWebGLContext::GetFramebufferAttachmentParameter( 2651 JSContext* const cx, const GLenum target, const GLenum attachment, 2652 const GLenum pname, JS::MutableHandle<JS::Value> retval, 2653 ErrorResult& rv) const { 2654 const FuncScope funcScope(*this, "getFramebufferAttachmentParameter"); 2655 retval.set(JS::NullValue()); 2656 RefPtr<webgl::NotLostData> notLost(mNotLost); 2657 if (!notLost) { 2658 return; 2659 } 2660 2661 const auto& state = State(); 2662 2663 if (!IsFramebufferTarget(mIsWebGL2, target)) { 2664 EnqueueError_ArgEnum("target", target); 2665 return; 2666 } 2667 auto fb = state.mBoundDrawFb; 2668 if (target == LOCAL_GL_READ_FRAMEBUFFER) { 2669 fb = state.mBoundReadFb; 2670 } 2671 2672 const auto fnGet = [&](const GLenum pname) { 2673 const auto fbId = fb ? fb->mId : 0; 2674 2675 const auto& inProcess = notLost->inProcess; 2676 if (inProcess) { 2677 return inProcess->GetFramebufferAttachmentParameter(fbId, attachment, 2678 pname); 2679 } 2680 const auto& child = notLost->outOfProcess; 2681 child->FlushPendingCmds(); 2682 Maybe<double> ret; 2683 if (!child->SendGetFramebufferAttachmentParameter(fbId, attachment, pname, 2684 &ret)) { 2685 ret.reset(); 2686 } 2687 return ret; 2688 }; 2689 2690 if (fb) { 2691 if (fb->mOpaque) { 2692 EnqueueError(LOCAL_GL_INVALID_OPERATION, 2693 "An opaque framebuffer's attachments cannot be inspected or " 2694 "changed."); 2695 return; 2696 } 2697 auto attachmentSlotEnum = attachment; 2698 if (mIsWebGL2 && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { 2699 // In webgl2, DEPTH_STENCIL is valid iff the DEPTH and STENCIL images 2700 // match, so check if the server errors. 2701 const auto maybe = fnGet(LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); 2702 if (!maybe) return; 2703 attachmentSlotEnum = LOCAL_GL_DEPTH_ATTACHMENT; 2704 } 2705 2706 const auto maybeSlot = fb->GetAttachment(attachmentSlotEnum); 2707 if (!maybeSlot) { 2708 EnqueueError_ArgEnum("attachment", attachment); 2709 return; 2710 } 2711 const auto& attached = *maybeSlot; 2712 2713 // - 2714 2715 if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { 2716 if (attached.rb) { 2717 (void)ToJSValueOrNull(cx, attached.rb, retval); 2718 } else { 2719 if (!mIsWebGL2 && !attached.tex) { 2720 EnqueueError_ArgEnum("pname", pname); 2721 return; 2722 } 2723 (void)ToJSValueOrNull(cx, attached.tex, retval); 2724 } 2725 return; 2726 } 2727 } 2728 2729 const auto maybe = fnGet(pname); 2730 if (maybe) { 2731 retval.set(JS::NumberValue(*maybe)); 2732 } 2733 } 2734 2735 void ClientWebGLContext::GetRenderbufferParameter( 2736 JSContext* cx, GLenum target, GLenum pname, 2737 JS::MutableHandle<JS::Value> retval) const { 2738 const FuncScope funcScope(*this, "getRenderbufferParameter"); 2739 retval.set(JS::NullValue()); 2740 RefPtr<webgl::NotLostData> notLost(mNotLost); 2741 if (!notLost) { 2742 return; 2743 } 2744 2745 if (target != LOCAL_GL_RENDERBUFFER) { 2746 EnqueueError_ArgEnum("target", target); 2747 return; 2748 } 2749 2750 const auto& state = State(); 2751 const auto& rb = state.mBoundRb; 2752 const auto rbId = rb ? rb->mId : 0; 2753 const auto maybe = [&]() { 2754 const auto& inProcess = notLost->inProcess; 2755 if (inProcess) { 2756 return inProcess->GetRenderbufferParameter(rbId, pname); 2757 } 2758 const auto& child = notLost->outOfProcess; 2759 child->FlushPendingCmds(); 2760 Maybe<double> ret; 2761 if (!child->SendGetRenderbufferParameter(rbId, pname, &ret)) { 2762 ret.reset(); 2763 } 2764 return ret; 2765 }(); 2766 if (maybe) { 2767 retval.set(JS::NumberValue(*maybe)); 2768 } 2769 } 2770 2771 void ClientWebGLContext::GetIndexedParameter( 2772 JSContext* cx, GLenum target, GLuint index, 2773 JS::MutableHandle<JS::Value> retval, ErrorResult& rv) const { 2774 const FuncScope funcScope(*this, "getIndexedParameter"); 2775 retval.set(JS::NullValue()); 2776 RefPtr<webgl::NotLostData> notLost(mNotLost); 2777 if (!notLost) { 2778 return; 2779 } 2780 2781 const auto& state = State(); 2782 2783 switch (target) { 2784 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: { 2785 const auto& list = state.mBoundTfo->mAttribBuffers; 2786 if (index >= list.size()) { 2787 EnqueueError(LOCAL_GL_INVALID_VALUE, 2788 "`index` (%u) >= MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", 2789 index); 2790 return; 2791 } 2792 (void)ToJSValueOrNull(cx, list[index], retval); 2793 return; 2794 } 2795 2796 case LOCAL_GL_UNIFORM_BUFFER_BINDING: { 2797 const auto& list = state.mBoundUbos; 2798 if (index >= list.size()) { 2799 EnqueueError(LOCAL_GL_INVALID_VALUE, 2800 "`index` (%u) >= MAX_UNIFORM_BUFFER_BINDINGS", index); 2801 return; 2802 } 2803 (void)ToJSValueOrNull(cx, list[index], retval); 2804 return; 2805 } 2806 } 2807 2808 const auto maybe = [&]() { 2809 const auto& inProcess = notLost->inProcess; 2810 if (inProcess) { 2811 return inProcess->GetIndexedParameter(target, index); 2812 } 2813 const auto& child = notLost->outOfProcess; 2814 child->FlushPendingCmds(); 2815 Maybe<double> ret; 2816 if (!child->SendGetIndexedParameter(target, index, &ret)) { 2817 ret.reset(); 2818 } 2819 return ret; 2820 }(); 2821 if (maybe) { 2822 switch (target) { 2823 case LOCAL_GL_COLOR_WRITEMASK: { 2824 const auto bs = std::bitset<4>(*maybe); 2825 const auto src = std::array<bool, 4>{bs[0], bs[1], bs[2], bs[3]}; 2826 JS::Rooted<JS::Value> arr(cx); 2827 if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) { 2828 rv = NS_ERROR_OUT_OF_MEMORY; 2829 } 2830 retval.set(arr); 2831 return; 2832 } 2833 2834 default: 2835 retval.set(JS::NumberValue(*maybe)); 2836 return; 2837 } 2838 } 2839 } 2840 2841 void ClientWebGLContext::GetUniform(JSContext* const cx, 2842 const WebGLProgramJS& prog, 2843 const WebGLUniformLocationJS& loc, 2844 JS::MutableHandle<JS::Value> retval) { 2845 const FuncScope funcScope(*this, "getUniform"); 2846 retval.set(JS::NullValue()); 2847 RefPtr<webgl::NotLostData> notLost(mNotLost); 2848 if (!notLost) { 2849 return; 2850 } 2851 2852 if (!prog.ValidateUsable(*this, "prog")) return; 2853 if (!loc.ValidateUsable(*this, "loc")) return; 2854 2855 const auto& progLinkResult = GetLinkResult(prog); 2856 if (!progLinkResult.success) { 2857 EnqueueError(LOCAL_GL_INVALID_OPERATION, "Program is not linked."); 2858 return; 2859 } 2860 2861 if (loc.mParent != &progLinkResult) { 2862 EnqueueError( 2863 LOCAL_GL_INVALID_OPERATION, 2864 "UniformLocation is not from the most recent linking of Program."); 2865 return; 2866 } 2867 2868 const auto res = [&]() { 2869 const auto& inProcess = notLost->inProcess; 2870 if (inProcess) { 2871 return inProcess->GetUniform(prog.mId, loc.mLocation); 2872 } 2873 const auto& child = notLost->outOfProcess; 2874 child->FlushPendingCmds(); 2875 webgl::GetUniformData ret; 2876 if (!child->SendGetUniform(prog.mId, loc.mLocation, &ret)) { 2877 ret = {}; 2878 } 2879 return ret; 2880 }(); 2881 if (!res.type) return; 2882 2883 const auto elemCount = ElemTypeComponents(res.type); 2884 MOZ_ASSERT(elemCount); 2885 2886 switch (res.type) { 2887 case LOCAL_GL_BOOL: 2888 retval.set(JS::BooleanValue(res.data[0])); 2889 return; 2890 2891 case LOCAL_GL_FLOAT: { 2892 const auto ptr = reinterpret_cast<const float*>(res.data); 2893 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, *ptr, retval)); 2894 return; 2895 } 2896 case LOCAL_GL_INT: { 2897 const auto ptr = reinterpret_cast<const int32_t*>(res.data); 2898 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, *ptr, retval)); 2899 return; 2900 } 2901 case LOCAL_GL_UNSIGNED_INT: 2902 case LOCAL_GL_SAMPLER_2D: 2903 case LOCAL_GL_SAMPLER_3D: 2904 case LOCAL_GL_SAMPLER_CUBE: 2905 case LOCAL_GL_SAMPLER_2D_SHADOW: 2906 case LOCAL_GL_SAMPLER_2D_ARRAY: 2907 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 2908 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 2909 case LOCAL_GL_INT_SAMPLER_2D: 2910 case LOCAL_GL_INT_SAMPLER_3D: 2911 case LOCAL_GL_INT_SAMPLER_CUBE: 2912 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 2913 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 2914 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 2915 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 2916 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: { 2917 const auto ptr = reinterpret_cast<const uint32_t*>(res.data); 2918 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, *ptr, retval)); 2919 return; 2920 } 2921 2922 // - 2923 2924 case LOCAL_GL_BOOL_VEC2: 2925 case LOCAL_GL_BOOL_VEC3: 2926 case LOCAL_GL_BOOL_VEC4: { 2927 const auto intArr = reinterpret_cast<const int32_t*>(res.data); 2928 bool boolArr[4] = {}; 2929 for (const auto i : IntegerRange(elemCount)) { 2930 boolArr[i] = bool(intArr[i]); 2931 } 2932 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, boolArr, elemCount, retval)); 2933 return; 2934 } 2935 2936 case LOCAL_GL_FLOAT_VEC2: 2937 case LOCAL_GL_FLOAT_VEC3: 2938 case LOCAL_GL_FLOAT_VEC4: 2939 case LOCAL_GL_FLOAT_MAT2: 2940 case LOCAL_GL_FLOAT_MAT3: 2941 case LOCAL_GL_FLOAT_MAT4: 2942 case LOCAL_GL_FLOAT_MAT2x3: 2943 case LOCAL_GL_FLOAT_MAT2x4: 2944 case LOCAL_GL_FLOAT_MAT3x2: 2945 case LOCAL_GL_FLOAT_MAT3x4: 2946 case LOCAL_GL_FLOAT_MAT4x2: 2947 case LOCAL_GL_FLOAT_MAT4x3: { 2948 const auto ptr = reinterpret_cast<const float*>(res.data); 2949 IgnoredErrorResult error; 2950 JSObject* obj = 2951 dom::Float32Array::Create(cx, this, Span(ptr, elemCount), error); 2952 MOZ_ASSERT(obj); 2953 retval.set(JS::ObjectOrNullValue(obj)); 2954 return; 2955 } 2956 2957 case LOCAL_GL_INT_VEC2: 2958 case LOCAL_GL_INT_VEC3: 2959 case LOCAL_GL_INT_VEC4: { 2960 const auto ptr = reinterpret_cast<const int32_t*>(res.data); 2961 IgnoredErrorResult error; 2962 JSObject* obj = 2963 dom::Int32Array::Create(cx, this, Span(ptr, elemCount), error); 2964 MOZ_ASSERT(obj); 2965 retval.set(JS::ObjectOrNullValue(obj)); 2966 return; 2967 } 2968 2969 case LOCAL_GL_UNSIGNED_INT_VEC2: 2970 case LOCAL_GL_UNSIGNED_INT_VEC3: 2971 case LOCAL_GL_UNSIGNED_INT_VEC4: { 2972 const auto ptr = reinterpret_cast<const uint32_t*>(res.data); 2973 IgnoredErrorResult error; 2974 JSObject* obj = 2975 dom::Uint32Array::Create(cx, this, Span(ptr, elemCount), error); 2976 MOZ_ASSERT(obj); 2977 retval.set(JS::ObjectOrNullValue(obj)); 2978 return; 2979 } 2980 2981 default: 2982 MOZ_CRASH("GFX: Invalid elemType."); 2983 } 2984 } 2985 2986 already_AddRefed<WebGLShaderPrecisionFormatJS> 2987 ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype, 2988 const GLenum precisiontype) { 2989 RefPtr<webgl::NotLostData> notLost(mNotLost); 2990 if (!notLost) { 2991 return nullptr; 2992 } 2993 2994 const FuncScope funcScope(*this, "getShaderPrecisionFormat"); 2995 if (IsContextLost()) return nullptr; 2996 2997 const auto& shaderPrecisions = *notLost->info.shaderPrecisions; 2998 const auto args = 2999 webgl::GetShaderPrecisionFormatArgs{shadertype, precisiontype}; 3000 const auto found = MaybeFind(shaderPrecisions, args); 3001 if (!found) { 3002 EnqueueError( 3003 LOCAL_GL_INVALID_ENUM, "Bad shaderType (%s) or precisionType (%s)", 3004 EnumString(shadertype).c_str(), EnumString(precisiontype).c_str()); 3005 return nullptr; 3006 } 3007 3008 return AsAddRefed(new WebGLShaderPrecisionFormatJS(*found)); 3009 } 3010 3011 void ClientWebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, 3012 GLclampf a) { 3013 const FuncScope funcScope(*this, "blendColor"); 3014 if (IsContextLost()) return; 3015 auto& state = State(); 3016 3017 const bool unclamped = 3018 (mIsWebGL2 || 3019 IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float) || 3020 IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)); 3021 if (!unclamped) { 3022 r = std::clamp(r, 0.0f, 1.0f); 3023 g = std::clamp(g, 0.0f, 1.0f); 3024 b = std::clamp(b, 0.0f, 1.0f); 3025 a = std::clamp(a, 0.0f, 1.0f); 3026 } 3027 3028 auto& cache = state.mBlendColor; 3029 cache[0] = r; 3030 cache[1] = g; 3031 cache[2] = b; 3032 cache[3] = a; 3033 3034 Run<RPROC(BlendColor)>(r, g, b, a); 3035 } 3036 3037 void ClientWebGLContext::BlendEquationSeparateI(Maybe<GLuint> i, GLenum modeRGB, 3038 GLenum modeAlpha) { 3039 Run<RPROC(BlendEquationSeparate)>(i, modeRGB, modeAlpha); 3040 } 3041 3042 void ClientWebGLContext::BlendFuncSeparateI(Maybe<GLuint> i, GLenum srcRGB, 3043 GLenum dstRGB, GLenum srcAlpha, 3044 GLenum dstAlpha) { 3045 Run<RPROC(BlendFuncSeparate)>(i, srcRGB, dstRGB, srcAlpha, dstAlpha); 3046 } 3047 3048 GLenum ClientWebGLContext::CheckFramebufferStatus(GLenum target) { 3049 RefPtr<webgl::NotLostData> notLost(mNotLost); 3050 if (!notLost) { 3051 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; 3052 } 3053 3054 const auto& inProcess = notLost->inProcess; 3055 if (inProcess) { 3056 return inProcess->CheckFramebufferStatus(target); 3057 } 3058 const auto& child = notLost->outOfProcess; 3059 child->FlushPendingCmds(); 3060 GLenum ret = 0; 3061 if (!child->SendCheckFramebufferStatus(target, &ret)) { 3062 ret = 0; 3063 } 3064 return ret; 3065 } 3066 3067 void ClientWebGLContext::Clear(GLbitfield mask) { 3068 Run<RPROC(Clear)>(mask); 3069 3070 AfterDrawCall(); 3071 } 3072 3073 // - 3074 3075 void ClientWebGLContext::ClearBufferTv(const GLenum buffer, 3076 const GLint drawBuffer, 3077 const webgl::AttribBaseType type, 3078 JS::AutoCheckCannotGC&& nogc, 3079 const Span<const uint8_t>& view, 3080 const GLuint srcElemOffset) { 3081 if (IsContextLost()) return; 3082 3083 const auto byteOffset = CheckedInt<size_t>(srcElemOffset) * sizeof(float); 3084 if (!byteOffset.isValid() || byteOffset.value() > view.Length()) { 3085 nogc.reset(); 3086 EnqueueError(LOCAL_GL_INVALID_VALUE, "`srcOffset` too large for `values`."); 3087 return; 3088 } 3089 webgl::TypedQuad data; 3090 data.type = type; 3091 3092 auto dataSize = data.data.size(); 3093 switch (buffer) { 3094 case LOCAL_GL_COLOR: 3095 break; 3096 3097 case LOCAL_GL_DEPTH: 3098 dataSize = sizeof(float); 3099 break; 3100 3101 case LOCAL_GL_STENCIL: 3102 dataSize = sizeof(int32_t); 3103 break; 3104 3105 default: 3106 nogc.reset(); 3107 EnqueueError_ArgEnum("buffer", buffer); 3108 return; 3109 } 3110 3111 const auto requiredBytes = byteOffset + dataSize; 3112 if (!requiredBytes.isValid() || requiredBytes.value() > view.Length()) { 3113 nogc.reset(); 3114 EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small."); 3115 return; 3116 } 3117 3118 memcpy(data.data.data(), view.data() + byteOffset.value(), dataSize); 3119 nogc.reset(); // Done with `view`. 3120 Run<RPROC(ClearBufferTv)>(buffer, drawBuffer, data); 3121 3122 AfterDrawCall(); 3123 } 3124 3125 void ClientWebGLContext::ClearBufferfi(GLenum buffer, GLint drawBuffer, 3126 GLfloat depth, GLint stencil) { 3127 Run<RPROC(ClearBufferfi)>(buffer, drawBuffer, depth, stencil); 3128 3129 AfterDrawCall(); 3130 } 3131 3132 // - 3133 3134 void ClientWebGLContext::ClearColor(GLclampf r, GLclampf g, GLclampf b, 3135 GLclampf a) { 3136 const FuncScope funcScope(*this, "clearColor"); 3137 if (IsContextLost()) return; 3138 auto& state = State(); 3139 3140 auto& cache = state.mClearColor; 3141 cache[0] = r; 3142 cache[1] = g; 3143 cache[2] = b; 3144 cache[3] = a; 3145 3146 Run<RPROC(ClearColor)>(r, g, b, a); 3147 } 3148 3149 void ClientWebGLContext::ClearDepth(GLclampf v) { Run<RPROC(ClearDepth)>(v); } 3150 3151 void ClientWebGLContext::ClearStencil(GLint v) { Run<RPROC(ClearStencil)>(v); } 3152 3153 void ClientWebGLContext::ColorMaskI(Maybe<GLuint> i, bool r, bool g, bool b, 3154 bool a) const { 3155 const FuncScope funcScope(*this, "colorMask"); 3156 if (IsContextLost()) return; 3157 3158 const uint8_t mask = 3159 uint8_t(r << 0) | uint8_t(g << 1) | uint8_t(b << 2) | uint8_t(a << 3); 3160 Run<RPROC(ColorMask)>(i, mask); 3161 } 3162 3163 void ClientWebGLContext::CullFace(GLenum face) { Run<RPROC(CullFace)>(face); } 3164 3165 void ClientWebGLContext::DepthFunc(GLenum func) { Run<RPROC(DepthFunc)>(func); } 3166 3167 void ClientWebGLContext::DepthMask(WebGLboolean b) { Run<RPROC(DepthMask)>(b); } 3168 3169 void ClientWebGLContext::DepthRange(GLclampf zNear, GLclampf zFar) { 3170 const FuncScope funcScope(*this, "depthRange"); 3171 if (IsContextLost()) return; 3172 auto& state = State(); 3173 3174 state.mDepthRange = {zNear, zFar}; 3175 3176 Run<RPROC(DepthRange)>(zNear, zFar); 3177 } 3178 3179 void ClientWebGLContext::Flush(const bool flushGl) const { 3180 RefPtr<webgl::NotLostData> notLost(mNotLost); 3181 if (!notLost) { 3182 return; 3183 } 3184 3185 const FuncScope funcScope(*this, "flush"); 3186 3187 if (flushGl) { 3188 Run<RPROC(Flush)>(); 3189 } 3190 3191 if (notLost->inProcess) return; 3192 const auto& child = notLost->outOfProcess; 3193 child->FlushPendingCmds(); 3194 } 3195 3196 void ClientWebGLContext::Finish() { 3197 RefPtr<webgl::NotLostData> notLost(mNotLost); 3198 if (!notLost) { 3199 return; 3200 } 3201 3202 const auto& inProcess = notLost->inProcess; 3203 if (inProcess) { 3204 inProcess->Finish(); 3205 return; 3206 } 3207 const auto& child = notLost->outOfProcess; 3208 child->FlushPendingCmds(); 3209 (void)child->SendFinish(); 3210 } 3211 3212 void ClientWebGLContext::FrontFace(GLenum mode) { Run<RPROC(FrontFace)>(mode); } 3213 3214 GLenum ClientWebGLContext::GetError() { 3215 const FuncScope funcScope(*this, "getError"); 3216 if (mNextError) { 3217 const auto ret = mNextError; 3218 mNextError = 0; 3219 return ret; 3220 } 3221 3222 RefPtr<webgl::NotLostData> notLost(mNotLost); 3223 if (!notLost) { 3224 return 0; 3225 } 3226 3227 const auto& inProcess = notLost->inProcess; 3228 if (inProcess) { 3229 return inProcess->GetError(); 3230 } 3231 const auto& child = notLost->outOfProcess; 3232 child->FlushPendingCmds(); 3233 GLenum ret = 0; 3234 if (!child->SendGetError(&ret)) { 3235 ret = 0; 3236 } 3237 return ret; 3238 } 3239 3240 void ClientWebGLContext::Hint(GLenum target, GLenum mode) { 3241 Run<RPROC(Hint)>(target, mode); 3242 } 3243 3244 void ClientWebGLContext::LineWidth(GLfloat width) { 3245 Run<RPROC(LineWidth)>(width); 3246 } 3247 3248 Maybe<webgl::ErrorInfo> SetPixelUnpack( 3249 const bool isWebgl2, webgl::PixelUnpackStateWebgl* const unpacking, 3250 const GLenum pname, const GLint param); 3251 3252 void ClientWebGLContext::PixelStorei(const GLenum pname, const GLint iparam) { 3253 const FuncScope funcScope(*this, "pixelStorei"); 3254 if (IsContextLost()) return; 3255 if (!ValidateNonNegative("param", iparam)) return; 3256 const auto param = static_cast<uint32_t>(iparam); 3257 3258 auto& state = State(); 3259 auto& packState = state.mPixelPackState; 3260 switch (pname) { 3261 case LOCAL_GL_PACK_ALIGNMENT: 3262 switch (param) { 3263 case 1: 3264 case 2: 3265 case 4: 3266 case 8: 3267 break; 3268 default: 3269 EnqueueError(LOCAL_GL_INVALID_VALUE, 3270 "PACK_ALIGNMENT must be one of [1,2,4,8], was %i.", 3271 iparam); 3272 return; 3273 } 3274 packState.alignmentInTypeElems = param; 3275 return; 3276 3277 case LOCAL_GL_PACK_ROW_LENGTH: 3278 if (!mIsWebGL2) break; 3279 packState.rowLength = param; 3280 return; 3281 3282 case LOCAL_GL_PACK_SKIP_PIXELS: 3283 if (!mIsWebGL2) break; 3284 packState.skipPixels = param; 3285 return; 3286 3287 case LOCAL_GL_PACK_SKIP_ROWS: 3288 if (!mIsWebGL2) break; 3289 packState.skipRows = param; 3290 return; 3291 3292 case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH: 3293 if (!IsSupported(WebGLExtensionID::MOZ_debug)) { 3294 EnqueueError_ArgEnum("pname", pname); 3295 return; 3296 } 3297 break; 3298 3299 default: 3300 break; 3301 } 3302 3303 const auto err = 3304 SetPixelUnpack(mIsWebGL2, &state.mPixelUnpackState, pname, iparam); 3305 if (err) { 3306 EnqueueError(*err); 3307 return; 3308 } 3309 } 3310 3311 void ClientWebGLContext::PolygonOffset(GLfloat factor, GLfloat units) { 3312 Run<RPROC(PolygonOffset)>(factor, units); 3313 } 3314 3315 void ClientWebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) { 3316 Run<RPROC(SampleCoverage)>(value, invert); 3317 } 3318 3319 void ClientWebGLContext::Scissor(GLint x, GLint y, GLsizei width, 3320 GLsizei height) { 3321 const FuncScope funcScope(*this, "scissor"); 3322 if (IsContextLost()) return; 3323 auto& state = State(); 3324 3325 if (!ValidateNonNegative("width", width) || 3326 !ValidateNonNegative("height", height)) { 3327 return; 3328 } 3329 3330 state.mScissor = {x, y, width, height}; 3331 3332 Run<RPROC(Scissor)>(x, y, width, height); 3333 } 3334 3335 void ClientWebGLContext::StencilFuncSeparate(GLenum face, GLenum func, 3336 GLint ref, GLuint mask) { 3337 Run<RPROC(StencilFuncSeparate)>(face, func, ref, mask); 3338 } 3339 3340 void ClientWebGLContext::StencilMaskSeparate(GLenum face, GLuint mask) { 3341 Run<RPROC(StencilMaskSeparate)>(face, mask); 3342 } 3343 3344 void ClientWebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, 3345 GLenum dpfail, GLenum dppass) { 3346 Run<RPROC(StencilOpSeparate)>(face, sfail, dpfail, dppass); 3347 } 3348 3349 void ClientWebGLContext::Viewport(GLint x, GLint y, GLsizei width, 3350 GLsizei height) { 3351 const FuncScope funcScope(*this, "viewport"); 3352 if (IsContextLost()) return; 3353 auto& state = State(); 3354 3355 if (!ValidateNonNegative("width", width) || 3356 !ValidateNonNegative("height", height)) { 3357 return; 3358 } 3359 3360 state.mViewport = {x, y, width, height}; 3361 3362 Run<RPROC(Viewport)>(x, y, width, height); 3363 } 3364 3365 // ------------------------- Buffer Objects ------------------------- 3366 3367 Maybe<const webgl::ErrorInfo> ValidateBindBuffer( 3368 const GLenum target, const webgl::BufferKind curKind) { 3369 if (curKind == webgl::BufferKind::Undefined) return {}; 3370 3371 auto requiredKind = webgl::BufferKind::NonIndex; 3372 switch (target) { 3373 case LOCAL_GL_COPY_READ_BUFFER: 3374 case LOCAL_GL_COPY_WRITE_BUFFER: 3375 return {}; // Always ok 3376 3377 case LOCAL_GL_ELEMENT_ARRAY_BUFFER: 3378 requiredKind = webgl::BufferKind::Index; 3379 break; 3380 3381 default: 3382 break; 3383 } 3384 3385 if (curKind != requiredKind) { 3386 const auto fnKindStr = [&](const webgl::BufferKind kind) { 3387 if (kind == webgl::BufferKind::Index) return "ELEMENT_ARRAY_BUFFER"; 3388 return "non-ELEMENT_ARRAY_BUFFER"; 3389 }; 3390 const auto info = nsPrintfCString( 3391 "Buffer previously bound to %s cannot be now bound to %s.", 3392 fnKindStr(curKind), fnKindStr(requiredKind)); 3393 return Some( 3394 webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION, info.BeginReading()}); 3395 } 3396 3397 return {}; 3398 } 3399 3400 Maybe<webgl::ErrorInfo> CheckBindBufferRange( 3401 const GLenum target, const GLuint index, const bool isBuffer, 3402 const uint64_t offset, const uint64_t size, const webgl::Limits& limits) { 3403 const auto fnSome = [&](const GLenum type, const nsACString& info) { 3404 return Some(webgl::ErrorInfo{type, info.BeginReading()}); 3405 }; 3406 3407 switch (target) { 3408 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 3409 if (index >= webgl::kMaxTransformFeedbackSeparateAttribs) { 3410 const auto info = nsPrintfCString( 3411 "`index` (%u) must be less than " 3412 "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS (%u).", 3413 index, webgl::kMaxTransformFeedbackSeparateAttribs); 3414 return fnSome(LOCAL_GL_INVALID_VALUE, info); 3415 } 3416 3417 if (offset % 4 != 0 || size % 4 != 0) { 3418 const auto info = 3419 nsPrintfCString("`offset` (%" PRIu64 ") and `size` (%" PRIu64 3420 ") must both be aligned to 4 for" 3421 " TRANSFORM_FEEDBACK_BUFFER.", 3422 offset, size); 3423 return fnSome(LOCAL_GL_INVALID_VALUE, info); 3424 } 3425 break; 3426 3427 case LOCAL_GL_UNIFORM_BUFFER: 3428 if (index >= limits.maxUniformBufferBindings) { 3429 const auto info = nsPrintfCString( 3430 "`index` (%u) must be less than MAX_UNIFORM_BUFFER_BINDINGS (%u).", 3431 index, limits.maxUniformBufferBindings); 3432 return fnSome(LOCAL_GL_INVALID_VALUE, info); 3433 } 3434 3435 if (offset % limits.uniformBufferOffsetAlignment != 0) { 3436 const auto info = 3437 nsPrintfCString("`offset` (%" PRIu64 3438 ") must be aligned to " 3439 "UNIFORM_BUFFER_OFFSET_ALIGNMENT (%u).", 3440 offset, limits.uniformBufferOffsetAlignment); 3441 return fnSome(LOCAL_GL_INVALID_VALUE, info); 3442 } 3443 break; 3444 3445 default: { 3446 const auto info = 3447 nsPrintfCString("Unrecognized `target`: 0x%04x", target); 3448 return fnSome(LOCAL_GL_INVALID_ENUM, info); 3449 } 3450 } 3451 3452 return {}; 3453 } 3454 3455 // - 3456 3457 void ClientWebGLContext::BindBuffer(const GLenum target, 3458 WebGLBufferJS* const buffer) { 3459 const FuncScope funcScope(*this, "bindBuffer"); 3460 if (IsContextLost()) return; 3461 if (buffer && !buffer->ValidateUsable(*this, "buffer")) return; 3462 3463 // - 3464 // Check for INVALID_ENUM 3465 3466 auto& state = State(); 3467 auto* slot = &(state.mBoundVao->mIndexBuffer); 3468 if (target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) { 3469 const auto itr = state.mBoundBufferByTarget.find(target); 3470 if (itr == state.mBoundBufferByTarget.end()) { 3471 EnqueueError_ArgEnum("target", target); 3472 return; 3473 } 3474 slot = &(itr->second); 3475 } 3476 3477 // - 3478 3479 auto kind = webgl::BufferKind::Undefined; 3480 if (buffer) { 3481 kind = buffer->mKind; 3482 } 3483 const auto err = ValidateBindBuffer(target, kind); 3484 if (err) { 3485 EnqueueError(err->type, "%s", err->info.c_str()); 3486 return; 3487 } 3488 3489 // - 3490 // Validation complete 3491 3492 if (buffer && buffer->mKind == webgl::BufferKind::Undefined) { 3493 if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { 3494 buffer->mKind = webgl::BufferKind::Index; 3495 } else { 3496 buffer->mKind = webgl::BufferKind::NonIndex; 3497 } 3498 } 3499 *slot = buffer; 3500 3501 // - 3502 3503 Run<RPROC(BindBuffer)>(target, buffer ? buffer->mId : 0); 3504 } 3505 3506 // - 3507 3508 void ClientWebGLContext::BindBufferRangeImpl(const GLenum target, 3509 const GLuint index, 3510 WebGLBufferJS* const buffer, 3511 const uint64_t offset, 3512 const uint64_t size) { 3513 if (buffer && !buffer->ValidateUsable(*this, "buffer")) return; 3514 auto& state = State(); 3515 3516 // - 3517 3518 const auto& limits = Limits(); 3519 auto err = 3520 CheckBindBufferRange(target, index, bool(buffer), offset, size, limits); 3521 if (err) { 3522 EnqueueError(err->type, "%s", err->info.c_str()); 3523 return; 3524 } 3525 3526 // - 3527 3528 auto kind = webgl::BufferKind::Undefined; 3529 if (buffer) { 3530 kind = buffer->mKind; 3531 } 3532 err = ValidateBindBuffer(target, kind); 3533 if (err) { 3534 EnqueueError(err->type, "%s", err->info.c_str()); 3535 return; 3536 } 3537 3538 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) { 3539 if (state.mTfActiveAndNotPaused) { 3540 EnqueueError(LOCAL_GL_INVALID_OPERATION, 3541 "Cannot change TRANSFORM_FEEDBACK_BUFFER while " 3542 "TransformFeedback is active and not paused."); 3543 return; 3544 } 3545 } 3546 3547 // - 3548 // Validation complete 3549 3550 if (buffer && buffer->mKind == webgl::BufferKind::Undefined) { 3551 buffer->mKind = webgl::BufferKind::NonIndex; 3552 } 3553 3554 // - 3555 3556 switch (target) { 3557 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 3558 state.mBoundTfo->mAttribBuffers[index] = buffer; 3559 break; 3560 3561 case LOCAL_GL_UNIFORM_BUFFER: 3562 state.mBoundUbos[index] = buffer; 3563 break; 3564 3565 default: 3566 MOZ_CRASH("Bad `target`"); 3567 } 3568 state.mBoundBufferByTarget[target] = buffer; 3569 3570 // - 3571 3572 Run<RPROC(BindBufferRange)>(target, index, buffer ? buffer->mId : 0, offset, 3573 size); 3574 } 3575 3576 static inline size_t SizeOfViewElem(const dom::ArrayBufferView& view) { 3577 const auto& elemType = view.Type(); 3578 if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews. 3579 return 1; 3580 3581 return js::Scalar::byteSize(elemType); 3582 } 3583 3584 CanvasUtils::ImageExtraction ImageExtractionResult( 3585 dom::HTMLCanvasElement* aCanvasElement, 3586 dom::OffscreenCanvas* aOffscreenCanvas) { 3587 if (aCanvasElement) { 3588 return CanvasUtils::ImageExtractionResult( 3589 aCanvasElement, nsContentUtils::GetCurrentJSContext(), 3590 aCanvasElement->NodePrincipal()); 3591 } 3592 if (aOffscreenCanvas) { 3593 return CanvasUtils::ImageExtractionResult( 3594 aOffscreenCanvas, nsContentUtils::GetCurrentJSContext(), 3595 aOffscreenCanvas->GetOwnerGlobal() 3596 ? aOffscreenCanvas->GetOwnerGlobal()->PrincipalOrNull() 3597 : nullptr); 3598 } 3599 3600 MOZ_ASSERT_UNREACHABLE( 3601 "Who called ReadPixels or GetBufferSubData without a canvas and how?"); 3602 3603 return CanvasUtils::ImageExtraction::Unrestricted; 3604 } 3605 3606 void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset, 3607 const dom::ArrayBufferView& dstData, 3608 GLuint dstElemOffset, 3609 GLuint dstElemCountOverride) { 3610 const FuncScope funcScope(*this, "getBufferSubData"); 3611 if (IsContextLost()) return; 3612 RefPtr<webgl::NotLostData> notLost(mNotLost); 3613 if (!notLost) { 3614 return; 3615 } 3616 if (!ValidateNonNegative("srcByteOffset", srcByteOffset)) return; 3617 3618 size_t elemSize = SizeOfViewElem(dstData); 3619 dstData.ProcessFixedData([&](const Span<uint8_t>& aData) { 3620 const auto& destView = 3621 ValidateArrayBufferView(aData, elemSize, dstElemOffset, 3622 dstElemCountOverride, LOCAL_GL_INVALID_VALUE); 3623 if (!destView) { 3624 return; 3625 } 3626 3627 RefPtr<ClientWebGLContext> self(this); 3628 auto randomizeOnExit = MakeScopeExit([&, self] { 3629 CanvasUtils::ImageExtraction extraction = 3630 ImageExtractionResult(self->mCanvasElement, self->mOffscreenCanvas); 3631 3632 if (extraction == CanvasUtils::ImageExtraction::Placeholder) { 3633 dom::GeneratePlaceholderCanvasData(destView->size_bytes(), 3634 destView->Elements()); 3635 } else if (extraction == CanvasUtils::ImageExtraction::Randomize) { 3636 // We have no idea what's in the buffer. So, we randomize it as if each 3637 // elemSize bytes is a single element. 3638 uint8_t elementsPerGroup = 1, 3639 bytesPerElement = static_cast<uint8_t>(elemSize), 3640 elementOffset = 0; 3641 bool skipLastElement = false; 3642 3643 nsRFPService::RandomizeElements( 3644 GetCookieJarSettings(), PrincipalOrNull(), destView->data(), 3645 destView->size_bytes(), elementsPerGroup, bytesPerElement, 3646 elementOffset, skipLastElement); 3647 } 3648 }); 3649 3650 const auto& inProcessContext = notLost->inProcess; 3651 if (inProcessContext) { 3652 inProcessContext->GetBufferSubData(target, srcByteOffset, *destView); 3653 return; 3654 } 3655 3656 const auto& child = notLost->outOfProcess; 3657 child->FlushPendingCmds(); 3658 mozilla::ipc::Shmem rawShmem; 3659 if (!child->SendGetBufferSubData(target, srcByteOffset, destView->size(), 3660 &rawShmem)) { 3661 return; 3662 } 3663 const webgl::RaiiShmem shmem{child, rawShmem}; 3664 if (!shmem) { 3665 EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "Failed to map in sub data buffer."); 3666 return; 3667 } 3668 3669 const auto shmemView = Span{shmem.ByteRange()}; 3670 MOZ_RELEASE_ASSERT(shmemView.size() == 1 + destView->size()); 3671 3672 const auto ok = bool(shmemView[0]); 3673 const auto srcView = shmemView.subspan(1); 3674 if (ok) { 3675 Memcpy(&*destView, srcView); 3676 } 3677 }); 3678 } 3679 3680 //// 3681 3682 void ClientWebGLContext::BufferData(GLenum target, WebGLsizeiptr rawSize, 3683 GLenum usage) { 3684 const FuncScope funcScope(*this, "bufferData"); 3685 if (!ValidateNonNegative("size", rawSize)) return; 3686 3687 const auto size = MaybeAs<size_t>(rawSize); 3688 if (!size) { 3689 EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "`size` too large for platform."); 3690 return; 3691 } 3692 Run<RPROC(BufferData_SizeOnly)>(target, *size, usage); 3693 } 3694 3695 void ClientWebGLContext::BufferData( 3696 GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc, 3697 GLenum usage) { 3698 const FuncScope funcScope(*this, "bufferData"); 3699 if (!ValidateNonNull("src", maybeSrc)) return; 3700 const auto& src = maybeSrc.Value(); 3701 3702 src.ProcessFixedData([&](const Span<const uint8_t>& aData) { 3703 Run<RPROC(BufferData)>(target, aData, usage); 3704 }); 3705 } 3706 3707 void ClientWebGLContext::BufferData(GLenum target, 3708 const dom::ArrayBufferView& src, 3709 GLenum usage, GLuint srcElemOffset, 3710 GLuint srcElemCountOverride) { 3711 const FuncScope funcScope(*this, "bufferData"); 3712 size_t elemSize = SizeOfViewElem(src); 3713 src.ProcessFixedData([&](const Span<uint8_t>& aData) { 3714 const auto& range = 3715 ValidateArrayBufferView(aData, elemSize, srcElemOffset, 3716 srcElemCountOverride, LOCAL_GL_INVALID_VALUE); 3717 if (!range) { 3718 return; 3719 } 3720 Run<RPROC(BufferData)>(target, *range, usage); 3721 }); 3722 } 3723 3724 //// 3725 3726 void ClientWebGLContext::BufferSubData(GLenum target, 3727 WebGLsizeiptr dstByteOffset, 3728 const dom::ArrayBuffer& src) { 3729 const FuncScope funcScope(*this, "bufferSubData"); 3730 src.ProcessFixedData([&](const Span<const uint8_t>& aData) { 3731 Run<RPROC(BufferSubData)>(target, dstByteOffset, aData, 3732 /* unsynchronized */ false); 3733 }); 3734 } 3735 3736 void ClientWebGLContext::BufferSubData(GLenum target, 3737 WebGLsizeiptr dstByteOffset, 3738 const dom::ArrayBufferView& src, 3739 GLuint srcElemOffset, 3740 GLuint srcElemCountOverride) { 3741 const FuncScope funcScope(*this, "bufferSubData"); 3742 size_t elemSize = SizeOfViewElem(src); 3743 src.ProcessFixedData([&](const Span<uint8_t>& aData) { 3744 const auto& range = 3745 ValidateArrayBufferView(aData, elemSize, srcElemOffset, 3746 srcElemCountOverride, LOCAL_GL_INVALID_VALUE); 3747 if (!range) { 3748 return; 3749 } 3750 Run<RPROC(BufferSubData)>(target, dstByteOffset, *range, 3751 /* unsynchronized */ false); 3752 }); 3753 } 3754 3755 void ClientWebGLContext::BufferSubData( 3756 GLenum target, WebGLsizeiptr dstByteOffset, 3757 const dom::AllowSharedBufferSource& src) { 3758 if (src.IsArrayBufferView()) { 3759 BufferSubData(target, dstByteOffset, src.GetAsArrayBufferView()); 3760 return; 3761 } else if (src.IsArrayBuffer()) { 3762 BufferSubData(target, dstByteOffset, src.GetAsArrayBuffer()); 3763 return; 3764 } 3765 3766 MOZ_ASSERT_UNREACHABLE("Union is uninitialized?"); 3767 } 3768 3769 void ClientWebGLContext::CopyBufferSubData(GLenum readTarget, 3770 GLenum writeTarget, 3771 GLintptr readOffset, 3772 GLintptr writeOffset, 3773 GLsizeiptr size) { 3774 const FuncScope funcScope(*this, "copyBufferSubData"); 3775 if (!ValidateNonNegative("readOffset", readOffset) || 3776 !ValidateNonNegative("writeOffset", writeOffset) || 3777 !ValidateNonNegative("size", size)) { 3778 return; 3779 } 3780 Run<RPROC(CopyBufferSubData)>( 3781 readTarget, writeTarget, static_cast<uint64_t>(readOffset), 3782 static_cast<uint64_t>(writeOffset), static_cast<uint64_t>(size)); 3783 } 3784 3785 // -------------------------- Framebuffer Objects -------------------------- 3786 3787 void ClientWebGLContext::BindFramebuffer(const GLenum target, 3788 WebGLFramebufferJS* const fb) { 3789 const FuncScope funcScope(*this, "bindFramebuffer"); 3790 if (IsContextLost()) return; 3791 if (fb && !fb->ValidateUsable(*this, "fb")) return; 3792 3793 if (!IsFramebufferTarget(mIsWebGL2, target)) { 3794 EnqueueError_ArgEnum("target", target); 3795 return; 3796 } 3797 3798 // - 3799 3800 auto& state = State(); 3801 3802 switch (target) { 3803 case LOCAL_GL_FRAMEBUFFER: 3804 state.mBoundDrawFb = fb; 3805 state.mBoundReadFb = fb; 3806 break; 3807 3808 case LOCAL_GL_DRAW_FRAMEBUFFER: 3809 state.mBoundDrawFb = fb; 3810 break; 3811 case LOCAL_GL_READ_FRAMEBUFFER: 3812 state.mBoundReadFb = fb; 3813 break; 3814 3815 default: 3816 MOZ_CRASH(); 3817 } 3818 3819 // - 3820 3821 if (fb) { 3822 fb->mHasBeenBound = true; 3823 } 3824 3825 Run<RPROC(BindFramebuffer)>(target, fb ? fb->mId : 0); 3826 } 3827 3828 // - 3829 3830 void ClientWebGLContext::FramebufferTexture2D(GLenum target, GLenum attachSlot, 3831 GLenum bindImageTarget, 3832 WebGLTextureJS* const tex, 3833 GLint mipLevel) const { 3834 const FuncScope funcScope(*this, "framebufferTexture2D"); 3835 if (IsContextLost()) return; 3836 3837 const auto bindTexTarget = ImageToTexTarget(bindImageTarget); 3838 uint32_t zLayer = 0; 3839 switch (bindTexTarget) { 3840 case LOCAL_GL_TEXTURE_2D: 3841 break; 3842 case LOCAL_GL_TEXTURE_CUBE_MAP: 3843 zLayer = bindImageTarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; 3844 break; 3845 default: 3846 EnqueueError_ArgEnum("imageTarget", bindImageTarget); 3847 return; 3848 } 3849 3850 if (!mIsWebGL2 && 3851 !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap)) { 3852 if (mipLevel != 0) { 3853 EnqueueError(LOCAL_GL_INVALID_VALUE, 3854 "mipLevel != 0 requires OES_fbo_render_mipmap."); 3855 return; 3856 } 3857 } 3858 3859 FramebufferAttach(target, attachSlot, bindImageTarget, nullptr, tex, 3860 static_cast<uint32_t>(mipLevel), zLayer, 0); 3861 } 3862 3863 Maybe<webgl::ErrorInfo> CheckFramebufferAttach(const GLenum bindImageTarget, 3864 const GLenum curTexTarget, 3865 const uint32_t mipLevel, 3866 const uint32_t zLayerBase, 3867 const uint32_t zLayerCount, 3868 const webgl::Limits& limits) { 3869 if (!curTexTarget) { 3870 return Some( 3871 webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION, 3872 "`tex` not yet bound. Call bindTexture first."}); 3873 } 3874 3875 auto texTarget = curTexTarget; 3876 if (bindImageTarget) { 3877 // FramebufferTexture2D 3878 const auto bindTexTarget = ImageToTexTarget(bindImageTarget); 3879 if (curTexTarget != bindTexTarget) { 3880 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION, 3881 "`tex` cannot be rebound to a new target."}); 3882 } 3883 3884 switch (bindTexTarget) { 3885 case LOCAL_GL_TEXTURE_2D: 3886 case LOCAL_GL_TEXTURE_CUBE_MAP: 3887 break; 3888 default: 3889 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_ENUM, 3890 "`tex` must have been bound to target " 3891 "TEXTURE_2D or TEXTURE_CUBE_MAP."}); 3892 } 3893 texTarget = bindTexTarget; 3894 } else { 3895 // FramebufferTextureLayer/Multiview 3896 switch (curTexTarget) { 3897 case LOCAL_GL_TEXTURE_2D_ARRAY: 3898 case LOCAL_GL_TEXTURE_3D: 3899 break; 3900 default: 3901 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION, 3902 "`tex` must have been bound to target " 3903 "TEXTURE_2D_ARRAY or TEXTURE_3D."}); 3904 } 3905 } 3906 MOZ_ASSERT(texTarget); 3907 uint32_t maxSize; 3908 uint32_t maxZ; 3909 switch (texTarget) { 3910 case LOCAL_GL_TEXTURE_2D: 3911 maxSize = limits.maxTex2dSize; 3912 maxZ = 1; 3913 break; 3914 case LOCAL_GL_TEXTURE_CUBE_MAP: 3915 maxSize = limits.maxTexCubeSize; 3916 maxZ = 6; 3917 break; 3918 case LOCAL_GL_TEXTURE_2D_ARRAY: 3919 maxSize = limits.maxTex2dSize; 3920 maxZ = limits.maxTexArrayLayers; 3921 break; 3922 case LOCAL_GL_TEXTURE_3D: 3923 maxSize = limits.maxTex3dSize; 3924 maxZ = limits.maxTex3dSize; 3925 break; 3926 default: 3927 MOZ_CRASH(); 3928 } 3929 const auto maxMipLevel = FloorLog2(maxSize); 3930 if (mipLevel > maxMipLevel) { 3931 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, 3932 "`mipLevel` too large for texture target."}); 3933 } 3934 const auto requiredZLayers = CheckedInt<uint32_t>(zLayerBase) + zLayerCount; 3935 if (!requiredZLayers.isValid() || requiredZLayers.value() > maxZ) { 3936 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, 3937 "`zLayer` too large for texture target."}); 3938 } 3939 3940 return {}; 3941 } 3942 3943 void ClientWebGLContext::FramebufferAttach( 3944 const GLenum target, const GLenum attachSlot, const GLenum bindImageTarget, 3945 WebGLRenderbufferJS* const rb, WebGLTextureJS* const tex, 3946 const uint32_t mipLevel, const uint32_t zLayerBase, 3947 const uint32_t numViewLayers) const { 3948 if (rb && !rb->ValidateUsable(*this, "rb")) return; 3949 if (tex && !tex->ValidateUsable(*this, "tex")) return; 3950 const auto& state = State(); 3951 const auto& limits = Limits(); 3952 3953 if (!IsFramebufferTarget(mIsWebGL2, target)) { 3954 EnqueueError_ArgEnum("target", target); 3955 return; 3956 } 3957 auto fb = state.mBoundDrawFb; 3958 if (target == LOCAL_GL_READ_FRAMEBUFFER) { 3959 fb = state.mBoundReadFb; 3960 } 3961 if (!fb) { 3962 EnqueueError(LOCAL_GL_INVALID_OPERATION, "No framebuffer bound."); 3963 return; 3964 } 3965 3966 if (fb->mOpaque) { 3967 EnqueueError( 3968 LOCAL_GL_INVALID_OPERATION, 3969 "An opaque framebuffer's attachments cannot be inspected or changed."); 3970 return; 3971 } 3972 3973 // - 3974 // Multiview-specific validation skipped by Host. 3975 3976 if (tex && numViewLayers) { 3977 if (tex->mTarget != LOCAL_GL_TEXTURE_2D_ARRAY) { 3978 EnqueueError(LOCAL_GL_INVALID_OPERATION, 3979 "`tex` must have been bound to target TEXTURE_2D_ARRAY."); 3980 return; 3981 } 3982 if (numViewLayers > limits.maxMultiviewLayers) { 3983 EnqueueError(LOCAL_GL_INVALID_VALUE, 3984 "`numViews` (%u) must be <= MAX_VIEWS (%u).", numViewLayers, 3985 limits.maxMultiviewLayers); 3986 return; 3987 } 3988 } 3989 3990 // - 3991 3992 webgl::ObjectId id = 0; 3993 if (tex) { 3994 auto zLayerCount = numViewLayers; 3995 if (!zLayerCount) { 3996 zLayerCount = 1; 3997 } 3998 const auto err = 3999 CheckFramebufferAttach(bindImageTarget, tex->mTarget, mipLevel, 4000 zLayerBase, zLayerCount, limits); 4001 if (err) { 4002 EnqueueError(err->type, "%s", err->info.c_str()); 4003 return; 4004 } 4005 id = tex->mId; 4006 } else if (rb) { 4007 if (!rb->mHasBeenBound) { 4008 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4009 "`rb` has not yet been bound with BindRenderbuffer."); 4010 return; 4011 } 4012 id = rb->mId; 4013 } 4014 4015 // Ready! 4016 // But DEPTH_STENCIL in webgl2 is actually two slots! 4017 4018 const auto fnAttachTo = [&](const GLenum actualAttachSlot) { 4019 const auto slot = fb->GetAttachment(actualAttachSlot); 4020 if (!slot) { 4021 EnqueueError_ArgEnum("attachment", actualAttachSlot); 4022 return; 4023 } 4024 4025 slot->rb = rb; 4026 slot->tex = tex; 4027 4028 Run<RPROC(FramebufferAttach)>(target, actualAttachSlot, bindImageTarget, id, 4029 mipLevel, zLayerBase, numViewLayers); 4030 }; 4031 4032 if (mIsWebGL2 && attachSlot == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { 4033 fnAttachTo(LOCAL_GL_DEPTH_ATTACHMENT); 4034 fnAttachTo(LOCAL_GL_STENCIL_ATTACHMENT); 4035 } else { 4036 fnAttachTo(attachSlot); 4037 } 4038 4039 if (bindImageTarget) { 4040 if (rb) { 4041 rb->mHasBeenBound = true; 4042 } 4043 if (tex) { 4044 tex->mTarget = ImageToTexTarget(bindImageTarget); 4045 } 4046 } 4047 } 4048 4049 // - 4050 4051 void ClientWebGLContext::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, 4052 GLint srcY1, GLint dstX0, GLint dstY0, 4053 GLint dstX1, GLint dstY1, 4054 GLbitfield mask, GLenum filter) { 4055 Run<RPROC(BlitFramebuffer)>(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, 4056 dstY1, mask, filter); 4057 4058 AfterDrawCall(); 4059 } 4060 4061 void ClientWebGLContext::InvalidateFramebuffer( 4062 GLenum target, const dom::Sequence<GLenum>& attachments, 4063 ErrorResult& unused) { 4064 Run<RPROC(InvalidateFramebuffer)>(target, Span{attachments}); 4065 4066 // Never invalidate the backbuffer, so never needs AfterDrawCall. 4067 } 4068 4069 void ClientWebGLContext::InvalidateSubFramebuffer( 4070 GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y, 4071 GLsizei width, GLsizei height, ErrorResult& unused) { 4072 Run<RPROC(InvalidateSubFramebuffer)>(target, Span{attachments}, x, y, width, 4073 height); 4074 4075 // Never invalidate the backbuffer, so never needs AfterDrawCall. 4076 } 4077 4078 void ClientWebGLContext::ReadBuffer(GLenum mode) { 4079 Run<RPROC(ReadBuffer)>(mode); 4080 } 4081 4082 // ----------------------- Renderbuffer objects ----------------------- 4083 4084 void ClientWebGLContext::BindRenderbuffer(const GLenum target, 4085 WebGLRenderbufferJS* const rb) { 4086 const FuncScope funcScope(*this, "bindRenderbuffer"); 4087 if (IsContextLost()) return; 4088 if (rb && !rb->ValidateUsable(*this, "rb")) return; 4089 auto& state = State(); 4090 4091 if (target != LOCAL_GL_RENDERBUFFER) { 4092 EnqueueError_ArgEnum("target", target); 4093 return; 4094 } 4095 4096 state.mBoundRb = rb; 4097 if (rb) { 4098 rb->mHasBeenBound = true; 4099 } 4100 } 4101 4102 void ClientWebGLContext::RenderbufferStorageMultisample(GLenum target, 4103 GLsizei samples, 4104 GLenum internalFormat, 4105 GLsizei width, 4106 GLsizei height) const { 4107 const FuncScope funcScope(*this, "renderbufferStorageMultisample"); 4108 if (IsContextLost()) return; 4109 4110 if (target != LOCAL_GL_RENDERBUFFER) { 4111 EnqueueError_ArgEnum("target", target); 4112 return; 4113 } 4114 4115 const auto& state = State(); 4116 4117 const auto& rb = state.mBoundRb; 4118 if (!rb) { 4119 EnqueueError(LOCAL_GL_INVALID_OPERATION, "No renderbuffer bound"); 4120 return; 4121 } 4122 4123 if (!ValidateNonNegative("width", width) || 4124 !ValidateNonNegative("height", height) || 4125 !ValidateNonNegative("samples", samples)) { 4126 return; 4127 } 4128 4129 if (internalFormat == LOCAL_GL_DEPTH_STENCIL && samples > 0) { 4130 // While our backend supports it trivially, the spec forbids it. 4131 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4132 "WebGL 1's DEPTH_STENCIL format may not be multisampled. Use " 4133 "DEPTH24_STENCIL8 when `samples > 0`."); 4134 return; 4135 } 4136 4137 Run<RPROC(RenderbufferStorageMultisample)>( 4138 rb->mId, static_cast<uint32_t>(samples), internalFormat, 4139 static_cast<uint32_t>(width), static_cast<uint32_t>(height)); 4140 } 4141 4142 // --------------------------- Texture objects --------------------------- 4143 4144 void ClientWebGLContext::ActiveTexture(const GLenum texUnitEnum) { 4145 const FuncScope funcScope(*this, "activeTexture"); 4146 if (IsContextLost()) return; 4147 4148 if (texUnitEnum < LOCAL_GL_TEXTURE0) { 4149 EnqueueError(LOCAL_GL_INVALID_VALUE, 4150 "`texture` (0x%04x) must be >= TEXTURE0 (0x%04x).", 4151 texUnitEnum, LOCAL_GL_TEXTURE0); 4152 return; 4153 } 4154 4155 const auto texUnit = texUnitEnum - LOCAL_GL_TEXTURE0; 4156 4157 auto& state = State(); 4158 if (texUnit >= state.mTexUnits.size()) { 4159 EnqueueError(LOCAL_GL_INVALID_VALUE, 4160 "TEXTURE%u must be < MAX_COMBINED_TEXTURE_IMAGE_UNITS (%zu).", 4161 texUnit, state.mTexUnits.size()); 4162 return; 4163 } 4164 4165 //- 4166 4167 state.mActiveTexUnit = texUnit; 4168 Run<RPROC(ActiveTexture)>(texUnit); 4169 } 4170 4171 static bool IsTexTarget(const GLenum texTarget, const bool webgl2) { 4172 switch (texTarget) { 4173 case LOCAL_GL_TEXTURE_2D: 4174 case LOCAL_GL_TEXTURE_CUBE_MAP: 4175 return true; 4176 4177 case LOCAL_GL_TEXTURE_2D_ARRAY: 4178 case LOCAL_GL_TEXTURE_3D: 4179 return webgl2; 4180 4181 default: 4182 return false; 4183 } 4184 } 4185 4186 void ClientWebGLContext::BindTexture(const GLenum texTarget, 4187 WebGLTextureJS* const tex) { 4188 const FuncScope funcScope(*this, "bindTexture"); 4189 if (IsContextLost()) return; 4190 if (tex && !tex->ValidateUsable(*this, "tex")) return; 4191 4192 if (!IsTexTarget(texTarget, mIsWebGL2)) { 4193 EnqueueError_ArgEnum("texTarget", texTarget); 4194 return; 4195 } 4196 4197 if (tex && tex->mTarget) { 4198 if (texTarget != tex->mTarget) { 4199 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4200 "Texture previously bound to %s cannot be bound now to %s.", 4201 EnumString(tex->mTarget).c_str(), 4202 EnumString(texTarget).c_str()); 4203 return; 4204 } 4205 } 4206 4207 auto& state = State(); 4208 auto& texUnit = state.mTexUnits[state.mActiveTexUnit]; 4209 texUnit.texByTarget[texTarget] = tex; 4210 if (tex) { 4211 tex->mTarget = texTarget; 4212 } 4213 4214 Run<RPROC(BindTexture)>(texTarget, tex ? tex->mId : 0); 4215 } 4216 4217 void ClientWebGLContext::GenerateMipmap(GLenum texTarget) const { 4218 Run<RPROC(GenerateMipmap)>(texTarget); 4219 } 4220 4221 void ClientWebGLContext::GetTexParameter( 4222 JSContext* cx, GLenum texTarget, GLenum pname, 4223 JS::MutableHandle<JS::Value> retval) const { 4224 const FuncScope funcScope(*this, "getTexParameter"); 4225 retval.set(JS::NullValue()); 4226 RefPtr<webgl::NotLostData> notLost(mNotLost); 4227 if (!notLost) { 4228 return; 4229 } 4230 4231 auto& state = State(); 4232 4233 auto& texUnit = state.mTexUnits[state.mActiveTexUnit]; 4234 4235 const auto& tex = Find(texUnit.texByTarget, texTarget, nullptr); 4236 if (!tex) { 4237 if (!IsTexTarget(texTarget, mIsWebGL2)) { 4238 EnqueueError_ArgEnum("texTarget", texTarget); 4239 } else { 4240 EnqueueError(LOCAL_GL_INVALID_OPERATION, "No texture bound to %s[%u].", 4241 EnumString(texTarget).c_str(), state.mActiveTexUnit); 4242 } 4243 return; 4244 } 4245 4246 const auto maybe = [&]() { 4247 const auto& inProcess = notLost->inProcess; 4248 if (inProcess) { 4249 return inProcess->GetTexParameter(tex->mId, pname); 4250 } 4251 const auto& child = notLost->outOfProcess; 4252 child->FlushPendingCmds(); 4253 Maybe<double> ret; 4254 if (!child->SendGetTexParameter(tex->mId, pname, &ret)) { 4255 ret.reset(); 4256 } 4257 return ret; 4258 }(); 4259 4260 if (maybe) { 4261 switch (pname) { 4262 case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: 4263 retval.set(JS::BooleanValue(*maybe)); 4264 break; 4265 4266 default: 4267 retval.set(JS::NumberValue(*maybe)); 4268 break; 4269 } 4270 } 4271 } 4272 4273 void ClientWebGLContext::TexParameterf(GLenum texTarget, GLenum pname, 4274 GLfloat param) { 4275 Run<RPROC(TexParameter_base)>(texTarget, pname, FloatOrInt(param)); 4276 } 4277 4278 void ClientWebGLContext::TexParameteri(GLenum texTarget, GLenum pname, 4279 GLint param) { 4280 Run<RPROC(TexParameter_base)>(texTarget, pname, FloatOrInt(param)); 4281 } 4282 4283 //////////////////////////////////// 4284 4285 static GLenum JSTypeMatchUnpackTypeError(GLenum unpackType, 4286 js::Scalar::Type jsType) { 4287 bool matches = false; 4288 switch (unpackType) { 4289 case LOCAL_GL_BYTE: 4290 matches = (jsType == js::Scalar::Type::Int8); 4291 break; 4292 4293 case LOCAL_GL_UNSIGNED_BYTE: 4294 matches = (jsType == js::Scalar::Type::Uint8 || 4295 jsType == js::Scalar::Type::Uint8Clamped); 4296 break; 4297 4298 case LOCAL_GL_SHORT: 4299 matches = (jsType == js::Scalar::Type::Int16); 4300 break; 4301 4302 case LOCAL_GL_UNSIGNED_SHORT: 4303 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: 4304 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: 4305 case LOCAL_GL_UNSIGNED_SHORT_5_6_5: 4306 case LOCAL_GL_HALF_FLOAT: 4307 case LOCAL_GL_HALF_FLOAT_OES: 4308 matches = (jsType == js::Scalar::Type::Uint16); 4309 break; 4310 4311 case LOCAL_GL_INT: 4312 matches = (jsType == js::Scalar::Type::Int32); 4313 break; 4314 4315 case LOCAL_GL_UNSIGNED_INT: 4316 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: 4317 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: 4318 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: 4319 case LOCAL_GL_UNSIGNED_INT_24_8: 4320 matches = (jsType == js::Scalar::Type::Uint32); 4321 break; 4322 4323 case LOCAL_GL_FLOAT: 4324 matches = (jsType == js::Scalar::Type::Float32); 4325 break; 4326 4327 case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV: 4328 matches = false; // No valid jsType, but we allow uploads with null. 4329 break; 4330 4331 default: 4332 return LOCAL_GL_INVALID_ENUM; 4333 } 4334 if (!matches) return LOCAL_GL_INVALID_OPERATION; 4335 return 0; 4336 } 4337 4338 ///////////////////////////////////////////////// 4339 4340 static inline uvec2 CastUvec2(const ivec2& val) { 4341 return {static_cast<uint32_t>(val.x), static_cast<uint32_t>(val.y)}; 4342 } 4343 4344 static inline uvec3 CastUvec3(const ivec3& val) { 4345 return {static_cast<uint32_t>(val.x), static_cast<uint32_t>(val.y), 4346 static_cast<uint32_t>(val.z)}; 4347 } 4348 4349 template <typename T> 4350 Range<T> SubRange(const Range<T>& full, const size_t offset, 4351 const size_t length) { 4352 const auto newBegin = full.begin() + offset; 4353 return Range<T>{newBegin, newBegin + length}; 4354 } 4355 4356 Maybe<Span<const uint8_t>> GetRangeFromData(const Span<uint8_t>& data, 4357 size_t bytesPerElem, 4358 GLuint elemOffset, 4359 GLuint elemCountOverride) { 4360 auto elemCount = data.size() / bytesPerElem; 4361 if (elemOffset > elemCount) return {}; 4362 elemCount -= elemOffset; 4363 4364 if (elemCountOverride) { 4365 if (elemCountOverride > elemCount) return {}; 4366 elemCount = elemCountOverride; 4367 } 4368 return Some( 4369 data.subspan(elemOffset * bytesPerElem, elemCount * bytesPerElem)); 4370 } 4371 4372 // - 4373 4374 static bool IsTexTargetForDims(const GLenum texTarget, const bool webgl2, 4375 const uint8_t funcDims) { 4376 if (!IsTexTarget(texTarget, webgl2)) return false; 4377 switch (texTarget) { 4378 case LOCAL_GL_TEXTURE_2D: 4379 case LOCAL_GL_TEXTURE_CUBE_MAP: 4380 return funcDims == 2; 4381 4382 default: 4383 return funcDims == 3; 4384 } 4385 } 4386 4387 void ClientWebGLContext::TexStorage(uint8_t funcDims, GLenum texTarget, 4388 GLsizei levels, GLenum internalFormat, 4389 const ivec3& size) const { 4390 const FuncScope funcScope(*this, "texStorage[23]D"); 4391 if (IsContextLost()) return; 4392 if (!IsTexTargetForDims(texTarget, mIsWebGL2, funcDims)) { 4393 EnqueueError_ArgEnum("texTarget", texTarget); 4394 return; 4395 } 4396 Run<RPROC(TexStorage)>(texTarget, static_cast<uint32_t>(levels), 4397 internalFormat, CastUvec3(size)); 4398 } 4399 4400 // - 4401 4402 void webgl::TexUnpackBlobDesc::Shrink(const webgl::PackingInfo& pi) { 4403 if (cpuData) { 4404 if (!size.x || !size.y || !size.z) return; 4405 4406 const auto unpackRes = ExplicitUnpacking(pi, {}); 4407 if (!unpackRes.isOk()) { 4408 return; 4409 } 4410 const auto& unpack = unpackRes.inspect(); 4411 4412 const auto bytesUpperBound = 4413 CheckedInt<size_t>(unpack.metrics.bytesPerRowStride) * 4414 unpack.metrics.totalRows; 4415 if (bytesUpperBound.isValid()) { 4416 auto& span = *cpuData; 4417 span = span.subspan(0, std::min(span.size(), bytesUpperBound.value())); 4418 } 4419 } 4420 } 4421 4422 // - 4423 4424 void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget, 4425 GLint level, GLenum respecFormat, 4426 const ivec3& offset, 4427 const Maybe<ivec3>& isize, GLint border, 4428 const webgl::PackingInfo& pi, 4429 const TexImageSource& src) const { 4430 RefPtr<webgl::NotLostData> notLost(mNotLost); 4431 if (!notLost) { 4432 return; 4433 } 4434 4435 const FuncScope funcScope(*this, "tex(Sub)Image[23]D"); 4436 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget), mIsWebGL2, funcDims)) { 4437 EnqueueError_ArgEnum("imageTarget", imageTarget); 4438 return; 4439 } 4440 if (border != 0) { 4441 EnqueueError(LOCAL_GL_INVALID_VALUE, "`border` must be 0."); 4442 return; 4443 } 4444 4445 Maybe<uvec3> size; 4446 if (isize) { 4447 size = Some(CastUvec3(isize.value())); 4448 } 4449 4450 // - 4451 4452 // - 4453 bool isDataUpload = false; 4454 auto desc = [&]() -> Maybe<webgl::TexUnpackBlobDesc> { 4455 if (src.mPboOffset) { 4456 isDataUpload = true; 4457 const auto offset = static_cast<uint64_t>(*src.mPboOffset); 4458 return Some(webgl::TexUnpackBlobDesc{imageTarget, 4459 size.value(), 4460 gfxAlphaType::NonPremult, 4461 {}, 4462 Some(offset)}); 4463 } 4464 4465 if (src.mView) { 4466 isDataUpload = true; 4467 const auto& view = *src.mView; 4468 const auto& jsType = view.Type(); 4469 const auto err = JSTypeMatchUnpackTypeError(pi.type, jsType); 4470 switch (err) { 4471 case LOCAL_GL_INVALID_ENUM: 4472 EnqueueError_ArgEnum("unpackType", pi.type); 4473 return {}; 4474 case LOCAL_GL_INVALID_OPERATION: 4475 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4476 "ArrayBufferView type %s not compatible with `type` %s.", 4477 name(jsType), EnumString(pi.type).c_str()); 4478 return {}; 4479 default: 4480 break; 4481 } 4482 4483 return view.ProcessData( 4484 [&](const Span<uint8_t>& aData, 4485 JS::AutoCheckCannotGC&& nogc) -> Maybe<webgl::TexUnpackBlobDesc> { 4486 const auto range = GetRangeFromData(aData, SizeOfViewElem(view), 4487 src.mViewElemOffset, 4488 src.mViewElemLengthOverride); 4489 if (!range) { 4490 nogc.reset(); 4491 EnqueueError(LOCAL_GL_INVALID_OPERATION, "`source` too small."); 4492 return {}; 4493 } 4494 return Some(webgl::TexUnpackBlobDesc{imageTarget, 4495 size.value(), 4496 gfxAlphaType::NonPremult, 4497 Some(*range), 4498 {}}); 4499 }); 4500 } 4501 4502 if (src.mImageBitmap) { 4503 return webgl::FromImageBitmap(imageTarget, size, *(src.mImageBitmap), 4504 src.mOut_error); 4505 } 4506 4507 if (src.mImageData) { 4508 const auto& imageData = *src.mImageData; 4509 dom::Uint8ClampedArray scopedArr; 4510 MOZ_RELEASE_ASSERT(scopedArr.Init(imageData.GetDataObject())); 4511 4512 return scopedArr.ProcessData( 4513 [&](const Span<uint8_t>& aData, 4514 JS::AutoCheckCannotGC&& nogc) -> Maybe<webgl::TexUnpackBlobDesc> { 4515 const auto dataSize = aData.Length(); 4516 const auto data = aData.Elements(); 4517 if (dataSize == 0) { 4518 nogc.reset(); // aData will not be used. 4519 EnqueueError( 4520 LOCAL_GL_INVALID_VALUE, 4521 "ImageData.data.buffer is Detached. (Maybe you Transfered " 4522 "it to a Worker?"); 4523 return {}; 4524 } 4525 4526 // - 4527 4528 const gfx::IntSize imageSize(imageData.Width(), imageData.Height()); 4529 const auto sizeFromDims = 4530 CheckedInt<size_t>(imageSize.width) * imageSize.height * 4; 4531 MOZ_RELEASE_ASSERT(sizeFromDims.isValid() && 4532 sizeFromDims.value() == dataSize); 4533 4534 const RefPtr<gfx::DataSourceSurface> surf = 4535 gfx::Factory::CreateWrappingDataSourceSurface( 4536 data, imageSize.width * 4, imageSize, 4537 gfx::SurfaceFormat::R8G8B8A8); 4538 MOZ_ASSERT(surf); 4539 4540 // - 4541 4542 const auto imageUSize = *uvec2::FromSize(imageSize); 4543 const auto concreteSize = 4544 size.valueOr(uvec3{imageUSize.x, imageUSize.y, 1}); 4545 4546 // WhatWG "HTML Living Standard" (30 October 2015): 4547 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be 4548 // returned as non-premultiplied alpha values." 4549 auto result = 4550 Some(webgl::TexUnpackBlobDesc{imageTarget, 4551 concreteSize, 4552 gfxAlphaType::NonPremult, 4553 {}, 4554 {}, 4555 Some(imageUSize), 4556 nullptr, 4557 {}, 4558 surf}); 4559 nogc.reset(); // Done with aData 4560 return result; 4561 }); 4562 } 4563 4564 if (src.mOffscreenCanvas) { 4565 return webgl::FromOffscreenCanvas( 4566 *this, imageTarget, size, *(src.mOffscreenCanvas), src.mOut_error); 4567 } 4568 4569 if (src.mVideoFrame) { 4570 return webgl::FromVideoFrame(*this, imageTarget, size, *(src.mVideoFrame), 4571 src.mOut_error); 4572 } 4573 4574 if (src.mDomElem) { 4575 return webgl::FromDomElem(*this, imageTarget, size, *(src.mDomElem), 4576 src.mOut_error); 4577 } 4578 4579 return Some(webgl::TexUnpackBlobDesc{ 4580 imageTarget, size.value(), gfxAlphaType::NonPremult, {}, {}}); 4581 }(); 4582 if (!desc) { 4583 return; 4584 } 4585 4586 // - 4587 4588 const auto& rawUnpacking = State().mPixelUnpackState; 4589 { 4590 auto defaultSubrectState = webgl::PixelPackingState{}; 4591 defaultSubrectState.alignmentInTypeElems = 4592 rawUnpacking.alignmentInTypeElems; 4593 const bool isSubrect = (rawUnpacking != defaultSubrectState); 4594 if (isDataUpload && isSubrect) { 4595 if (rawUnpacking.flipY || rawUnpacking.premultiplyAlpha) { 4596 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4597 "Non-DOM-Element uploads with alpha-premult" 4598 " or y-flip do not support subrect selection."); 4599 return; 4600 } 4601 } 4602 } 4603 desc->unpacking = rawUnpacking; 4604 4605 if (desc->structuredSrcSize) { 4606 // WebGL 2 spec: 4607 // ### 5.35 Pixel store parameters for uploads from TexImageSource 4608 // UNPACK_ALIGNMENT and UNPACK_ROW_LENGTH are ignored. 4609 const auto& elemSize = *desc->structuredSrcSize; 4610 desc->unpacking.alignmentInTypeElems = 1; 4611 desc->unpacking.rowLength = elemSize.x; 4612 } 4613 if (!desc->unpacking.rowLength) { 4614 desc->unpacking.rowLength = desc->size.x; 4615 } 4616 if (!desc->unpacking.imageHeight) { 4617 desc->unpacking.imageHeight = desc->size.y; 4618 } 4619 4620 // - 4621 4622 // - 4623 4624 mozilla::ipc::Shmem* pShmem = nullptr; 4625 // Image to release after WebGLContext::TexImage(). 4626 RefPtr<layers::Image> keepAliveImage; 4627 RefPtr<gfx::SourceSurface> keepAliveSurf; 4628 4629 if (desc->sd) { 4630 const auto& sd = *(desc->sd); 4631 const auto sdType = sd.type(); 4632 const auto& contextInfo = notLost->info; 4633 4634 // TODO (Bug 754256): Figure out the source colorSpace. 4635 const auto& webgl = this; 4636 dom::PredefinedColorSpace srcColorSpace = dom::PredefinedColorSpace::Srgb; 4637 dom::PredefinedColorSpace dstColorSpace = 4638 webgl->mUnpackColorSpace ? *webgl->mUnpackColorSpace 4639 : dom::PredefinedColorSpace::Srgb; 4640 bool sameColorSpace = (srcColorSpace == dstColorSpace); 4641 4642 const auto fallbackReason = [&]() -> Maybe<std::string> { 4643 // Canvas2D surfaces may require and depend upon conversions such as 4644 // unpremultiplying the source data. We allow these conversions to occur 4645 // because it is still a performance benefit to do the conversion in the 4646 // GPU process where WebGL processing happens, rather than cause excess 4647 // synchronization and data transfer back to the content process. 4648 const bool allowConversion = 4649 sdType == layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface; 4650 if (const char* fallbackReason = 4651 BlitPreventReason(imageTarget, level, offset, respecFormat, pi, 4652 *desc, contextInfo.optionalRenderableFormatBits, 4653 sameColorSpace, allowConversion)) { 4654 return Some(std::string(fallbackReason)); 4655 } 4656 4657 const bool canUploadViaSd = contextInfo.uploadableSdTypes[sdType]; 4658 if (!canUploadViaSd) { 4659 const nsPrintfCString msg( 4660 "Fast uploads for resource type %i not implemented.", int(sdType)); 4661 return Some(ToString(msg)); 4662 } 4663 4664 switch (sdType) { 4665 default: 4666 break; 4667 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: { 4668 const auto& sdb = sd.get_SurfaceDescriptorBuffer(); 4669 const auto& data = sdb.data(); 4670 if (data.type() == layers::MemoryOrShmem::TShmem) { 4671 pShmem = &data.get_Shmem(); 4672 } else { 4673 return Some( 4674 std::string{"SurfaceDescriptorBuffer data is not Shmem."}); 4675 } 4676 } break; 4677 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10: { 4678 const auto& sdD3D = sd.get_SurfaceDescriptorD3D10(); 4679 const auto& inProcess = notLost->inProcess; 4680 MOZ_ASSERT(desc->image); 4681 keepAliveImage = desc->image; 4682 4683 if (sdD3D.gpuProcessTextureId().isSome() && inProcess) { 4684 return Some( 4685 std::string{"gpuProcessTextureId works only in GPU process."}); 4686 } 4687 } break; 4688 case layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo: { 4689 const auto& inProcess = notLost->inProcess; 4690 MOZ_ASSERT(desc->image); 4691 keepAliveImage = desc->image; 4692 if (inProcess) { 4693 return Some(std::string{ 4694 "SurfaceDescriptorGPUVideo works only in GPU process."}); 4695 } 4696 const auto& sdv = sd.get_SurfaceDescriptorGPUVideo(); 4697 if (sdv.type() != layers::SurfaceDescriptorGPUVideo:: 4698 TSurfaceDescriptorRemoteDecoder) { 4699 return Some(std::string{ 4700 "SurfaceDescriptorGPUVideo does not contain RemoteDecoder."}); 4701 } 4702 const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder(); 4703 const auto& subdesc = sdrd.subdesc(); 4704 if (subdesc.type() != 4705 layers::RemoteDecoderVideoSubDescriptor::Tnull_t) { 4706 return Some( 4707 std::string{"SurfaceDescriptorGPUVideo does not contain " 4708 "RemoteDecoder null subdesc."}); 4709 } 4710 } break; 4711 case layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage: { 4712 const auto& inProcess = notLost->inProcess; 4713 MOZ_ASSERT(desc->sourceSurf); 4714 keepAliveSurf = desc->sourceSurf; 4715 if (inProcess) { 4716 return Some(std::string{ 4717 "SurfaceDescriptorExternalImage works only in GPU process."}); 4718 } 4719 } break; 4720 case layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface: { 4721 const auto& inProcess = notLost->inProcess; 4722 MOZ_ASSERT(desc->sourceSurf); 4723 keepAliveSurf = desc->sourceSurf; 4724 if (inProcess) { 4725 return Some(std::string{ 4726 "SurfaceDescriptorCanvasSurface works only in GPU process."}); 4727 } 4728 } break; 4729 } 4730 4731 if (StaticPrefs::webgl_disable_DOM_blit_uploads()) { 4732 return Some(std::string{"DOM blit uploads are disabled."}); 4733 } 4734 return {}; 4735 }(); 4736 4737 if (fallbackReason) { 4738 EnqueuePerfWarning("Missed GPU-copy fast-path: %s", 4739 fallbackReason->c_str()); 4740 4741 const auto& image = desc->image; 4742 if (image) { 4743 const RefPtr<gfx::SourceSurface> surf = image->GetAsSourceSurface(); 4744 if (surf) { 4745 // WARNING: OSX can lose our MakeCurrent here. 4746 desc->sourceSurf = surf->GetDataSurface(); 4747 } 4748 } else if (desc->sourceSurf && !desc->sourceSurf->IsDataSourceSurface()) { 4749 // If the surface descriptor type was not supported, but there is an 4750 // underlying source surface, convert it to a data surface for fallback 4751 // usage. 4752 desc->sourceSurf = desc->sourceSurf->GetDataSurface(); 4753 } 4754 if (!desc->sourceSurf) { 4755 EnqueueError(LOCAL_GL_OUT_OF_MEMORY, 4756 "Failed to retrieve source bytes for CPU upload."); 4757 return; 4758 } 4759 desc->sd = Nothing(); 4760 } 4761 } 4762 desc->image = nullptr; 4763 if (desc->sd) { 4764 desc->sourceSurf = nullptr; 4765 } 4766 4767 desc->Shrink(pi); 4768 4769 // - 4770 4771 std::shared_ptr<webgl::RaiiShmem> tempShmem; 4772 4773 const bool doInlineUpload = !desc->sd; 4774 // Why always de-inline SDs here? 4775 // 1. This way we always send SDs down the same handling path, which 4776 // should keep things from breaking if things flip between paths because of 4777 // what we get handed by SurfaceFromElement etc. 4778 // 2. We don't actually always grab strong-refs to the resources in the SDs, 4779 // so we should try to use them sooner rather than later. Yes we should fix 4780 // this, but for now let's give the SDs the best chance of lucking out, eh? 4781 // :) 4782 // 3. It means we don't need to write QueueParamTraits<SurfaceDescriptor>. 4783 if (doInlineUpload) { 4784 // We definitely want e.g. TexImage(PBO) here. 4785 Run<RPROC(TexImage)>(static_cast<uint32_t>(level), respecFormat, 4786 CastUvec3(offset), pi, std::move(*desc)); 4787 } else { 4788 // We can't handle shmems like SurfaceDescriptorBuffer inline, so use ipdl. 4789 const auto& inProcess = notLost->inProcess; 4790 if (inProcess) { 4791 return inProcess->TexImage(static_cast<uint32_t>(level), respecFormat, 4792 CastUvec3(offset), pi, *desc); 4793 } 4794 const auto& child = notLost->outOfProcess; 4795 child->FlushPendingCmds(); 4796 4797 // The shmem we're handling was only shared from RDD to Content, and 4798 // immediately on Content receiving it, it was closed! RIP 4799 // Eventually we'll be able to make shmems that can traverse multiple 4800 // endpoints, but for now we need to make a new Content->WebGLParent shmem 4801 // and memcpy into it. We don't use `desc` elsewhere, so just replace the 4802 // Shmem buried within it with one that's valid for WebGLChild->Parent 4803 // transport. 4804 if (pShmem) { 4805 MOZ_ASSERT(desc->sd); 4806 const auto srcBytes = ShmemRange<uint8_t>(*pShmem); 4807 tempShmem = std::make_shared<webgl::RaiiShmem>(); 4808 4809 // We need Unsafe because we want to dictate when to destroy it from the 4810 // client side. 4811 *tempShmem = webgl::RaiiShmem::AllocUnsafe(child, srcBytes.length()); 4812 if (!*tempShmem) { 4813 NS_WARNING("AllocShmem failed in TexImage"); 4814 return; 4815 } 4816 const auto dstBytes = ShmemRange<uint8_t>(tempShmem->Shmem()); 4817 Memcpy(&dstBytes, srcBytes.begin()); 4818 4819 *pShmem = tempShmem->Shmem(); 4820 // Not Extract, because we free tempShmem manually below, after the remote 4821 // side has finished executing SendTexImage. 4822 } 4823 4824 (void)child->SendTexImage(static_cast<uint32_t>(level), respecFormat, 4825 CastUvec3(offset), pi, std::move(*desc)); 4826 4827 if (tempShmem || keepAliveImage || keepAliveSurf) { 4828 const auto eventTarget = GetCurrentSerialEventTarget(); 4829 MOZ_ASSERT(eventTarget); 4830 child->SendPing()->Then(eventTarget, __func__, 4831 [tempShmem, keepAliveImage, keepAliveSurf]() { 4832 // Cleans up when (our copy of) 4833 // sendableShmem/image goes out of scope. 4834 }); 4835 } 4836 } 4837 } 4838 4839 // - 4840 4841 void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims, 4842 GLenum imageTarget, GLint level, 4843 GLenum format, const ivec3& offset, 4844 const ivec3& isize, GLint border, 4845 const TexImageSource& src, 4846 GLsizei pboImageSize) const { 4847 const FuncScope funcScope(*this, "compressedTex(Sub)Image[23]D"); 4848 if (IsContextLost()) return; 4849 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget), mIsWebGL2, funcDims)) { 4850 EnqueueError_ArgEnum("imageTarget", imageTarget); 4851 return; 4852 } 4853 if (border != 0) { 4854 EnqueueError(LOCAL_GL_INVALID_VALUE, "`border` must be 0."); 4855 return; 4856 } 4857 4858 if (src.mView) { 4859 src.mView->ProcessData([&](const Span<uint8_t>& aData, 4860 JS::AutoCheckCannotGC&& aNoGC) { 4861 const auto range = 4862 GetRangeFromData(aData, SizeOfViewElem(*src.mView), 4863 src.mViewElemOffset, src.mViewElemLengthOverride); 4864 if (!range) { 4865 aNoGC.reset(); 4866 EnqueueError(LOCAL_GL_INVALID_VALUE, "`source` too small."); 4867 return; 4868 } 4869 4870 // We don't need to shrink `range` because valid calls require 4871 // `range` to match requirements exactly. 4872 4873 RunWithGCData<RPROC(CompressedTexImage)>( 4874 std::move(aNoGC), sub, imageTarget, static_cast<uint32_t>(level), 4875 format, CastUvec3(offset), CastUvec3(isize), *range, 4876 static_cast<uint32_t>(pboImageSize), Maybe<uint64_t>()); 4877 return; 4878 }); 4879 return; 4880 } 4881 if (!src.mPboOffset) { 4882 MOZ_CRASH("impossible"); 4883 } 4884 if (!ValidateNonNegative("offset", *src.mPboOffset)) { 4885 return; 4886 } 4887 4888 Run<RPROC(CompressedTexImage)>( 4889 sub, imageTarget, static_cast<uint32_t>(level), format, CastUvec3(offset), 4890 CastUvec3(isize), Span<const uint8_t>{}, 4891 static_cast<uint32_t>(pboImageSize), Some(*src.mPboOffset)); 4892 } 4893 4894 void ClientWebGLContext::CopyTexImage(uint8_t funcDims, GLenum imageTarget, 4895 GLint level, GLenum respecFormat, 4896 const ivec3& dstOffset, 4897 const ivec2& srcOffset, const ivec2& size, 4898 GLint border) const { 4899 const FuncScope funcScope(*this, "copy(Sub)Image[23]D"); 4900 if (IsContextLost()) return; 4901 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget), mIsWebGL2, funcDims)) { 4902 EnqueueError_ArgEnum("imageTarget", imageTarget); 4903 return; 4904 } 4905 if (border != 0) { 4906 EnqueueError(LOCAL_GL_INVALID_VALUE, "`border` must be 0."); 4907 return; 4908 } 4909 Run<RPROC(CopyTexImage)>(imageTarget, static_cast<uint32_t>(level), 4910 respecFormat, CastUvec3(dstOffset), srcOffset, 4911 CastUvec2(size)); 4912 } 4913 4914 // ------------------- Programs and shaders -------------------------------- 4915 4916 void ClientWebGLContext::UseProgram(WebGLProgramJS* const prog) { 4917 const FuncScope funcScope(*this, "useProgram"); 4918 if (IsContextLost()) return; 4919 if (prog && !prog->ValidateUsable(*this, "prog")) return; 4920 4921 auto& state = State(); 4922 4923 if (state.mTfActiveAndNotPaused) { 4924 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4925 "Transform feedback is active and not paused."); 4926 return; 4927 } 4928 4929 if (prog) { 4930 const auto& res = GetLinkResult(*prog); 4931 if (!res.success) { 4932 EnqueueError(LOCAL_GL_INVALID_OPERATION, 4933 "Program must be linked successfully."); 4934 return; 4935 } 4936 } 4937 4938 // - 4939 4940 state.mCurrentProgram = prog; 4941 state.mProgramKeepAlive = prog ? prog->mKeepAliveWeak.lock() : nullptr; 4942 state.mActiveLinkResult = prog ? prog->mResult : nullptr; 4943 4944 Run<RPROC(UseProgram)>(prog ? prog->mId : 0); 4945 } 4946 4947 void ClientWebGLContext::ValidateProgram(WebGLProgramJS& prog) const { 4948 RefPtr<webgl::NotLostData> notLost(mNotLost); 4949 if (!notLost) { 4950 return; 4951 } 4952 const FuncScope funcScope(*this, "validateProgram"); 4953 if (!prog.ValidateUsable(*this, "prog")) return; 4954 4955 prog.mLastValidate = [&]() { 4956 const auto& inProcess = notLost->inProcess; 4957 if (inProcess) { 4958 return inProcess->ValidateProgram(prog.mId); 4959 } 4960 const auto& child = notLost->outOfProcess; 4961 child->FlushPendingCmds(); 4962 bool ret = {}; 4963 if (!child->SendValidateProgram(prog.mId, &ret)) { 4964 ret = {}; 4965 } 4966 return ret; 4967 }(); 4968 } 4969 4970 // ------------------------ Uniforms and attributes ------------------------ 4971 4972 Maybe<double> ClientWebGLContext::GetVertexAttribPriv(const GLuint index, 4973 const GLenum pname) { 4974 RefPtr<webgl::NotLostData> notLost(mNotLost); 4975 if (!notLost) { 4976 return Nothing(); 4977 } 4978 const auto& inProcess = notLost->inProcess; 4979 if (inProcess) { 4980 return inProcess->GetVertexAttrib(index, pname); 4981 } 4982 const auto& child = notLost->outOfProcess; 4983 child->FlushPendingCmds(); 4984 Maybe<double> ret; 4985 if (!child->SendGetVertexAttrib(index, pname, &ret)) { 4986 ret.reset(); 4987 } 4988 return ret; 4989 } 4990 4991 void ClientWebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, 4992 GLenum pname, 4993 JS::MutableHandle<JS::Value> retval, 4994 ErrorResult& rv) { 4995 const FuncScope funcScope(*this, "getVertexAttrib"); 4996 retval.set(JS::NullValue()); 4997 RefPtr<webgl::NotLostData> notLost(mNotLost); 4998 if (!notLost) { 4999 return; 5000 } 5001 const auto& state = State(); 5002 5003 const auto& genericAttribs = state.mGenericVertexAttribs; 5004 if (index >= genericAttribs.size()) { 5005 EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` (%u) >= MAX_VERTEX_ATTRIBS", 5006 index); 5007 return; 5008 } 5009 5010 switch (pname) { 5011 case LOCAL_GL_CURRENT_VERTEX_ATTRIB: { 5012 const auto& attrib = genericAttribs[index]; 5013 switch (attrib.type) { 5014 case webgl::AttribBaseType::Float: { 5015 const auto ptr = reinterpret_cast<const float*>(attrib.data.data()); 5016 retval.setObjectOrNull( 5017 dom::Float32Array::Create(cx, this, Span(ptr, 4), rv)); 5018 break; 5019 } 5020 case webgl::AttribBaseType::Int: { 5021 const auto ptr = reinterpret_cast<const int32_t*>(attrib.data.data()); 5022 retval.setObjectOrNull( 5023 dom::Int32Array::Create(cx, this, Span(ptr, 4), rv)); 5024 break; 5025 } 5026 case webgl::AttribBaseType::Uint: { 5027 const auto ptr = 5028 reinterpret_cast<const uint32_t*>(attrib.data.data()); 5029 retval.setObjectOrNull( 5030 dom::Uint32Array::Create(cx, this, Span(ptr, 4), rv)); 5031 break; 5032 } 5033 case webgl::AttribBaseType::Boolean: 5034 MOZ_CRASH("impossible"); 5035 } 5036 5037 return; 5038 } 5039 5040 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: { 5041 const auto& buffers = state.mBoundVao->mAttribBuffers; 5042 const auto& buffer = buffers[index]; 5043 (void)ToJSValueOrNull(cx, buffer, retval); 5044 return; 5045 } 5046 5047 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER: 5048 // Disallowed from JS, but allowed in Host. 5049 EnqueueError_ArgEnum("pname", pname); 5050 return; 5051 5052 default: 5053 break; 5054 } 5055 5056 const auto maybe = GetVertexAttribPriv(index, pname); 5057 if (maybe) { 5058 switch (pname) { 5059 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED: 5060 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: 5061 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER: 5062 retval.set(JS::BooleanValue(*maybe)); 5063 break; 5064 5065 default: 5066 retval.set(JS::NumberValue(*maybe)); 5067 break; 5068 } 5069 } 5070 } 5071 5072 void ClientWebGLContext::UniformData(const GLenum funcElemType, 5073 const WebGLUniformLocationJS* const loc, 5074 bool transpose, 5075 const Range<const uint8_t>& bytes, 5076 JS::AutoCheckCannotGC&& nogc, 5077 GLuint elemOffset, 5078 GLuint elemCountOverride) const { 5079 // FuncScope::~FuncScope() can GC in a failure case, so all `return` 5080 // statements need to `nogc.reset()` up until the `nogc` is consumed by 5081 // `RunWithGCData`. 5082 const FuncScope funcScope(*this, "uniform setter"); 5083 if (IsContextLost()) { 5084 nogc.reset(); 5085 return; 5086 } 5087 5088 const auto& activeLinkResult = GetActiveLinkResult(); 5089 if (!activeLinkResult) { 5090 nogc.reset(); 5091 EnqueueError(LOCAL_GL_INVALID_OPERATION, "No active linked Program."); 5092 return; 5093 } 5094 5095 // - 5096 5097 auto availCount = bytes.length() / sizeof(float); 5098 if (elemOffset > availCount) { 5099 nogc.reset(); 5100 EnqueueError(LOCAL_GL_INVALID_VALUE, "`elemOffset` too large for `data`."); 5101 return; 5102 } 5103 availCount -= elemOffset; 5104 if (elemCountOverride) { 5105 if (elemCountOverride > availCount) { 5106 nogc.reset(); 5107 EnqueueError(LOCAL_GL_INVALID_VALUE, 5108 "`elemCountOverride` too large for `data`."); 5109 return; 5110 } 5111 availCount = elemCountOverride; 5112 } 5113 5114 // - 5115 5116 const auto channels = ElemTypeComponents(funcElemType); 5117 if (!availCount || availCount % channels != 0) { 5118 nogc.reset(); 5119 EnqueueError(LOCAL_GL_INVALID_VALUE, 5120 "`values` length (%u) must be a positive " 5121 "integer multiple of size of %s.", 5122 availCount, EnumString(funcElemType).c_str()); 5123 return; 5124 } 5125 5126 // - 5127 5128 uint32_t locId = -1; 5129 if (MOZ_LIKELY(loc)) { 5130 locId = loc->mLocation; 5131 if (!loc->ValidateUsable(*this, "location")) { 5132 nogc.reset(); 5133 return; 5134 } 5135 5136 // - 5137 if (loc->mParent != activeLinkResult) { 5138 nogc.reset(); 5139 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5140 "UniformLocation is not from the current active Program."); 5141 return; 5142 } 5143 5144 // - 5145 5146 bool funcMatchesLocation = false; 5147 for (const auto allowed : loc->mValidUploadElemTypes) { 5148 funcMatchesLocation |= (funcElemType == allowed); 5149 } 5150 if (MOZ_UNLIKELY(!funcMatchesLocation)) { 5151 std::string validSetters; 5152 for (const auto allowed : loc->mValidUploadElemTypes) { 5153 validSetters += EnumString(allowed); 5154 validSetters += '/'; 5155 } 5156 validSetters.pop_back(); // Cheekily discard the extra trailing '/'. 5157 5158 nogc.reset(); 5159 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5160 "Uniform's `type` requires uniform setter of type %s.", 5161 validSetters.c_str()); 5162 return; 5163 } 5164 } 5165 5166 // - 5167 5168 const auto begin = 5169 reinterpret_cast<const webgl::UniformDataVal*>(bytes.begin().get()) + 5170 elemOffset; 5171 const auto range = Span{begin, availCount}; 5172 RunWithGCData<RPROC(UniformData)>(std::move(nogc), locId, transpose, range); 5173 } 5174 5175 // - 5176 5177 void ClientWebGLContext::BindVertexArray(WebGLVertexArrayJS* const vao) { 5178 const FuncScope funcScope(*this, "bindVertexArray"); 5179 if (IsContextLost()) return; 5180 if (vao && !vao->ValidateUsable(*this, "vao")) return; 5181 auto& state = State(); 5182 5183 if (vao) { 5184 vao->mHasBeenBound = true; 5185 state.mBoundVao = vao; 5186 } else { 5187 state.mBoundVao = state.mDefaultVao; 5188 } 5189 5190 Run<RPROC(BindVertexArray)>(vao ? vao->mId : 0); 5191 } 5192 5193 void ClientWebGLContext::EnableVertexAttribArray(GLuint index) { 5194 Run<RPROC(EnableVertexAttribArray)>(index); 5195 } 5196 5197 void ClientWebGLContext::DisableVertexAttribArray(GLuint index) { 5198 Run<RPROC(DisableVertexAttribArray)>(index); 5199 } 5200 5201 WebGLsizeiptr ClientWebGLContext::GetVertexAttribOffset(GLuint index, 5202 GLenum pname) { 5203 const FuncScope funcScope(*this, "getVertexAttribOffset"); 5204 if (IsContextLost()) return 0; 5205 5206 if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) { 5207 EnqueueError_ArgEnum("pname", pname); 5208 return 0; 5209 } 5210 5211 const auto maybe = GetVertexAttribPriv(index, pname); 5212 if (!maybe) return 0; 5213 return *maybe; 5214 } 5215 5216 void ClientWebGLContext::VertexAttrib4Tv(GLuint index, webgl::AttribBaseType t, 5217 const Range<const uint8_t>& src) { 5218 const FuncScope funcScope(*this, "vertexAttrib[1234]u?[fi]{v}"); 5219 if (IsContextLost()) return; 5220 auto& state = State(); 5221 5222 if (src.length() / sizeof(float) < 4) { 5223 EnqueueError(LOCAL_GL_INVALID_VALUE, "Array must have >=4 elements."); 5224 return; 5225 } 5226 5227 auto& list = state.mGenericVertexAttribs; 5228 if (index >= list.size()) { 5229 EnqueueError(LOCAL_GL_INVALID_VALUE, 5230 "`index` must be < MAX_VERTEX_ATTRIBS."); 5231 return; 5232 } 5233 5234 auto& attrib = list[index]; 5235 attrib.type = t; 5236 memcpy(attrib.data.data(), src.begin().get(), attrib.data.size()); 5237 5238 Run<RPROC(VertexAttrib4T)>(index, attrib); 5239 } 5240 5241 // - 5242 5243 void ClientWebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor) { 5244 Run<RPROC(VertexAttribDivisor)>(index, divisor); 5245 } 5246 5247 // - 5248 5249 void ClientWebGLContext::VertexAttribPointerImpl(bool isFuncInt, GLuint index, 5250 GLint rawChannels, GLenum type, 5251 bool normalized, 5252 GLsizei rawByteStrideOrZero, 5253 WebGLintptr rawByteOffset) { 5254 const FuncScope funcScope(*this, "vertexAttribI?Pointer"); 5255 if (IsContextLost()) return; 5256 auto& state = State(); 5257 5258 const auto channels = MaybeAs<uint8_t>(rawChannels); 5259 if (!channels) { 5260 EnqueueError(LOCAL_GL_INVALID_VALUE, 5261 "Channel count `size` must be within [1,4]."); 5262 return; 5263 } 5264 5265 const auto byteStrideOrZero = MaybeAs<uint8_t>(rawByteStrideOrZero); 5266 if (!byteStrideOrZero) { 5267 EnqueueError(LOCAL_GL_INVALID_VALUE, "`stride` must be within [0,255]."); 5268 return; 5269 } 5270 5271 if (!ValidateNonNegative("byteOffset", rawByteOffset)) return; 5272 const auto byteOffset = static_cast<uint64_t>(rawByteOffset); 5273 5274 // - 5275 5276 const webgl::VertAttribPointerDesc desc{ 5277 isFuncInt, *channels, normalized, *byteStrideOrZero, type, byteOffset}; 5278 5279 const auto res = CheckVertexAttribPointer(mIsWebGL2, desc); 5280 if (res.isErr()) { 5281 const auto& err = res.inspectErr(); 5282 EnqueueError(err.type, "%s", err.info.c_str()); 5283 return; 5284 } 5285 5286 auto& list = state.mBoundVao->mAttribBuffers; 5287 if (index >= list.size()) { 5288 EnqueueError(LOCAL_GL_INVALID_VALUE, 5289 "`index` (%u) must be < MAX_VERTEX_ATTRIBS.", index); 5290 return; 5291 } 5292 5293 const auto buffer = state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER]; 5294 if (!buffer && byteOffset) { 5295 return EnqueueError(LOCAL_GL_INVALID_OPERATION, 5296 "If ARRAY_BUFFER is null, byteOffset must be zero."); 5297 } 5298 5299 Run<RPROC(VertexAttribPointer)>(index, desc); 5300 5301 list[index] = buffer; 5302 } 5303 5304 // -------------------------------- Drawing ------------------------------- 5305 5306 void ClientWebGLContext::DrawArraysInstanced(GLenum mode, GLint first, 5307 GLsizei count, GLsizei primcount) { 5308 Run<RPROC(DrawArraysInstanced)>(mode, first, count, primcount); 5309 AfterDrawCall(); 5310 } 5311 5312 void ClientWebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, 5313 GLenum type, WebGLintptr offset, 5314 GLsizei primcount) { 5315 Run<RPROC(DrawElementsInstanced)>(mode, count, type, offset, primcount); 5316 AfterDrawCall(); 5317 } 5318 5319 // ------------------------------ Readback ------------------------------- 5320 void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, 5321 GLsizei height, GLenum format, GLenum type, 5322 WebGLsizeiptr offset, 5323 dom::CallerType aCallerType, 5324 ErrorResult& out_error) const { 5325 const FuncScope funcScope(*this, "readPixels"); 5326 if (!ReadPixels_SharedPrecheck(&type, aCallerType, out_error)) return; 5327 const auto& state = State(); 5328 if (!ValidateNonNegative("width", width)) return; 5329 if (!ValidateNonNegative("height", height)) return; 5330 if (!ValidateNonNegative("offset", offset)) return; 5331 5332 const auto desc = webgl::ReadPixelsDesc{{x, y}, 5333 *uvec2::From(width, height), 5334 {format, type}, 5335 state.mPixelPackState}; 5336 Run<RPROC(ReadPixelsPbo)>(desc, static_cast<uint64_t>(offset)); 5337 } 5338 5339 void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, 5340 GLsizei height, GLenum format, GLenum type, 5341 const dom::ArrayBufferView& dstData, 5342 GLuint dstElemOffset, 5343 dom::CallerType aCallerType, 5344 ErrorResult& out_error) const { 5345 const FuncScope funcScope(*this, "readPixels"); 5346 if (!ReadPixels_SharedPrecheck(&type, aCallerType, out_error)) return; 5347 const auto& state = State(); 5348 if (!ValidateNonNegative("width", width)) return; 5349 if (!ValidateNonNegative("height", height)) return; 5350 5351 //// 5352 5353 js::Scalar::Type reqScalarType; 5354 if (!GetJSScalarFromGLType(type, &reqScalarType)) { 5355 nsCString name; 5356 WebGLContext::EnumName(type, &name); 5357 EnqueueError(LOCAL_GL_INVALID_ENUM, "type: invalid enum value %s", 5358 name.BeginReading()); 5359 return; 5360 } 5361 5362 auto viewElemType = dstData.Type(); 5363 if (viewElemType == js::Scalar::Uint8Clamped) { 5364 viewElemType = js::Scalar::Uint8; 5365 } 5366 if (viewElemType != reqScalarType) { 5367 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5368 "`pixels` type does not match `type`."); 5369 return; 5370 } 5371 5372 size_t elemSize = SizeOfViewElem(dstData); 5373 dstData.ProcessFixedData([&](const Span<uint8_t>& aData) { 5374 const auto& range = ValidateArrayBufferView(aData, elemSize, dstElemOffset, 5375 0, LOCAL_GL_INVALID_VALUE); 5376 if (!range) { 5377 return; 5378 } 5379 5380 const auto desc = webgl::ReadPixelsDesc{{x, y}, 5381 *uvec2::From(width, height), 5382 {format, type}, 5383 state.mPixelPackState}; 5384 bool succeeded = DoReadPixels(desc, *range); 5385 if (succeeded) { 5386 CanvasUtils::ImageExtraction extraction = 5387 ImageExtractionResult(mCanvasElement, mOffscreenCanvas); 5388 5389 if (extraction == CanvasUtils::ImageExtraction::Placeholder) { 5390 dom::GeneratePlaceholderCanvasData(range->size(), range->Elements()); 5391 } else { 5392 RecordCanvasUsage(CanvasExtractionAPI::ReadPixels, 5393 CSSIntSize(width, height)); 5394 if (extraction == CanvasUtils::ImageExtraction::Randomize) { 5395 const auto pii = webgl::PackingInfoInfo::For(desc.pi); 5396 // DoReadPixels() requres pii to be Some(). 5397 MOZ_ASSERT(pii.isSome()); 5398 5399 // With WebGL, the alpha channel is always the last element (if it 5400 // exists) in the pixel. With nsRFPService::RandomizeElements, we do 5401 // random % (pii->elementsPerPixel - 1) + offset to get the channel 5402 // we want to randomize. With the offset being 0, we avoid the last 5403 // element, which is the alpha channel. 5404 // If WebGL had ARGB or some other format where the alpha channel 5405 // was not the last element, we would need to adjust the offset. 5406 constexpr uint8_t alphaChannelOffset = 0; 5407 bool hasAlphaChannel = 5408 format == LOCAL_GL_SRGB_ALPHA || format == LOCAL_GL_RGBA || 5409 format == LOCAL_GL_BGRA || format == LOCAL_GL_LUMINANCE_ALPHA; 5410 nsRFPService::RandomizeElements( 5411 GetCookieJarSettings(), PrincipalOrNull(), range->data(), 5412 range->size_bytes(), pii->elementsPerPixel, pii->bytesPerElement, 5413 alphaChannelOffset, hasAlphaChannel); 5414 } 5415 } 5416 } 5417 }); 5418 } 5419 5420 bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc& desc, 5421 const Span<uint8_t> dest) const { 5422 RefPtr<webgl::NotLostData> notLost(mNotLost); 5423 if (!notLost) { 5424 return false; 5425 } 5426 const auto& inProcess = notLost->inProcess; 5427 if (inProcess) { 5428 inProcess->ReadPixelsInto(desc, dest); 5429 return true; 5430 } 5431 const auto& child = notLost->outOfProcess; 5432 child->FlushPendingCmds(); 5433 webgl::ReadPixelsResultIpc res = {}; 5434 if (!child->SendReadPixels(desc, dest.size(), &res)) { 5435 res = {}; 5436 } 5437 if (!res.byteStride || !res.shmem) return false; 5438 const auto& byteStride = res.byteStride; 5439 const auto& subrect = res.subrect; 5440 const webgl::RaiiShmem shmem{child, res.shmem.ref()}; 5441 if (!shmem) { 5442 EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "Failed to map in back buffer."); 5443 return false; 5444 } 5445 5446 const auto& shmemBytes = Span{shmem.ByteRange()}; 5447 5448 const auto pii = webgl::PackingInfoInfo::For(desc.pi); 5449 if (!pii) { 5450 gfxCriticalError() << "ReadPixels: Bad " << desc.pi; 5451 return false; 5452 } 5453 const auto bpp = pii->BytesPerPixel(); 5454 5455 const auto& packing = desc.packState; 5456 auto packRect = *uvec2::From(subrect.x, subrect.y); 5457 packRect.x += packing.skipPixels; 5458 packRect.y += packing.skipRows; 5459 5460 const auto xByteSize = bpp * static_cast<uint32_t>(subrect.width); 5461 const ptrdiff_t byteOffset = packRect.y * byteStride + packRect.x * bpp; 5462 5463 const auto srcSubrect = shmemBytes.subspan(byteOffset); 5464 const auto destSubrect = dest.subspan(byteOffset); 5465 5466 for (const auto i : IntegerRange(subrect.height)) { 5467 const auto srcRow = srcSubrect.subspan(i * byteStride, xByteSize); 5468 const auto destRow = destSubrect.subspan(i * byteStride, xByteSize); 5469 Memcpy(&destRow, srcRow); 5470 } 5471 5472 return true; 5473 } 5474 5475 bool ClientWebGLContext::ReadPixels_SharedPrecheck( 5476 GLenum* const inout_readType, dom::CallerType aCallerType, 5477 ErrorResult& out_error) const { 5478 if (IsContextLost()) return false; 5479 5480 GLenum validHalfFloatType = LOCAL_GL_HALF_FLOAT; 5481 GLenum forbiddenHalfFloatType = LOCAL_GL_HALF_FLOAT_OES; 5482 if (!mIsWebGL2) { 5483 std::swap(validHalfFloatType, forbiddenHalfFloatType); // Tragic. 5484 } 5485 if (*inout_readType == forbiddenHalfFloatType) { 5486 const auto msg = fmt::format( 5487 FMT_STRING("For WebGL {}, for `type`, enum {} is forbidden. Use {}."), 5488 mIsWebGL2 ? "2" : "1", EnumString(forbiddenHalfFloatType), 5489 EnumString(validHalfFloatType)); 5490 EnqueueError({LOCAL_GL_INVALID_ENUM, msg}); 5491 return false; 5492 } 5493 // Normalize to HALF_FLOAT non-_OES internally: 5494 if (*inout_readType == LOCAL_GL_HALF_FLOAT_OES) { 5495 *inout_readType = LOCAL_GL_HALF_FLOAT; 5496 } 5497 5498 if (mCanvasElement && mCanvasElement->IsWriteOnly() && 5499 aCallerType != dom::CallerType::System) { 5500 JsWarning("readPixels: Not allowed"); 5501 out_error.Throw(NS_ERROR_DOM_SECURITY_ERR); 5502 return false; 5503 } 5504 5505 // Security check passed, but don't let content readPixel calls through for 5506 // now, if Resist Fingerprinting Mode is enabled. 5507 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { 5508 JsWarning("readPixels: Not allowed in Resist Fingerprinting Mode"); 5509 out_error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 5510 return false; 5511 } 5512 5513 return true; 5514 } 5515 5516 // --------------------------------- GL Query --------------------------------- 5517 5518 static inline GLenum QuerySlotTarget(const GLenum specificTarget) { 5519 if (specificTarget == LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE) { 5520 return LOCAL_GL_ANY_SAMPLES_PASSED; 5521 } 5522 return specificTarget; 5523 } 5524 5525 void ClientWebGLContext::GetQuery(JSContext* cx, GLenum specificTarget, 5526 GLenum pname, 5527 JS::MutableHandle<JS::Value> retval) const { 5528 const FuncScope funcScope(*this, "getQuery"); 5529 retval.set(JS::NullValue()); 5530 RefPtr<webgl::NotLostData> notLost(mNotLost); 5531 if (!notLost) { 5532 return; 5533 } 5534 const auto& limits = Limits(); 5535 auto& state = State(); 5536 5537 if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { 5538 if (pname == LOCAL_GL_QUERY_COUNTER_BITS) { 5539 switch (specificTarget) { 5540 case LOCAL_GL_TIME_ELAPSED_EXT: 5541 retval.set(JS::NumberValue(limits.queryCounterBitsTimeElapsed)); 5542 return; 5543 5544 case LOCAL_GL_TIMESTAMP_EXT: 5545 retval.set(JS::NumberValue(limits.queryCounterBitsTimestamp)); 5546 return; 5547 5548 default: 5549 EnqueueError_ArgEnum("target", specificTarget); 5550 return; 5551 } 5552 } 5553 } 5554 5555 if (pname != LOCAL_GL_CURRENT_QUERY) { 5556 EnqueueError_ArgEnum("pname", pname); 5557 return; 5558 } 5559 5560 const auto slotTarget = QuerySlotTarget(specificTarget); 5561 const auto& slot = MaybeFind(state.mCurrentQueryByTarget, slotTarget); 5562 if (!slot) { 5563 EnqueueError_ArgEnum("target", specificTarget); 5564 return; 5565 } 5566 5567 auto query = *slot; 5568 if (query && query->mTarget != specificTarget) { 5569 query = nullptr; 5570 } 5571 5572 (void)ToJSValueOrNull(cx, query, retval); 5573 } 5574 5575 void ClientWebGLContext::GetQueryParameter( 5576 JSContext*, WebGLQueryJS& query, const GLenum pname, 5577 JS::MutableHandle<JS::Value> retval) const { 5578 const FuncScope funcScope(*this, "getQueryParameter"); 5579 retval.set(JS::NullValue()); 5580 RefPtr<webgl::NotLostData> notLost(mNotLost); 5581 if (!notLost) { 5582 return; 5583 } 5584 if (!query.ValidateUsable(*this, "query")) return; 5585 5586 auto maybe = [&]() { 5587 const auto& inProcess = notLost->inProcess; 5588 if (inProcess) { 5589 return inProcess->GetQueryParameter(query.mId, pname); 5590 } 5591 const auto& child = notLost->outOfProcess; 5592 child->FlushPendingCmds(); 5593 Maybe<double> ret; 5594 if (!child->SendGetQueryParameter(query.mId, pname, &ret)) { 5595 ret.reset(); 5596 } 5597 return ret; 5598 }(); 5599 if (!maybe) return; 5600 5601 // We must usually wait for an event loop before the query can be available. 5602 const bool canBeAvailable = 5603 (query.mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries()); 5604 if (!canBeAvailable) { 5605 if (pname != LOCAL_GL_QUERY_RESULT_AVAILABLE) { 5606 return; 5607 } 5608 maybe = Some(0.0); 5609 } 5610 5611 switch (pname) { 5612 case LOCAL_GL_QUERY_RESULT_AVAILABLE: 5613 retval.set(JS::BooleanValue(*maybe)); 5614 break; 5615 5616 default: 5617 retval.set(JS::NumberValue(*maybe)); 5618 break; 5619 } 5620 } 5621 5622 void ClientWebGLContext::BeginQuery(const GLenum specificTarget, 5623 WebGLQueryJS& query) { 5624 const FuncScope funcScope(*this, "beginQuery"); 5625 if (IsContextLost()) return; 5626 if (!query.ValidateUsable(*this, "query")) return; 5627 auto& state = State(); 5628 5629 const auto slotTarget = QuerySlotTarget(specificTarget); 5630 const auto& slot = MaybeFind(state.mCurrentQueryByTarget, slotTarget); 5631 if (!slot) { 5632 EnqueueError_ArgEnum("target", specificTarget); 5633 return; 5634 } 5635 5636 if (*slot) { 5637 auto enumStr = EnumString(slotTarget); 5638 if (slotTarget == LOCAL_GL_ANY_SAMPLES_PASSED) { 5639 enumStr += "/ANY_SAMPLES_PASSED_CONSERVATIVE"; 5640 } 5641 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5642 "A Query is already active for %s.", enumStr.c_str()); 5643 return; 5644 } 5645 5646 if (query.mTarget && query.mTarget != specificTarget) { 5647 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5648 "`query` cannot be changed to a different target."); 5649 return; 5650 } 5651 5652 *slot = &query; 5653 query.mTarget = specificTarget; 5654 5655 Run<RPROC(BeginQuery)>(specificTarget, query.mId); 5656 } 5657 5658 void ClientWebGLContext::EndQuery(const GLenum specificTarget) { 5659 const FuncScope funcScope(*this, "endQuery"); 5660 if (IsContextLost()) return; 5661 auto& state = State(); 5662 5663 const auto slotTarget = QuerySlotTarget(specificTarget); 5664 const auto& maybeSlot = MaybeFind(state.mCurrentQueryByTarget, slotTarget); 5665 if (!maybeSlot) { 5666 EnqueueError_ArgEnum("target", specificTarget); 5667 return; 5668 } 5669 auto& slot = *maybeSlot; 5670 if (!slot || slot->mTarget != specificTarget) { 5671 EnqueueError(LOCAL_GL_INVALID_OPERATION, "No Query is active for %s.", 5672 EnumString(specificTarget).c_str()); 5673 return; 5674 } 5675 const auto query = slot; 5676 slot = nullptr; 5677 5678 Run<RPROC(EndQuery)>(specificTarget); 5679 5680 auto& availRunnable = EnsureAvailabilityRunnable(); 5681 availRunnable.mQueries.push_back(query.get()); 5682 query->mCanBeAvailable = false; 5683 } 5684 5685 void ClientWebGLContext::QueryCounter(WebGLQueryJS& query, 5686 const GLenum target) const { 5687 const FuncScope funcScope(*this, "queryCounter"); 5688 if (IsContextLost()) return; 5689 if (!query.ValidateUsable(*this, "query")) return; 5690 5691 if (target != LOCAL_GL_TIMESTAMP) { 5692 EnqueueError(LOCAL_GL_INVALID_ENUM, "`target` must be TIMESTAMP."); 5693 return; 5694 } 5695 5696 if (query.mTarget && query.mTarget != target) { 5697 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5698 "`query` cannot be changed to a different target."); 5699 return; 5700 } 5701 query.mTarget = target; 5702 5703 Run<RPROC(QueryCounter)>(query.mId); 5704 5705 auto& availRunnable = EnsureAvailabilityRunnable(); 5706 availRunnable.mQueries.push_back(&query); 5707 query.mCanBeAvailable = false; 5708 } 5709 5710 // -------------------------------- Sampler ------------------------------- 5711 void ClientWebGLContext::GetSamplerParameter( 5712 JSContext* cx, const WebGLSamplerJS& sampler, const GLenum pname, 5713 JS::MutableHandle<JS::Value> retval) const { 5714 const FuncScope funcScope(*this, "getSamplerParameter"); 5715 retval.set(JS::NullValue()); 5716 RefPtr<webgl::NotLostData> notLost(mNotLost); 5717 if (!notLost) { 5718 return; 5719 } 5720 if (!sampler.ValidateUsable(*this, "sampler")) return; 5721 5722 const auto maybe = [&]() { 5723 const auto& inProcess = notLost->inProcess; 5724 if (inProcess) { 5725 return inProcess->GetSamplerParameter(sampler.mId, pname); 5726 } 5727 const auto& child = notLost->outOfProcess; 5728 child->FlushPendingCmds(); 5729 Maybe<double> ret; 5730 if (!child->SendGetSamplerParameter(sampler.mId, pname, &ret)) { 5731 ret.reset(); 5732 } 5733 return ret; 5734 }(); 5735 if (maybe) { 5736 retval.set(JS::NumberValue(*maybe)); 5737 } 5738 } 5739 5740 void ClientWebGLContext::BindSampler(const GLuint unit, 5741 WebGLSamplerJS* const sampler) { 5742 const FuncScope funcScope(*this, "bindSampler"); 5743 if (IsContextLost()) return; 5744 if (sampler && !sampler->ValidateUsable(*this, "sampler")) return; 5745 auto& state = State(); 5746 5747 auto& texUnits = state.mTexUnits; 5748 if (unit >= texUnits.size()) { 5749 EnqueueError(LOCAL_GL_INVALID_VALUE, "`unit` (%u) larger than %zu.", unit, 5750 texUnits.size()); 5751 return; 5752 } 5753 5754 // - 5755 5756 texUnits[unit].sampler = sampler; 5757 5758 Run<RPROC(BindSampler)>(unit, sampler ? sampler->mId : 0); 5759 } 5760 5761 void ClientWebGLContext::SamplerParameteri(WebGLSamplerJS& sampler, 5762 const GLenum pname, 5763 const GLint param) const { 5764 const FuncScope funcScope(*this, "samplerParameteri"); 5765 if (IsContextLost()) return; 5766 if (!sampler.ValidateUsable(*this, "sampler")) return; 5767 5768 Run<RPROC(SamplerParameteri)>(sampler.mId, pname, param); 5769 } 5770 5771 void ClientWebGLContext::SamplerParameterf(WebGLSamplerJS& sampler, 5772 const GLenum pname, 5773 const GLfloat param) const { 5774 const FuncScope funcScope(*this, "samplerParameterf"); 5775 if (IsContextLost()) return; 5776 if (!sampler.ValidateUsable(*this, "sampler")) return; 5777 5778 Run<RPROC(SamplerParameterf)>(sampler.mId, pname, param); 5779 } 5780 5781 // ------------------------------- GL Sync --------------------------------- 5782 5783 void ClientWebGLContext::GetSyncParameter( 5784 JSContext* const cx, WebGLSyncJS& sync, const GLenum pname, 5785 JS::MutableHandle<JS::Value> retval) const { 5786 const FuncScope funcScope(*this, "getSyncParameter"); 5787 retval.set(JS::NullValue()); 5788 RefPtr<webgl::NotLostData> notLost(mNotLost); 5789 if (!notLost) { 5790 return; 5791 } 5792 if (!sync.ValidateUsable(*this, "sync")) return; 5793 5794 retval.set([&]() -> JS::Value { 5795 switch (pname) { 5796 case LOCAL_GL_OBJECT_TYPE: 5797 return JS::NumberValue(LOCAL_GL_SYNC_FENCE); 5798 case LOCAL_GL_SYNC_CONDITION: 5799 return JS::NumberValue(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE); 5800 case LOCAL_GL_SYNC_FLAGS: 5801 return JS::NumberValue(0); 5802 case LOCAL_GL_SYNC_STATUS: { 5803 const auto res = ClientWaitSync(sync, 0, 0); 5804 const auto signaled = (res == LOCAL_GL_ALREADY_SIGNALED || 5805 res == LOCAL_GL_CONDITION_SATISFIED); 5806 return JS::NumberValue(signaled ? LOCAL_GL_SIGNALED 5807 : LOCAL_GL_UNSIGNALED); 5808 } 5809 default: 5810 EnqueueError_ArgEnum("pname", pname); 5811 return JS::NullValue(); 5812 } 5813 }()); 5814 } 5815 5816 // - 5817 5818 GLenum ClientWebGLContext::ClientWaitSync(WebGLSyncJS& sync, 5819 const GLbitfield flags, 5820 const GLuint64 timeout) const { 5821 RefPtr<webgl::NotLostData> notLost(mNotLost); 5822 if (!notLost) { 5823 return LOCAL_GL_WAIT_FAILED; 5824 } 5825 const FuncScope funcScope(*this, "clientWaitSync"); 5826 if (!sync.ValidateUsable(*this, "sync")) return LOCAL_GL_WAIT_FAILED; 5827 5828 static constexpr auto VALID_BITS = LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT; 5829 if ((flags | VALID_BITS) != VALID_BITS) { 5830 EnqueueError(LOCAL_GL_INVALID_VALUE, 5831 "`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0."); 5832 return LOCAL_GL_WAIT_FAILED; 5833 } 5834 5835 if (timeout > webgl::kMaxClientWaitSyncTimeoutNS) { 5836 EnqueueError( 5837 LOCAL_GL_INVALID_OPERATION, 5838 "`timeout` (%sns) must be less than MAX_CLIENT_WAIT_TIMEOUT_WEBGL " 5839 "(%sns).", 5840 ToStringWithCommas(timeout).c_str(), 5841 ToStringWithCommas(webgl::kMaxClientWaitSyncTimeoutNS).c_str()); 5842 return LOCAL_GL_WAIT_FAILED; 5843 } 5844 5845 const bool canBeAvailable = 5846 (sync.mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries()); 5847 if (!canBeAvailable) { 5848 constexpr uint8_t WARN_AT = 100; 5849 if (sync.mNumQueriesBeforeFirstFrameBoundary <= WARN_AT) { 5850 sync.mNumQueriesBeforeFirstFrameBoundary += 1; 5851 if (sync.mNumQueriesBeforeFirstFrameBoundary == WARN_AT) { 5852 EnqueueWarning( 5853 "ClientWaitSync must return TIMEOUT_EXPIRED until control has" 5854 " returned to the user agent's main loop, but was polled %hhu " 5855 "times. Are you spin-locking? (only warns once)", 5856 sync.mNumQueriesBeforeFirstFrameBoundary); 5857 } 5858 } 5859 return LOCAL_GL_TIMEOUT_EXPIRED; 5860 } 5861 5862 if (mCompletedSyncId >= sync.mId) { 5863 return LOCAL_GL_ALREADY_SIGNALED; 5864 } 5865 if (flags & LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) { 5866 Flush(); 5867 } else { 5868 constexpr uint8_t WARN_AT = 100; 5869 if (sync.mNumQueriesWithoutFlushCommandsBit <= WARN_AT) { 5870 sync.mNumQueriesWithoutFlushCommandsBit += 1; 5871 if (sync.mNumQueriesWithoutFlushCommandsBit == WARN_AT) { 5872 EnqueueWarning( 5873 "ClientWaitSync with timeout=0 (or GetSyncParameter(SYNC_STATUS)) " 5874 "called %hhu times without SYNC_FLUSH_COMMANDS_BIT. If you do not " 5875 "flush, this sync object is not guaranteed to ever complete.", 5876 sync.mNumQueriesWithoutFlushCommandsBit); 5877 } 5878 } 5879 } 5880 if (!timeout) return LOCAL_GL_TIMEOUT_EXPIRED; 5881 5882 // - 5883 // Fine, time to block: 5884 5885 const auto ret = [&]() { 5886 const auto& inProcess = notLost->inProcess; 5887 if (inProcess) { 5888 return inProcess->ClientWaitSync(sync.mId, flags, timeout); 5889 } 5890 const auto& child = notLost->outOfProcess; 5891 child->FlushPendingCmds(); 5892 GLenum ret = {}; 5893 if (!child->SendClientWaitSync(sync.mId, flags, timeout, &ret)) { 5894 ret = {}; 5895 } 5896 return ret; 5897 }(); 5898 5899 switch (ret) { 5900 case LOCAL_GL_CONDITION_SATISFIED: 5901 case LOCAL_GL_ALREADY_SIGNALED: 5902 OnSyncComplete(sync.mId); 5903 break; 5904 } 5905 5906 return ret; 5907 } 5908 5909 void ClientWebGLContext::WaitSync(const WebGLSyncJS& sync, 5910 const GLbitfield flags, 5911 const GLint64 timeout) const { 5912 const FuncScope funcScope(*this, "waitSync"); 5913 if (IsContextLost()) return; 5914 if (!sync.ValidateUsable(*this, "sync")) return; 5915 5916 if (flags != 0) { 5917 EnqueueError(LOCAL_GL_INVALID_VALUE, "`flags` must be 0."); 5918 return; 5919 } 5920 if (timeout != -1) { 5921 EnqueueError(LOCAL_GL_INVALID_VALUE, "`timeout` must be TIMEOUT_IGNORED."); 5922 return; 5923 } 5924 5925 JsWarning("waitSync is a no-op."); 5926 } 5927 5928 // -------------------------- Transform Feedback --------------------------- 5929 5930 void ClientWebGLContext::BindTransformFeedback( 5931 const GLenum target, WebGLTransformFeedbackJS* const tf) { 5932 const FuncScope funcScope(*this, "bindTransformFeedback"); 5933 if (IsContextLost()) return; 5934 if (tf && !tf->ValidateUsable(*this, "tf")) return; 5935 auto& state = State(); 5936 5937 if (target != LOCAL_GL_TRANSFORM_FEEDBACK) { 5938 EnqueueError(LOCAL_GL_INVALID_ENUM, "`target` must be TRANSFORM_FEEDBACK."); 5939 return; 5940 } 5941 if (state.mTfActiveAndNotPaused) { 5942 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5943 "Current Transform Feedback object is active and not paused."); 5944 return; 5945 } 5946 5947 if (tf) { 5948 tf->mHasBeenBound = true; 5949 state.mBoundTfo = tf; 5950 } else { 5951 state.mBoundTfo = state.mDefaultTfo; 5952 } 5953 5954 Run<RPROC(BindTransformFeedback)>(tf ? tf->mId : 0); 5955 } 5956 5957 void ClientWebGLContext::BeginTransformFeedback(const GLenum primMode) { 5958 const FuncScope funcScope(*this, "beginTransformFeedback"); 5959 if (IsContextLost()) return; 5960 auto& state = State(); 5961 auto& tfo = *(state.mBoundTfo); 5962 5963 if (tfo.mActiveOrPaused) { 5964 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5965 "Transform Feedback is already active or paused."); 5966 return; 5967 } 5968 MOZ_ASSERT(!state.mTfActiveAndNotPaused); 5969 5970 auto& prog = state.mCurrentProgram; 5971 if (!prog) { 5972 EnqueueError(LOCAL_GL_INVALID_OPERATION, "No program in use."); 5973 return; 5974 } 5975 const auto& linkResult = GetLinkResult(*prog); 5976 if (!linkResult.success) { 5977 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5978 "Program is not successfully linked."); 5979 return; 5980 } 5981 5982 auto tfBufferCount = linkResult.active.activeTfVaryings.size(); 5983 if (tfBufferCount && 5984 linkResult.tfBufferMode == LOCAL_GL_INTERLEAVED_ATTRIBS) { 5985 tfBufferCount = 1; 5986 } 5987 if (!tfBufferCount) { 5988 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5989 "Program does not use Transform Feedback."); 5990 return; 5991 } 5992 5993 const auto& buffers = tfo.mAttribBuffers; 5994 for (const auto i : IntegerRange(tfBufferCount)) { 5995 if (!buffers[i]) { 5996 EnqueueError(LOCAL_GL_INVALID_OPERATION, 5997 "Transform Feedback buffer %u is null.", i); 5998 return; 5999 } 6000 } 6001 6002 switch (primMode) { 6003 case LOCAL_GL_POINTS: 6004 case LOCAL_GL_LINES: 6005 case LOCAL_GL_TRIANGLES: 6006 break; 6007 default: 6008 EnqueueError(LOCAL_GL_INVALID_ENUM, 6009 "`primitiveMode` must be POINTS, LINES< or TRIANGLES."); 6010 return; 6011 } 6012 6013 // - 6014 6015 tfo.mActiveOrPaused = true; 6016 tfo.mActiveProgram = prog; 6017 tfo.mActiveProgramKeepAlive = prog->mKeepAliveWeak.lock(); 6018 prog->mActiveTfos.insert(&tfo); 6019 state.mTfActiveAndNotPaused = true; 6020 6021 Run<RPROC(BeginTransformFeedback)>(primMode); 6022 } 6023 6024 void ClientWebGLContext::EndTransformFeedback() { 6025 const FuncScope funcScope(*this, "endTransformFeedback"); 6026 if (IsContextLost()) return; 6027 auto& state = State(); 6028 auto& tfo = *(state.mBoundTfo); 6029 6030 if (!tfo.mActiveOrPaused) { 6031 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6032 "Transform Feedback is not active or paused."); 6033 return; 6034 } 6035 6036 tfo.mActiveOrPaused = false; 6037 tfo.mActiveProgram->mActiveTfos.erase(&tfo); 6038 tfo.mActiveProgram = nullptr; 6039 tfo.mActiveProgramKeepAlive = nullptr; 6040 state.mTfActiveAndNotPaused = false; 6041 Run<RPROC(EndTransformFeedback)>(); 6042 } 6043 6044 void ClientWebGLContext::PauseTransformFeedback() { 6045 const FuncScope funcScope(*this, "pauseTransformFeedback"); 6046 if (IsContextLost()) return; 6047 auto& state = State(); 6048 auto& tfo = *(state.mBoundTfo); 6049 6050 if (!tfo.mActiveOrPaused) { 6051 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6052 "Transform Feedback is not active."); 6053 return; 6054 } 6055 if (!state.mTfActiveAndNotPaused) { 6056 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6057 "Transform Feedback is already paused."); 6058 return; 6059 } 6060 6061 state.mTfActiveAndNotPaused = false; 6062 Run<RPROC(PauseTransformFeedback)>(); 6063 } 6064 6065 void ClientWebGLContext::ResumeTransformFeedback() { 6066 const FuncScope funcScope(*this, "resumeTransformFeedback"); 6067 if (IsContextLost()) return; 6068 auto& state = State(); 6069 auto& tfo = *(state.mBoundTfo); 6070 6071 if (!tfo.mActiveOrPaused) { 6072 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6073 "Transform Feedback is not active and paused."); 6074 return; 6075 } 6076 if (state.mTfActiveAndNotPaused) { 6077 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6078 "Transform Feedback is not paused."); 6079 return; 6080 } 6081 if (state.mCurrentProgram != tfo.mActiveProgram) { 6082 EnqueueError( 6083 LOCAL_GL_INVALID_OPERATION, 6084 "Cannot Resume Transform Feedback with a program link result different" 6085 " from when Begin was called."); 6086 return; 6087 } 6088 6089 state.mTfActiveAndNotPaused = true; 6090 Run<RPROC(ResumeTransformFeedback)>(); 6091 } 6092 6093 void ClientWebGLContext::SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS* fb, 6094 bool value) { 6095 fb->mInOpaqueRAF = value; 6096 Run<RPROC(SetFramebufferIsInOpaqueRAF)>(fb->mId, value); 6097 } 6098 6099 // ---------------------------- Misc Extensions ---------------------------- 6100 void ClientWebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers) { 6101 const auto range = MakeRange(buffers); 6102 const auto vec = std::vector<GLenum>(range.begin().get(), range.end().get()); 6103 Run<RPROC(DrawBuffers)>(vec); 6104 } 6105 6106 void ClientWebGLContext::EnqueueErrorImpl(const GLenum error, 6107 const nsACString& text) const { 6108 if (IsContextLost()) return; 6109 AutoEnqueueFlush(); 6110 Run<RPROC(GenerateError)>(error, ToString(text)); 6111 } 6112 6113 void ClientWebGLContext::RequestExtension(const WebGLExtensionID ext) const { 6114 Run<RPROC(RequestExtension)>(ext); 6115 } 6116 6117 // - 6118 6119 bool ClientWebGLContext::IsExtensionForbiddenForCaller( 6120 const WebGLExtensionID ext, const dom::CallerType callerType) const { 6121 if (callerType == dom::CallerType::System) { 6122 return false; 6123 } 6124 6125 if (StaticPrefs::webgl_enable_privileged_extensions()) { 6126 return false; 6127 } 6128 6129 switch (ext) { 6130 case WebGLExtensionID::MOZ_debug: 6131 return true; 6132 6133 case WebGLExtensionID::WEBGL_debug_renderer_info: 6134 return !StaticPrefs::webgl_enable_debug_renderer_info(); 6135 6136 case WebGLExtensionID::WEBGL_debug_shaders: 6137 return ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo); 6138 6139 default: 6140 return false; 6141 } 6142 } 6143 6144 bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext, 6145 const dom::CallerType callerType) const { 6146 if (IsExtensionForbiddenForCaller(ext, callerType)) { 6147 return false; 6148 } 6149 6150 const auto& limits = Limits(); 6151 return limits.supportedExtensions[ext]; 6152 } 6153 6154 void ClientWebGLContext::GetSupportedExtensions( 6155 dom::Nullable<nsTArray<nsString>>& retval, 6156 const dom::CallerType callerType) const { 6157 retval.SetNull(); 6158 if (IsContextLost()) return; 6159 6160 auto& retarr = retval.SetValue(); 6161 for (const auto i : MakeEnumeratedRange(WebGLExtensionID::Max)) { 6162 if (!IsSupported(i, callerType)) continue; 6163 6164 const auto& extStr = GetExtensionName(i); 6165 retarr.AppendElement(NS_ConvertUTF8toUTF16(extStr)); 6166 } 6167 } 6168 6169 // - 6170 6171 void ClientWebGLContext::GetSupportedProfilesASTC( 6172 dom::Nullable<nsTArray<nsString>>& retval) const { 6173 retval.SetNull(); 6174 if (IsContextLost()) return; 6175 const auto& limits = Limits(); 6176 6177 auto& retarr = retval.SetValue(); 6178 retarr.AppendElement(u"ldr"_ns); 6179 if (limits.astcHdr) { 6180 retarr.AppendElement(u"hdr"_ns); 6181 } 6182 } 6183 6184 void ClientWebGLContext::ProvokingVertex(const GLenum rawMode) const { 6185 const FuncScope funcScope(*this, "provokingVertex"); 6186 if (IsContextLost()) return; 6187 6188 const auto mode = AsEnumCase<webgl::ProvokingVertex>(rawMode); 6189 if (!mode) { 6190 EnqueueError_ArgEnum("mode", rawMode); 6191 return; 6192 } 6193 6194 Run<RPROC(ProvokingVertex)>(*mode); 6195 6196 funcScope.mKeepNotLostOrNull->state.mProvokingVertex = *mode; 6197 } 6198 6199 // - 6200 6201 uint32_t ClientWebGLContext::GetPrincipalHashValue() const { 6202 if (mCanvasElement) { 6203 return mCanvasElement->NodePrincipal()->GetHashValue(); 6204 } 6205 if (mOffscreenCanvas) { 6206 nsIGlobalObject* global = mOffscreenCanvas->GetOwnerGlobal(); 6207 if (global) { 6208 nsIPrincipal* principal = global->PrincipalOrNull(); 6209 if (principal) { 6210 return principal->GetHashValue(); 6211 } 6212 } 6213 } 6214 return 0; 6215 } 6216 6217 // --------------------------- 6218 6219 void ClientWebGLContext::EnqueueError_ArgEnum(const char* const argName, 6220 const GLenum val) const { 6221 EnqueueError(LOCAL_GL_INVALID_ENUM, "Bad `%s`: 0x%04x", argName, val); 6222 } 6223 6224 // - 6225 // WebGLProgramJS 6226 6227 void ClientWebGLContext::AttachShader(WebGLProgramJS& prog, 6228 WebGLShaderJS& shader) const { 6229 const FuncScope funcScope(*this, "attachShader"); 6230 if (IsContextLost()) return; 6231 if (!prog.ValidateUsable(*this, "program")) return; 6232 if (!shader.ValidateUsable(*this, "shader")) return; 6233 6234 auto& slot = *MaybeFind(prog.mNextLink_Shaders, shader.mType); 6235 if (slot.shader) { 6236 if (&shader == slot.shader) { 6237 EnqueueError(LOCAL_GL_INVALID_OPERATION, "`shader` is already attached."); 6238 } else { 6239 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6240 "Only one of each type of" 6241 " shader may be attached to a program."); 6242 } 6243 return; 6244 } 6245 slot = {&shader, shader.mKeepAliveWeak.lock()}; 6246 6247 Run<RPROC(AttachShader)>(prog.mId, shader.mId); 6248 } 6249 6250 void ClientWebGLContext::BindAttribLocation(WebGLProgramJS& prog, 6251 const GLuint location, 6252 const nsAString& name) const { 6253 const FuncScope funcScope(*this, "bindAttribLocation"); 6254 if (IsContextLost()) return; 6255 if (!prog.ValidateUsable(*this, "program")) return; 6256 6257 const auto& nameU8 = ToString(NS_ConvertUTF16toUTF8(name)); 6258 Run<RPROC(BindAttribLocation)>(prog.mId, location, nameU8); 6259 } 6260 6261 void ClientWebGLContext::DetachShader(WebGLProgramJS& prog, 6262 const WebGLShaderJS& shader) const { 6263 const FuncScope funcScope(*this, "detachShader"); 6264 if (IsContextLost()) return; 6265 if (!prog.ValidateUsable(*this, "program")) return; 6266 if (!shader.ValidateUsable(*this, "shader")) return; 6267 6268 auto& slot = *MaybeFind(prog.mNextLink_Shaders, shader.mType); 6269 6270 if (slot.shader != &shader) { 6271 EnqueueError(LOCAL_GL_INVALID_OPERATION, "`shader` is not attached."); 6272 return; 6273 } 6274 slot = {}; 6275 6276 Run<RPROC(DetachShader)>(prog.mId, shader.mId); 6277 } 6278 6279 void ClientWebGLContext::GetAttachedShaders( 6280 const WebGLProgramJS& prog, 6281 dom::Nullable<nsTArray<RefPtr<WebGLShaderJS>>>& retval) const { 6282 const FuncScope funcScope(*this, "getAttachedShaders"); 6283 if (IsContextLost()) return; 6284 if (!prog.ValidateUsable(*this, "program")) return; 6285 6286 auto& arr = retval.SetValue(); 6287 for (const auto& pair : prog.mNextLink_Shaders) { 6288 const auto& attachment = pair.second; 6289 if (!attachment.shader) continue; 6290 arr.AppendElement(attachment.shader); 6291 } 6292 } 6293 6294 void ClientWebGLContext::LinkProgram(WebGLProgramJS& prog) const { 6295 const FuncScope funcScope(*this, "linkProgram"); 6296 if (IsContextLost()) return; 6297 if (!prog.ValidateUsable(*this, "program")) return; 6298 6299 if (!prog.mActiveTfos.empty()) { 6300 EnqueueError(LOCAL_GL_INVALID_OPERATION, 6301 "Program still in use by active or paused" 6302 " Transform Feedback objects."); 6303 return; 6304 } 6305 6306 prog.mResult = std::make_shared<webgl::LinkResult>(); 6307 prog.mUniformLocByName = Nothing(); 6308 prog.mUniformBlockBindings = {}; 6309 Run<RPROC(LinkProgram)>(prog.mId); 6310 } 6311 6312 void ClientWebGLContext::TransformFeedbackVaryings( 6313 WebGLProgramJS& prog, const dom::Sequence<nsString>& varyings, 6314 const GLenum bufferMode) const { 6315 const FuncScope funcScope(*this, "transformFeedbackVaryings"); 6316 if (IsContextLost()) return; 6317 if (!prog.ValidateUsable(*this, "program")) return; 6318 6319 std::vector<std::string> varyingsU8; 6320 varyingsU8.reserve(varyings.Length()); 6321 for (const auto& cur : varyings) { 6322 const auto curU8 = ToString(NS_ConvertUTF16toUTF8(cur)); 6323 varyingsU8.push_back(curU8); 6324 } 6325 6326 Run<RPROC(TransformFeedbackVaryings)>(prog.mId, varyingsU8, bufferMode); 6327 } 6328 6329 void ClientWebGLContext::UniformBlockBinding(WebGLProgramJS& prog, 6330 const GLuint blockIndex, 6331 const GLuint blockBinding) const { 6332 const FuncScope funcScope(*this, "uniformBlockBinding"); 6333 if (IsContextLost()) return; 6334 if (!prog.ValidateUsable(*this, "program")) return; 6335 const auto& state = State(); 6336 6337 (void)GetLinkResult(prog); 6338 auto& list = prog.mUniformBlockBindings; 6339 if (blockIndex >= list.size()) { 6340 EnqueueError( 6341 LOCAL_GL_INVALID_VALUE, 6342 "`blockIndex` (%u) must be less than ACTIVE_UNIFORM_BLOCKS (%zu).", 6343 blockIndex, list.size()); 6344 return; 6345 } 6346 if (blockBinding >= state.mBoundUbos.size()) { 6347 EnqueueError(LOCAL_GL_INVALID_VALUE, 6348 "`blockBinding` (%u) must be less than " 6349 "MAX_UNIFORM_BUFFER_BINDINGS (%zu).", 6350 blockBinding, state.mBoundUbos.size()); 6351 return; 6352 } 6353 6354 list[blockIndex] = blockBinding; 6355 Run<RPROC(UniformBlockBinding)>(prog.mId, blockIndex, blockBinding); 6356 } 6357 6358 // WebGLProgramJS link result reflection 6359 6360 already_AddRefed<WebGLActiveInfoJS> ClientWebGLContext::GetActiveAttrib( 6361 const WebGLProgramJS& prog, const GLuint index) { 6362 const FuncScope funcScope(*this, "getActiveAttrib"); 6363 if (IsContextLost()) return nullptr; 6364 if (!prog.ValidateUsable(*this, "program")) return nullptr; 6365 6366 const auto& res = GetLinkResult(prog); 6367 const auto& list = res.active.activeAttribs; 6368 if (index >= list.size()) { 6369 EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large."); 6370 return nullptr; 6371 } 6372 6373 const auto& info = list[index]; 6374 return AsAddRefed(new WebGLActiveInfoJS(info)); 6375 } 6376 6377 already_AddRefed<WebGLActiveInfoJS> ClientWebGLContext::GetActiveUniform( 6378 const WebGLProgramJS& prog, const GLuint index) { 6379 const FuncScope funcScope(*this, "getActiveUniform"); 6380 if (IsContextLost()) return nullptr; 6381 if (!prog.ValidateUsable(*this, "program")) return nullptr; 6382 6383 const auto& res = GetLinkResult(prog); 6384 const auto& list = res.active.activeUniforms; 6385 if (index >= list.size()) { 6386 EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large."); 6387 return nullptr; 6388 } 6389 6390 const auto& info = list[index]; 6391 return AsAddRefed(new WebGLActiveInfoJS(info)); 6392 } 6393 6394 void ClientWebGLContext::GetActiveUniformBlockName(const WebGLProgramJS& prog, 6395 const GLuint index, 6396 nsAString& retval) const { 6397 retval.SetIsVoid(true); 6398 const FuncScope funcScope(*this, "getActiveUniformBlockName"); 6399 if (IsContextLost()) return; 6400 if (!prog.ValidateUsable(*this, "program")) return; 6401 6402 const auto& res = GetLinkResult(prog); 6403 if (!res.success) { 6404 EnqueueError(LOCAL_GL_INVALID_OPERATION, "Program has not been linked."); 6405 return; 6406 } 6407 6408 const auto& list = res.active.activeUniformBlocks; 6409 if (index >= list.size()) { 6410 EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large."); 6411 return; 6412 } 6413 6414 const auto& block = list[index]; 6415 CopyUTF8toUTF16(block.name, retval); 6416 } 6417 6418 void ClientWebGLContext::GetActiveUniformBlockParameter( 6419 JSContext* const cx, const WebGLProgramJS& prog, const GLuint index, 6420 const GLenum pname, JS::MutableHandle<JS::Value> retval, ErrorResult& rv) { 6421 const FuncScope funcScope(*this, "getActiveUniformBlockParameter"); 6422 retval.set(JS::NullValue()); 6423 RefPtr<webgl::NotLostData> notLost(mNotLost); 6424 if (!notLost) { 6425 return; 6426 } 6427 if (!prog.ValidateUsable(*this, "program")) return; 6428 6429 const auto& res = GetLinkResult(prog); 6430 const auto& list = res.active.activeUniformBlocks; 6431 if (index >= list.size()) { 6432 EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large."); 6433 return; 6434 } 6435 const auto& block = list[index]; 6436 6437 retval.set([&]() -> JS::Value { 6438 switch (pname) { 6439 case LOCAL_GL_UNIFORM_BLOCK_BINDING: 6440 return JS::NumberValue(prog.mUniformBlockBindings[index]); 6441 6442 case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE: 6443 return JS::NumberValue(block.dataSize); 6444 6445 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: 6446 return JS::NumberValue(block.activeUniformIndices.size()); 6447 6448 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: { 6449 const auto& indices = block.activeUniformIndices; 6450 return Create<dom::Uint32Array>(cx, this, indices, rv); 6451 } 6452 6453 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: 6454 return JS::BooleanValue(block.referencedByVertexShader); 6455 6456 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: 6457 return JS::BooleanValue(block.referencedByFragmentShader); 6458 6459 default: 6460 EnqueueError_ArgEnum("pname", pname); 6461 return JS::NullValue(); 6462 } 6463 }()); 6464 } 6465 6466 void ClientWebGLContext::GetActiveUniforms( 6467 JSContext* const cx, const WebGLProgramJS& prog, 6468 const dom::Sequence<GLuint>& uniformIndices, const GLenum pname, 6469 JS::MutableHandle<JS::Value> retval) const { 6470 const FuncScope funcScope(*this, "getActiveUniforms"); 6471 retval.set(JS::NullValue()); 6472 RefPtr<webgl::NotLostData> notLost(mNotLost); 6473 if (!notLost) { 6474 return; 6475 } 6476 if (!prog.ValidateUsable(*this, "program")) return; 6477 6478 const auto& res = GetLinkResult(prog); 6479 const auto& list = res.active.activeUniforms; 6480 6481 const auto count = uniformIndices.Length(); 6482 JS::Rooted<JSObject*> array(cx, JS::NewArrayObject(cx, count)); 6483 if (!array) return; // Just bail. 6484 6485 for (const auto i : IntegerRange(count)) { 6486 const auto index = uniformIndices[i]; 6487 if (index >= list.size()) { 6488 EnqueueError(LOCAL_GL_INVALID_VALUE, 6489 "`uniformIndices[%u]`: `%u` too large.", i, index); 6490 return; 6491 } 6492 const auto& uniform = list[index]; 6493 6494 JS::Rooted<JS::Value> value(cx); 6495 switch (pname) { 6496 case LOCAL_GL_UNIFORM_TYPE: 6497 value = JS::NumberValue(uniform.elemType); 6498 break; 6499 6500 case LOCAL_GL_UNIFORM_SIZE: 6501 value = JS::NumberValue(uniform.elemCount); 6502 break; 6503 6504 case LOCAL_GL_UNIFORM_BLOCK_INDEX: 6505 value = JS::NumberValue(uniform.block_index); 6506 break; 6507 6508 case LOCAL_GL_UNIFORM_OFFSET: 6509 value = JS::NumberValue(uniform.block_offset); 6510 break; 6511 6512 case LOCAL_GL_UNIFORM_ARRAY_STRIDE: 6513 value = JS::NumberValue(uniform.block_arrayStride); 6514 break; 6515 6516 case LOCAL_GL_UNIFORM_MATRIX_STRIDE: 6517 value = JS::NumberValue(uniform.block_matrixStride); 6518 break; 6519 6520 case LOCAL_GL_UNIFORM_IS_ROW_MAJOR: 6521 value = JS::BooleanValue(uniform.block_isRowMajor); 6522 break; 6523 6524 default: 6525 EnqueueError_ArgEnum("pname", pname); 6526 return; 6527 } 6528 if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) return; 6529 } 6530 6531 retval.setObject(*array); 6532 } 6533 6534 already_AddRefed<WebGLActiveInfoJS> 6535 ClientWebGLContext::GetTransformFeedbackVarying(const WebGLProgramJS& prog, 6536 const GLuint index) { 6537 const FuncScope funcScope(*this, "getTransformFeedbackVarying"); 6538 if (IsContextLost()) return nullptr; 6539 if (!prog.ValidateUsable(*this, "program")) return nullptr; 6540 6541 const auto& res = GetLinkResult(prog); 6542 const auto& list = res.active.activeTfVaryings; 6543 if (index >= list.size()) { 6544 EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large."); 6545 return nullptr; 6546 } 6547 6548 const auto& info = list[index]; 6549 return AsAddRefed(new WebGLActiveInfoJS(info)); 6550 } 6551 6552 GLint ClientWebGLContext::GetAttribLocation(const WebGLProgramJS& prog, 6553 const nsAString& name) const { 6554 const FuncScope funcScope(*this, "getAttribLocation"); 6555 if (IsContextLost()) return -1; 6556 if (!prog.ValidateUsable(*this, "program")) return -1; 6557 6558 const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name)); 6559 const auto& res = GetLinkResult(prog); 6560 for (const auto& cur : res.active.activeAttribs) { 6561 if (cur.name == nameU8) return cur.location; 6562 } 6563 6564 const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8); 6565 if (err) { 6566 EnqueueError(err->type, "%s", err->info.c_str()); 6567 } 6568 return -1; 6569 } 6570 6571 GLint ClientWebGLContext::GetFragDataLocation(const WebGLProgramJS& prog, 6572 const nsAString& name) const { 6573 RefPtr<webgl::NotLostData> notLost(mNotLost); 6574 if (!notLost) { 6575 return -1; 6576 } 6577 const FuncScope funcScope(*this, "getFragDataLocation"); 6578 if (!prog.ValidateUsable(*this, "program")) return -1; 6579 6580 const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name)); 6581 6582 const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8); 6583 if (err) { 6584 EnqueueError(*err); 6585 return -1; 6586 } 6587 6588 return [&]() { 6589 const auto& inProcess = notLost->inProcess; 6590 if (inProcess) { 6591 return inProcess->GetFragDataLocation(prog.mId, nameU8); 6592 } 6593 const auto& child = notLost->outOfProcess; 6594 child->FlushPendingCmds(); 6595 GLint ret = {}; 6596 if (!child->SendGetFragDataLocation(prog.mId, nameU8, &ret)) { 6597 ret = {}; 6598 } 6599 return ret; 6600 }(); 6601 } 6602 6603 GLuint ClientWebGLContext::GetUniformBlockIndex( 6604 const WebGLProgramJS& prog, const nsAString& blockName) const { 6605 const FuncScope funcScope(*this, "getUniformBlockIndex"); 6606 if (IsContextLost()) return LOCAL_GL_INVALID_INDEX; 6607 if (!prog.ValidateUsable(*this, "program")) return LOCAL_GL_INVALID_INDEX; 6608 6609 const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(blockName)); 6610 6611 const auto& res = GetLinkResult(prog); 6612 const auto& list = res.active.activeUniformBlocks; 6613 for (const auto i : IntegerRange(list.size())) { 6614 const auto& cur = list[i]; 6615 if (cur.name == nameU8) { 6616 return i; 6617 } 6618 } 6619 return LOCAL_GL_INVALID_INDEX; 6620 } 6621 6622 void ClientWebGLContext::GetUniformIndices( 6623 const WebGLProgramJS& prog, const dom::Sequence<nsString>& uniformNames, 6624 dom::Nullable<nsTArray<GLuint>>& retval) const { 6625 const FuncScope funcScope(*this, "getUniformIndices"); 6626 if (IsContextLost()) return; 6627 if (!prog.ValidateUsable(*this, "program")) return; 6628 6629 const auto& res = GetLinkResult(prog); 6630 auto ret = nsTArray<GLuint>(uniformNames.Length()); 6631 6632 for (const auto& queriedNameU16 : uniformNames) { 6633 const auto queriedName = ToString(NS_ConvertUTF16toUTF8(queriedNameU16)); 6634 const auto impliedProperArrayQueriedName = queriedName + "[0]"; 6635 6636 GLuint activeId = LOCAL_GL_INVALID_INDEX; 6637 for (const auto i : IntegerRange(res.active.activeUniforms.size())) { 6638 // O(N^2) ok for small N. 6639 const auto& activeInfoForI = res.active.activeUniforms[i]; 6640 if (queriedName == activeInfoForI.name || 6641 impliedProperArrayQueriedName == activeInfoForI.name) { 6642 activeId = i; 6643 break; 6644 } 6645 } 6646 ret.AppendElement(activeId); 6647 } 6648 6649 retval.SetValue(std::move(ret)); 6650 } 6651 6652 already_AddRefed<WebGLUniformLocationJS> ClientWebGLContext::GetUniformLocation( 6653 const WebGLProgramJS& prog, const nsAString& name) const { 6654 const FuncScope funcScope(*this, "getUniformLocation"); 6655 if (IsContextLost()) return nullptr; 6656 if (!prog.ValidateUsable(*this, "program")) return nullptr; 6657 6658 const auto& res = GetLinkResult(prog); 6659 6660 if (!prog.mUniformLocByName) { 6661 // Cache a map from name->location. 6662 // Since the only way to set uniforms requires calling GetUniformLocation, 6663 // we expect apps to query most active uniforms once for each scalar or 6664 // array. NB: Uniform array setters do have overflow semantics, even though 6665 // uniform locations aren't guaranteed contiguous, but GetUniformLocation 6666 // must still be called once per array. 6667 prog.mUniformLocByName.emplace(); 6668 6669 for (const auto& activeUniform : res.active.activeUniforms) { 6670 if (activeUniform.block_index != -1) continue; 6671 6672 auto locName = activeUniform.name; 6673 const auto indexed = webgl::ParseIndexed(locName); 6674 if (indexed) { 6675 locName = indexed->name; 6676 } 6677 6678 const auto err = CheckGLSLVariableName(mIsWebGL2, locName); 6679 if (err) continue; 6680 6681 const auto baseLength = locName.size(); 6682 for (const auto& pair : activeUniform.locByIndex) { 6683 if (indexed) { 6684 locName.erase(baseLength); // Erase previous "[N]". 6685 locName += '['; 6686 locName += std::to_string(pair.first); 6687 locName += ']'; 6688 } 6689 const auto locInfo = 6690 WebGLProgramJS::UniformLocInfo{pair.second, activeUniform.elemType}; 6691 prog.mUniformLocByName->insert({locName, locInfo}); 6692 } 6693 } 6694 } 6695 const auto& locByName = *(prog.mUniformLocByName); 6696 6697 const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name)); 6698 auto loc = MaybeFind(locByName, nameU8); 6699 if (!loc) { 6700 loc = MaybeFind(locByName, nameU8 + "[0]"); 6701 } 6702 if (!loc) { 6703 const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8); 6704 if (err) { 6705 EnqueueError(err->type, "%s", err->info.c_str()); 6706 } 6707 return nullptr; 6708 } 6709 6710 return AsAddRefed(new WebGLUniformLocationJS(*this, prog.mResult.get(), 6711 loc->location, loc->elemType)); 6712 } 6713 6714 std::array<uint16_t, 3> ValidUploadElemTypes(const GLenum elemType) { 6715 std::vector<GLenum> ret; 6716 switch (elemType) { 6717 case LOCAL_GL_BOOL: 6718 ret = {LOCAL_GL_FLOAT, LOCAL_GL_INT, LOCAL_GL_UNSIGNED_INT}; 6719 break; 6720 case LOCAL_GL_BOOL_VEC2: 6721 ret = {LOCAL_GL_FLOAT_VEC2, LOCAL_GL_INT_VEC2, 6722 LOCAL_GL_UNSIGNED_INT_VEC2}; 6723 break; 6724 case LOCAL_GL_BOOL_VEC3: 6725 ret = {LOCAL_GL_FLOAT_VEC3, LOCAL_GL_INT_VEC3, 6726 LOCAL_GL_UNSIGNED_INT_VEC3}; 6727 break; 6728 case LOCAL_GL_BOOL_VEC4: 6729 ret = {LOCAL_GL_FLOAT_VEC4, LOCAL_GL_INT_VEC4, 6730 LOCAL_GL_UNSIGNED_INT_VEC4}; 6731 break; 6732 6733 case LOCAL_GL_SAMPLER_2D: 6734 case LOCAL_GL_SAMPLER_3D: 6735 case LOCAL_GL_SAMPLER_CUBE: 6736 case LOCAL_GL_SAMPLER_2D_SHADOW: 6737 case LOCAL_GL_SAMPLER_2D_ARRAY: 6738 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 6739 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 6740 case LOCAL_GL_INT_SAMPLER_2D: 6741 case LOCAL_GL_INT_SAMPLER_3D: 6742 case LOCAL_GL_INT_SAMPLER_CUBE: 6743 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 6744 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 6745 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 6746 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 6747 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 6748 ret = {LOCAL_GL_INT}; 6749 break; 6750 6751 default: 6752 ret = {elemType}; 6753 break; 6754 } 6755 6756 std::array<uint16_t, 3> arr = {}; 6757 MOZ_ASSERT(arr[2] == 0); 6758 for (const auto i : IntegerRange(ret.size())) { 6759 arr[i] = AssertedCast<uint16_t>(ret[i]); 6760 } 6761 return arr; 6762 } 6763 6764 void ClientWebGLContext::GetProgramInfoLog(const WebGLProgramJS& prog, 6765 nsAString& retval) const { 6766 retval.SetIsVoid(true); 6767 const FuncScope funcScope(*this, "getProgramInfoLog"); 6768 if (IsContextLost()) return; 6769 if (!prog.ValidateUsable(*this, "program")) return; 6770 6771 const auto& res = GetLinkResult(prog); 6772 CopyUTF8toUTF16(res.log, retval); 6773 } 6774 6775 void ClientWebGLContext::GetProgramParameter( 6776 JSContext* const js, const WebGLProgramJS& prog, const GLenum pname, 6777 JS::MutableHandle<JS::Value> retval) const { 6778 const FuncScope funcScope(*this, "getProgramParameter"); 6779 retval.set(JS::NullValue()); 6780 RefPtr<webgl::NotLostData> notLost(mNotLost); 6781 if (!notLost) { 6782 return; 6783 } 6784 if (!prog.ValidateUsable(*this, "program")) return; 6785 6786 retval.set([&]() -> JS::Value { 6787 switch (pname) { 6788 case LOCAL_GL_DELETE_STATUS: 6789 // "Is flagged for deletion?" 6790 return JS::BooleanValue(!prog.mKeepAlive); 6791 case LOCAL_GL_VALIDATE_STATUS: 6792 return JS::BooleanValue(prog.mLastValidate); 6793 case LOCAL_GL_ATTACHED_SHADERS: { 6794 size_t shaders = 0; 6795 for (const auto& pair : prog.mNextLink_Shaders) { 6796 const auto& slot = pair.second; 6797 if (slot.shader) { 6798 shaders += 1; 6799 } 6800 } 6801 return JS::NumberValue(shaders); 6802 } 6803 default: 6804 break; 6805 } 6806 6807 const auto& res = GetLinkResult(prog); 6808 6809 switch (pname) { 6810 case LOCAL_GL_LINK_STATUS: 6811 return JS::BooleanValue(res.success); 6812 6813 case LOCAL_GL_ACTIVE_ATTRIBUTES: 6814 return JS::NumberValue(res.active.activeAttribs.size()); 6815 6816 case LOCAL_GL_ACTIVE_UNIFORMS: 6817 return JS::NumberValue(res.active.activeUniforms.size()); 6818 6819 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE: 6820 if (!mIsWebGL2) break; 6821 return JS::NumberValue(res.tfBufferMode); 6822 6823 case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS: 6824 if (!mIsWebGL2) break; 6825 return JS::NumberValue(res.active.activeTfVaryings.size()); 6826 6827 case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS: 6828 if (!mIsWebGL2) break; 6829 return JS::NumberValue(res.active.activeUniformBlocks.size()); 6830 6831 default: 6832 break; 6833 } 6834 EnqueueError_ArgEnum("pname", pname); 6835 return JS::NullValue(); 6836 }()); 6837 } 6838 6839 // - 6840 // WebGLShaderJS 6841 6842 void ClientWebGLContext::CompileShader(WebGLShaderJS& shader) const { 6843 const FuncScope funcScope(*this, "compileShader"); 6844 if (IsContextLost()) return; 6845 if (!shader.ValidateUsable(*this, "shader")) return; 6846 6847 shader.mResult = {}; 6848 Run<RPROC(CompileShader)>(shader.mId); 6849 } 6850 6851 void ClientWebGLContext::GetShaderInfoLog(const WebGLShaderJS& shader, 6852 nsAString& retval) const { 6853 retval.SetIsVoid(true); 6854 const FuncScope funcScope(*this, "getShaderInfoLog"); 6855 if (IsContextLost()) return; 6856 if (!shader.ValidateUsable(*this, "shader")) return; 6857 6858 const auto& result = GetCompileResult(shader); 6859 CopyUTF8toUTF16(result.log, retval); 6860 } 6861 6862 void ClientWebGLContext::GetShaderParameter( 6863 JSContext* const cx, const WebGLShaderJS& shader, const GLenum pname, 6864 JS::MutableHandle<JS::Value> retval) const { 6865 const FuncScope funcScope(*this, "getShaderParameter"); 6866 retval.set(JS::NullValue()); 6867 RefPtr<webgl::NotLostData> notLost(mNotLost); 6868 if (!notLost) { 6869 return; 6870 } 6871 if (!shader.ValidateUsable(*this, "shader")) return; 6872 6873 retval.set([&]() -> JS::Value { 6874 switch (pname) { 6875 case LOCAL_GL_SHADER_TYPE: 6876 return JS::NumberValue(shader.mType); 6877 6878 case LOCAL_GL_DELETE_STATUS: // "Is flagged for deletion?" 6879 return JS::BooleanValue(!shader.mKeepAlive); 6880 6881 case LOCAL_GL_COMPILE_STATUS: { 6882 const auto& result = GetCompileResult(shader); 6883 return JS::BooleanValue(result.success); 6884 } 6885 6886 default: 6887 EnqueueError_ArgEnum("pname", pname); 6888 return JS::NullValue(); 6889 } 6890 }()); 6891 } 6892 6893 void ClientWebGLContext::GetShaderSource(const WebGLShaderJS& shader, 6894 nsAString& retval) const { 6895 retval.SetIsVoid(true); 6896 const FuncScope funcScope(*this, "getShaderSource"); 6897 if (IsContextLost()) return; 6898 if (!shader.ValidateUsable(*this, "shader")) return; 6899 6900 CopyUTF8toUTF16(shader.mSource, retval); 6901 } 6902 6903 void ClientWebGLContext::GetTranslatedShaderSource(const WebGLShaderJS& shader, 6904 nsAString& retval) const { 6905 retval.SetIsVoid(true); 6906 const FuncScope funcScope(*this, "getTranslatedShaderSource"); 6907 if (IsContextLost()) return; 6908 if (!shader.ValidateUsable(*this, "shader")) return; 6909 6910 const auto& result = GetCompileResult(shader); 6911 CopyUTF8toUTF16(result.translatedSource, retval); 6912 } 6913 6914 void ClientWebGLContext::ShaderSource(WebGLShaderJS& shader, 6915 const nsAString& sourceU16) const { 6916 const FuncScope funcScope(*this, "shaderSource"); 6917 if (IsContextLost()) return; 6918 if (!shader.ValidateUsable(*this, "shader")) return; 6919 6920 shader.mSource = ToString(NS_ConvertUTF16toUTF8(sourceU16)); 6921 Run<RPROC(ShaderSource)>(shader.mId, shader.mSource); 6922 } 6923 6924 // - 6925 6926 const webgl::CompileResult& ClientWebGLContext::GetCompileResult( 6927 const WebGLShaderJS& shader) const { 6928 RefPtr<webgl::NotLostData> notLost(mNotLost); 6929 if (!notLost) { 6930 return shader.mResult; 6931 } 6932 if (shader.mResult.pending) { 6933 shader.mResult = [&]() { 6934 const auto& inProcess = notLost->inProcess; 6935 if (inProcess) { 6936 return inProcess->GetCompileResult(shader.mId); 6937 } 6938 const auto& child = notLost->outOfProcess; 6939 child->FlushPendingCmds(); 6940 webgl::CompileResult ret = {}; 6941 if (!child->SendGetCompileResult(shader.mId, &ret)) { 6942 ret = {}; 6943 } 6944 return ret; 6945 }(); 6946 } 6947 return shader.mResult; 6948 } 6949 6950 const webgl::LinkResult& ClientWebGLContext::GetLinkResult( 6951 const WebGLProgramJS& prog) const { 6952 RefPtr<webgl::NotLostData> notLost(mNotLost); 6953 if (!notLost) { 6954 return *(prog.mResult); 6955 } 6956 if (prog.mResult->pending) { 6957 *(prog.mResult) = [&]() { 6958 const auto& inProcess = notLost->inProcess; 6959 if (inProcess) { 6960 return inProcess->GetLinkResult(prog.mId); 6961 } 6962 const auto& child = notLost->outOfProcess; 6963 child->FlushPendingCmds(); 6964 webgl::LinkResult ret; 6965 if (!child->SendGetLinkResult(prog.mId, &ret)) { 6966 ret = {}; 6967 } 6968 return ret; 6969 }(); 6970 6971 prog.mUniformBlockBindings.resize( 6972 prog.mResult->active.activeUniformBlocks.size()); 6973 6974 auto& state = State(); 6975 if (state.mCurrentProgram == &prog && prog.mResult->success) { 6976 state.mActiveLinkResult = prog.mResult; 6977 } 6978 } 6979 return *(prog.mResult); 6980 } 6981 6982 #undef RPROC 6983 6984 // --------------------------- 6985 6986 Maybe<Span<uint8_t>> ClientWebGLContext::ValidateArrayBufferView( 6987 const Span<uint8_t>& bytes, size_t elemSize, GLuint elemOffset, 6988 GLuint elemCountOverride, const GLenum errorEnum) const { 6989 size_t elemCount = bytes.Length() / elemSize; 6990 if (elemOffset > elemCount) { 6991 EnqueueError(errorEnum, "Invalid offset into ArrayBufferView."); 6992 return Nothing(); 6993 } 6994 elemCount -= elemOffset; 6995 6996 if (elemCountOverride) { 6997 if (elemCountOverride > elemCount) { 6998 EnqueueError(errorEnum, "Invalid sub-length for ArrayBufferView."); 6999 return Nothing(); 7000 } 7001 elemCount = elemCountOverride; 7002 } 7003 7004 return Some(bytes.Subspan(elemOffset * elemSize, elemCount * elemSize)); 7005 } 7006 7007 // --------------------------- 7008 7009 webgl::ObjectJS::ObjectJS(const ClientWebGLContext* const webgl) 7010 : mGeneration(webgl ? webgl->mNotLost : nullptr), 7011 mId(webgl ? webgl->NextId() : 0) {} 7012 7013 // - 7014 7015 WebGLFramebufferJS::WebGLFramebufferJS(const ClientWebGLContext& webgl, 7016 bool opaque) 7017 : webgl::ObjectJS(&webgl), mOpaque(opaque) { 7018 (void)mAttachments[LOCAL_GL_DEPTH_ATTACHMENT]; 7019 (void)mAttachments[LOCAL_GL_STENCIL_ATTACHMENT]; 7020 if (!webgl.mIsWebGL2) { 7021 (void)mAttachments[LOCAL_GL_DEPTH_STENCIL_ATTACHMENT]; 7022 } 7023 7024 EnsureColorAttachments(); 7025 } 7026 7027 void WebGLFramebufferJS::EnsureColorAttachments() { 7028 const auto& webgl = Context(); 7029 if (!webgl) return; // Context is lost. 7030 7031 const auto& limits = webgl->Limits(); 7032 auto maxColorDrawBuffers = limits.maxColorDrawBuffers; 7033 if (!webgl->mIsWebGL2 && 7034 !webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { 7035 maxColorDrawBuffers = 1; 7036 } 7037 for (const auto i : IntegerRange(maxColorDrawBuffers)) { 7038 (void)mAttachments[LOCAL_GL_COLOR_ATTACHMENT0 + i]; 7039 } 7040 } 7041 7042 WebGLProgramJS::WebGLProgramJS(const ClientWebGLContext& webgl) 7043 : webgl::ObjectJS(&webgl), 7044 mKeepAlive(std::make_shared<webgl::ProgramKeepAlive>(*this)), 7045 mKeepAliveWeak(mKeepAlive) { 7046 (void)mNextLink_Shaders[LOCAL_GL_VERTEX_SHADER]; 7047 (void)mNextLink_Shaders[LOCAL_GL_FRAGMENT_SHADER]; 7048 7049 mResult = std::make_shared<webgl::LinkResult>(); 7050 } 7051 7052 WebGLShaderJS::WebGLShaderJS(const ClientWebGLContext& webgl, const GLenum type) 7053 : webgl::ObjectJS(&webgl), 7054 mType(type), 7055 mKeepAlive(std::make_shared<webgl::ShaderKeepAlive>(*this)), 7056 mKeepAliveWeak(mKeepAlive) {} 7057 7058 WebGLTransformFeedbackJS::WebGLTransformFeedbackJS( 7059 const ClientWebGLContext& webgl) 7060 : webgl::ObjectJS(&webgl), 7061 mAttribBuffers(webgl::kMaxTransformFeedbackSeparateAttribs) {} 7062 7063 WebGLVertexArrayJS::WebGLVertexArrayJS(const ClientWebGLContext* const webgl) 7064 : webgl::ObjectJS(webgl), 7065 mAttribBuffers(Context() ? Context()->Limits().maxVertexAttribs : 0) {} 7066 7067 // - 7068 7069 #define _(WebGLType) \ 7070 JSObject* WebGLType##JS::WrapObject(JSContext* const cx, \ 7071 JS::Handle<JSObject*> givenProto) { \ 7072 return dom::WebGLType##_Binding::Wrap(cx, this, givenProto); \ 7073 } 7074 7075 _(WebGLBuffer) 7076 _(WebGLFramebuffer) 7077 _(WebGLProgram) 7078 _(WebGLQuery) 7079 _(WebGLRenderbuffer) 7080 _(WebGLSampler) 7081 _(WebGLShader) 7082 _(WebGLSync) 7083 _(WebGLTexture) 7084 _(WebGLTransformFeedback) 7085 _(WebGLUniformLocation) 7086 //_(WebGLVertexArray) // The webidl is `WebGLVertexArrayObject` :( 7087 7088 #undef _ 7089 7090 JSObject* WebGLVertexArrayJS::WrapObject(JSContext* const cx, 7091 JS::Handle<JSObject*> givenProto) { 7092 return dom::WebGLVertexArrayObject_Binding::Wrap(cx, this, givenProto); 7093 } 7094 7095 bool WebGLActiveInfoJS::WrapObject(JSContext* const cx, 7096 JS::Handle<JSObject*> givenProto, 7097 JS::MutableHandle<JSObject*> reflector) { 7098 return dom::WebGLActiveInfo_Binding::Wrap(cx, this, givenProto, reflector); 7099 } 7100 7101 bool WebGLShaderPrecisionFormatJS::WrapObject( 7102 JSContext* const cx, JS::Handle<JSObject*> givenProto, 7103 JS::MutableHandle<JSObject*> reflector) { 7104 return dom::WebGLShaderPrecisionFormat_Binding::Wrap(cx, this, givenProto, 7105 reflector); 7106 } 7107 7108 // --------------------- 7109 7110 template <typename T> 7111 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, 7112 const std::vector<RefPtr<T>>& field, 7113 const char* name, uint32_t flags) { 7114 for (const auto& cur : field) { 7115 ImplCycleCollectionTraverse(callback, cur, name, flags); 7116 } 7117 } 7118 7119 template <typename T> 7120 void ImplCycleCollectionUnlink(std::vector<RefPtr<T>>& field) { 7121 field = {}; 7122 } 7123 7124 // - 7125 7126 template <typename T, size_t N> 7127 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, 7128 const std::array<RefPtr<T>, N>& field, 7129 const char* name, uint32_t flags) { 7130 for (const auto& cur : field) { 7131 ImplCycleCollectionTraverse(callback, cur, name, flags); 7132 } 7133 } 7134 7135 template <typename T, size_t N> 7136 void ImplCycleCollectionUnlink(std::array<RefPtr<T>, N>& field) { 7137 field = {}; 7138 } 7139 7140 // - 7141 7142 template <typename T> 7143 void ImplCycleCollectionTraverse( 7144 nsCycleCollectionTraversalCallback& callback, 7145 const std::unordered_map<GLenum, RefPtr<T>>& field, const char* name, 7146 uint32_t flags) { 7147 for (const auto& pair : field) { 7148 ImplCycleCollectionTraverse(callback, pair.second, name, flags); 7149 } 7150 } 7151 7152 template <typename T> 7153 void ImplCycleCollectionUnlink(std::unordered_map<GLenum, RefPtr<T>>& field) { 7154 field = {}; 7155 } 7156 7157 // - 7158 7159 void ImplCycleCollectionTraverse( 7160 nsCycleCollectionTraversalCallback& callback, 7161 const std::unordered_map<GLenum, WebGLFramebufferJS::Attachment>& field, 7162 const char* name, uint32_t flags) { 7163 for (const auto& pair : field) { 7164 const auto& attach = pair.second; 7165 ImplCycleCollectionTraverse(callback, attach.rb, name, flags); 7166 ImplCycleCollectionTraverse(callback, attach.tex, name, flags); 7167 } 7168 } 7169 7170 void ImplCycleCollectionUnlink( 7171 std::unordered_map<GLenum, WebGLFramebufferJS::Attachment>& field) { 7172 field = {}; 7173 } 7174 7175 // - 7176 7177 void ImplCycleCollectionTraverse( 7178 nsCycleCollectionTraversalCallback& callback, 7179 const std::unordered_map<GLenum, WebGLProgramJS::Attachment>& field, 7180 const char* name, uint32_t flags) { 7181 for (const auto& pair : field) { 7182 const auto& attach = pair.second; 7183 ImplCycleCollectionTraverse(callback, attach.shader, name, flags); 7184 } 7185 } 7186 7187 void ImplCycleCollectionUnlink( 7188 std::unordered_map<GLenum, WebGLProgramJS::Attachment>& field) { 7189 field = {}; 7190 } 7191 7192 // - 7193 7194 void ImplCycleCollectionUnlink( 7195 const RefPtr<ClientWebGLExtensionLoseContext>& field) { 7196 const_cast<RefPtr<ClientWebGLExtensionLoseContext>&>(field) = nullptr; 7197 } 7198 void ImplCycleCollectionUnlink(const RefPtr<WebGLProgramJS>& field) { 7199 const_cast<RefPtr<WebGLProgramJS>&>(field) = nullptr; 7200 } 7201 void ImplCycleCollectionUnlink(const RefPtr<WebGLShaderJS>& field) { 7202 const_cast<RefPtr<WebGLShaderJS>&>(field) = nullptr; 7203 } 7204 7205 // ---------------------- 7206 7207 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, 7208 const RefPtr<webgl::NotLostData>& field, 7209 const char* name, uint32_t flags) { 7210 if (!field) return; 7211 7212 ImplCycleCollectionTraverse(callback, field->extensions, 7213 "NotLostData.extensions", flags); 7214 7215 const auto& state = field->state; 7216 7217 ImplCycleCollectionTraverse(callback, state.mDefaultTfo, "state.mDefaultTfo", 7218 flags); 7219 ImplCycleCollectionTraverse(callback, state.mDefaultVao, "state.mDefaultVao", 7220 flags); 7221 7222 ImplCycleCollectionTraverse(callback, state.mCurrentProgram, 7223 "state.mCurrentProgram", flags); 7224 7225 ImplCycleCollectionTraverse(callback, state.mBoundBufferByTarget, 7226 "state.mBoundBufferByTarget", flags); 7227 ImplCycleCollectionTraverse(callback, state.mBoundUbos, "state.mBoundUbos", 7228 flags); 7229 ImplCycleCollectionTraverse(callback, state.mBoundDrawFb, 7230 "state.mBoundDrawFb", flags); 7231 ImplCycleCollectionTraverse(callback, state.mBoundReadFb, 7232 "state.mBoundReadFb", flags); 7233 ImplCycleCollectionTraverse(callback, state.mBoundRb, "state.mBoundRb", 7234 flags); 7235 ImplCycleCollectionTraverse(callback, state.mBoundTfo, "state.mBoundTfo", 7236 flags); 7237 ImplCycleCollectionTraverse(callback, state.mBoundVao, "state.mBoundVao", 7238 flags); 7239 ImplCycleCollectionTraverse(callback, state.mCurrentQueryByTarget, 7240 "state.state.mCurrentQueryByTarget", flags); 7241 7242 for (const auto& texUnit : state.mTexUnits) { 7243 ImplCycleCollectionTraverse(callback, texUnit.sampler, 7244 "state.mTexUnits[].sampler", flags); 7245 ImplCycleCollectionTraverse(callback, texUnit.texByTarget, 7246 "state.mTexUnits[].texByTarget", flags); 7247 } 7248 } 7249 7250 void ImplCycleCollectionUnlink(RefPtr<webgl::NotLostData>& field) { 7251 if (!field) return; 7252 const auto keepAlive = field; 7253 keepAlive->extensions = {}; 7254 keepAlive->state = {}; 7255 field = nullptr; 7256 } 7257 7258 // ----------------------------------------------------- 7259 7260 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBufferJS) 7261 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebufferJS, mAttachments) 7262 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgramJS, mNextLink_Shaders) 7263 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQueryJS) 7264 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbufferJS) 7265 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSamplerJS) 7266 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShaderJS) 7267 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSyncJS) 7268 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTextureJS) 7269 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLTransformFeedbackJS, mAttribBuffers, 7270 mActiveProgram) 7271 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocationJS) 7272 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArrayJS, mIndexBuffer, 7273 mAttribBuffers) 7274 7275 // - 7276 7277 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClientWebGLContext) 7278 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 7279 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal) 7280 NS_INTERFACE_MAP_ENTRY(nsISupports) 7281 NS_INTERFACE_MAP_END 7282 7283 NS_IMPL_CYCLE_COLLECTING_ADDREF(ClientWebGLContext) 7284 NS_IMPL_CYCLE_COLLECTING_RELEASE(ClientWebGLContext) 7285 7286 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR( 7287 ClientWebGLContext, mExtLoseContext, mNotLost, 7288 // Don't forget nsICanvasRenderingContextInternal: 7289 mCanvasElement, mOffscreenCanvas) 7290 7291 // ----------------------------- 7292 7293 } // namespace mozilla