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/. */
6 #include "ClientWebGLContext.h"
10 #include "ClientWebGLExtensions.h"
11 #include "gfxCrashReporterUtils.h"
12 #include "HostWebGLContext.h"
13 #include "js/PropertyAndElement.h" // JS_DefineElement
14 #include "js/ScalarType.h" // js::Scalar::Type
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/ToJSValue.h"
17 #include "mozilla/dom/TypedArray.h"
18 #include "mozilla/dom/WebGLContextEvent.h"
19 #include "mozilla/dom/WorkerCommon.h"
20 #include "mozilla/EnumeratedRange.h"
21 #include "mozilla/gfx/gfxVars.h"
22 #include "mozilla/gfx/CanvasManagerChild.h"
23 #include "mozilla/ipc/Shmem.h"
24 #include "mozilla/gfx/Swizzle.h"
25 #include "mozilla/layers/CompositableForwarder.h"
26 #include "mozilla/layers/CompositorBridgeChild.h"
27 #include "mozilla/layers/ImageBridgeChild.h"
28 #include "mozilla/layers/OOPCanvasRenderer.h"
29 #include "mozilla/layers/TextureClientSharedSurface.h"
30 #include "mozilla/layers/WebRenderUserData.h"
31 #include "mozilla/layers/WebRenderCanvasRenderer.h"
32 #include "mozilla/ResultVariant.h"
33 #include "mozilla/ScopeExit.h"
34 #include "mozilla/StaticPrefs_webgl.h"
35 #include "nsContentUtils.h"
36 #include "nsDisplayList.h"
37 #include "TexUnpackBlob.h"
38 #include "WebGLFormats.h"
39 #include "WebGLMethodDispatcher.h"
40 #include "WebGLChild.h"
41 #include "WebGLTextureUpload.h"
42 #include "WebGLValidateStrings.h"
47 std::string
SanitizeRenderer(const std::string
&);
52 webgl::NotLostData::NotLostData(ClientWebGLContext
& _context
)
53 : context(_context
) {}
55 webgl::NotLostData::~NotLostData() {
57 outOfProcess
->Destroy();
63 bool webgl::ObjectJS::ValidateForContext(
64 const ClientWebGLContext
& targetContext
, const char* const argName
) const {
65 if (!IsForContext(targetContext
)) {
66 targetContext
.EnqueueError(
67 LOCAL_GL_INVALID_OPERATION
,
68 "`%s` is from a different (or lost) WebGL context.", argName
);
74 void webgl::ObjectJS::WarnInvalidUse(const ClientWebGLContext
& targetContext
,
75 const char* const argName
) const {
76 if (!ValidateForContext(targetContext
, argName
)) return;
78 const auto errEnum
= ErrorOnDeleted();
79 targetContext
.EnqueueError(errEnum
, "Object `%s` is already deleted.",
85 WebGLBufferJS::~WebGLBufferJS() {
86 const auto webgl
= Context();
88 webgl
->DeleteBuffer(this);
92 WebGLFramebufferJS::~WebGLFramebufferJS() {
93 const auto webgl
= Context();
95 webgl
->DeleteFramebuffer(this);
99 WebGLQueryJS::~WebGLQueryJS() {
100 const auto webgl
= Context();
102 webgl
->DeleteQuery(this);
106 WebGLRenderbufferJS::~WebGLRenderbufferJS() {
107 const auto webgl
= Context();
109 webgl
->DeleteRenderbuffer(this);
113 WebGLSamplerJS::~WebGLSamplerJS() {
114 const auto webgl
= Context();
116 webgl
->DeleteSampler(this);
120 WebGLSyncJS::~WebGLSyncJS() {
121 const auto webgl
= Context();
123 webgl
->DeleteSync(this);
127 WebGLTextureJS::~WebGLTextureJS() {
128 const auto webgl
= Context();
130 webgl
->DeleteTexture(this);
134 WebGLTransformFeedbackJS::~WebGLTransformFeedbackJS() {
135 const auto webgl
= Context();
137 webgl
->DeleteTransformFeedback(this);
141 WebGLVertexArrayJS::~WebGLVertexArrayJS() {
142 const auto webgl
= Context();
144 webgl
->DeleteVertexArray(this);
150 static bool GetJSScalarFromGLType(GLenum type
,
151 js::Scalar::Type
* const out_scalarType
) {
154 *out_scalarType
= js::Scalar::Int8
;
157 case LOCAL_GL_UNSIGNED_BYTE
:
158 *out_scalarType
= js::Scalar::Uint8
;
162 *out_scalarType
= js::Scalar::Int16
;
165 case LOCAL_GL_HALF_FLOAT
:
166 case LOCAL_GL_HALF_FLOAT_OES
:
167 case LOCAL_GL_UNSIGNED_SHORT
:
168 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
169 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
170 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
171 *out_scalarType
= js::Scalar::Uint16
;
174 case LOCAL_GL_UNSIGNED_INT
:
175 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
:
176 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV
:
177 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
178 case LOCAL_GL_UNSIGNED_INT_24_8
:
179 *out_scalarType
= js::Scalar::Uint32
;
182 *out_scalarType
= js::Scalar::Int32
;
186 *out_scalarType
= js::Scalar::Float32
;
194 ClientWebGLContext::ClientWebGLContext(const bool webgl2
)
196 mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {}
198 ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); }
200 void ClientWebGLContext::JsWarning(const std::string
& utf8
) const {
201 nsIGlobalObject
* global
= nullptr;
202 if (mCanvasElement
) {
203 mozilla::dom::Document
* doc
= mCanvasElement
->OwnerDoc();
205 global
= doc
->GetScopeObject();
207 } else if (mOffscreenCanvas
) {
208 global
= mOffscreenCanvas
->GetOwnerGlobal();
212 if (!api
.Init(global
)) {
215 const auto& cx
= api
.cx();
216 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
219 void AutoJsWarning(const std::string
& utf8
) {
220 if (NS_IsMainThread()) {
221 const AutoJSContext cx
;
222 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
226 JSContext
* cx
= dom::GetCurrentWorkerThreadJSContext();
227 if (NS_WARN_IF(!cx
)) {
231 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
236 bool ClientWebGLContext::DispatchEvent(const nsAString
& eventName
) const {
237 const auto kCanBubble
= CanBubble::eYes
;
238 const auto kIsCancelable
= Cancelable::eYes
;
239 bool useDefaultHandler
= true;
241 if (mCanvasElement
) {
242 nsContentUtils::DispatchTrustedEvent(mCanvasElement
->OwnerDoc(),
243 mCanvasElement
, eventName
, kCanBubble
,
244 kIsCancelable
, &useDefaultHandler
);
245 } else if (mOffscreenCanvas
) {
246 // OffscreenCanvas case
247 RefPtr
<dom::Event
> event
=
248 new dom::Event(mOffscreenCanvas
, nullptr, nullptr);
249 event
->InitEvent(eventName
, kCanBubble
, kIsCancelable
);
250 event
->SetTrusted(true);
251 useDefaultHandler
= mOffscreenCanvas
->DispatchEvent(
252 *event
, dom::CallerType::System
, IgnoreErrors());
254 return useDefaultHandler
;
259 void ClientWebGLContext::EmulateLoseContext() const {
260 const FuncScope
funcScope(*this, "loseContext");
261 if (mLossStatus
!= webgl::LossStatus::Ready
) {
262 JsWarning("loseContext: Already lost.");
264 mNextError
= LOCAL_GL_INVALID_OPERATION
;
268 OnContextLoss(webgl::ContextLossReason::Manual
);
271 void ClientWebGLContext::OnContextLoss(
272 const webgl::ContextLossReason reason
) const {
273 JsWarning("WebGL context was lost.");
276 for (const auto& ext
: mNotLost
->extensions
) {
278 ext
->mContext
= nullptr; // Detach.
280 mNotLost
= {}; // Lost now!
281 mNextError
= LOCAL_GL_CONTEXT_LOST_WEBGL
;
285 case webgl::ContextLossReason::Guilty
:
286 mLossStatus
= webgl::LossStatus::LostForever
;
289 case webgl::ContextLossReason::None
:
290 mLossStatus
= webgl::LossStatus::Lost
;
293 case webgl::ContextLossReason::Manual
:
294 mLossStatus
= webgl::LossStatus::LostManually
;
298 const auto weak
= WeakPtr
<const ClientWebGLContext
>(this);
299 const auto fnRun
= [weak
]() {
300 const auto strong
= RefPtr
<const ClientWebGLContext
>(weak
);
302 strong
->Event_webglcontextlost();
304 already_AddRefed
<mozilla::CancelableRunnable
> runnable
=
305 NS_NewCancelableRunnableFunction("enqueue Event_webglcontextlost", fnRun
);
306 NS_DispatchToCurrentThread(std::move(runnable
));
309 void ClientWebGLContext::Event_webglcontextlost() const {
310 const bool useDefaultHandler
= DispatchEvent(u
"webglcontextlost"_ns
);
311 if (useDefaultHandler
) {
312 mLossStatus
= webgl::LossStatus::LostForever
;
315 if (mLossStatus
== webgl::LossStatus::Lost
) {
316 RestoreContext(webgl::LossStatus::Lost
);
320 void ClientWebGLContext::RestoreContext(
321 const webgl::LossStatus requiredStatus
) const {
322 if (requiredStatus
!= mLossStatus
) {
324 "restoreContext: Only valid iff context lost with loseContext().");
326 mNextError
= LOCAL_GL_INVALID_OPERATION
;
330 MOZ_RELEASE_ASSERT(mLossStatus
== webgl::LossStatus::Lost
||
331 mLossStatus
== webgl::LossStatus::LostManually
);
333 if (mAwaitingRestore
) return;
334 mAwaitingRestore
= true;
336 const auto weak
= WeakPtr
<const ClientWebGLContext
>(this);
337 const auto fnRun
= [weak
]() {
338 const auto strong
= RefPtr
<const ClientWebGLContext
>(weak
);
340 strong
->Event_webglcontextrestored();
342 already_AddRefed
<mozilla::CancelableRunnable
> runnable
=
343 NS_NewCancelableRunnableFunction("enqueue Event_webglcontextrestored",
345 NS_DispatchToCurrentThread(std::move(runnable
));
348 void ClientWebGLContext::Event_webglcontextrestored() const {
349 mAwaitingRestore
= false;
350 mLossStatus
= webgl::LossStatus::Ready
;
354 if (mCanvasElement
) {
355 requestSize
= {mCanvasElement
->Width(), mCanvasElement
->Height()};
356 } else if (mOffscreenCanvas
) {
357 requestSize
= {mOffscreenCanvas
->Width(), mOffscreenCanvas
->Height()};
359 MOZ_ASSERT_UNREACHABLE("no HTMLCanvasElement or OffscreenCanvas!");
363 if (!requestSize
.x
) {
366 if (!requestSize
.y
) {
370 const auto mutThis
= const_cast<ClientWebGLContext
*>(
371 this); // TODO: Make context loss non-mutable.
372 if (!mutThis
->CreateHostContext(requestSize
)) {
373 mLossStatus
= webgl::LossStatus::LostForever
;
379 (void)DispatchEvent(u
"webglcontextrestored"_ns
);
384 void ClientWebGLContext::ThrowEvent_WebGLContextCreationError(
385 const std::string
& text
) const {
387 msg
.AppendPrintf("Failed to create WebGL context: %s", text
.c_str());
388 JsWarning(msg
.BeginReading());
390 RefPtr
<dom::EventTarget
> target
= mCanvasElement
;
391 if (!target
&& mOffscreenCanvas
) {
392 target
= mOffscreenCanvas
;
393 } else if (!target
) {
397 const auto kEventName
= u
"webglcontextcreationerror"_ns
;
399 dom::WebGLContextEventInit eventInit
;
400 // eventInit.mCancelable = true; // The spec says this, but it's silly.
401 eventInit
.mStatusMessage
= NS_ConvertASCIItoUTF16(text
.c_str());
403 const RefPtr
<dom::WebGLContextEvent
> event
=
404 dom::WebGLContextEvent::Constructor(target
, kEventName
, eventInit
);
405 event
->SetTrusted(true);
407 target
->DispatchEvent(*event
);
410 // -------------------------------------------------------------------------
411 // Client-side helper methods. Dispatch to a Host method.
412 // -------------------------------------------------------------------------
414 // If we are running WebGL in this process then call the HostWebGLContext
415 // method directly. Otherwise, dispatch over IPC.
416 template <typename MethodT
, typename
... Args
>
417 void ClientWebGLContext::Run_WithDestArgTypes(
418 std::optional
<JS::AutoCheckCannotGC
>&& noGc
, const MethodT method
,
419 const size_t id
, const Args
&... args
) const {
421 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
423 // `AutoCheckCannotGC` must be reset after the GC data is done being used but
424 // *before* the `notLost` destructor runs, since the latter can GC.
425 const auto cleanup
= MakeScopeExit([&]() { noGc
.reset(); });
427 if (IsContextLost()) {
431 const auto& inProcess
= notLost
->inProcess
;
433 (inProcess
.get()->*method
)(args
...);
437 const auto& child
= notLost
->outOfProcess
;
439 const auto info
= webgl::SerializationInfo(id
, args
...);
440 const auto maybeDest
= child
->AllocPendingCmdBytes(info
.requiredByteCount
,
441 info
.alignmentOverhead
);
443 noGc
.reset(); // Reset early, as GC data will not be used, but JsWarning
445 JsWarning("Failed to allocate internal command buffer.");
446 OnContextLoss(webgl::ContextLossReason::None
);
449 const auto& destBytes
= *maybeDest
;
450 webgl::Serialize(destBytes
, id
, args
...);
455 #define RPROC(_METHOD) \
456 decltype(&HostWebGLContext::_METHOD), &HostWebGLContext::_METHOD
458 // ------------------------- Composition, etc -------------------------
460 void ClientWebGLContext::OnBeforePaintTransaction() { Present(nullptr); }
462 void ClientWebGLContext::EndComposition() {
463 // Mark ourselves as no longer invalidated.
469 layers::TextureType
ClientWebGLContext::GetTexTypeForSwapChain() const {
470 const RefPtr
<layers::ImageBridgeChild
> imageBridge
=
471 layers::ImageBridgeChild::GetSingleton();
472 const bool isOutOfProcess
= mNotLost
&& mNotLost
->outOfProcess
!= nullptr;
473 return layers::TexTypeForWebgl(imageBridge
, isOutOfProcess
);
476 void ClientWebGLContext::Present(WebGLFramebufferJS
* const xrFb
,
478 const webgl::SwapChainOptions
& options
) {
479 const auto texType
= GetTexTypeForSwapChain();
480 Present(xrFb
, texType
, webvr
, options
);
483 // Fill in remote texture ids to SwapChainOptions if async present is enabled.
484 webgl::SwapChainOptions
ClientWebGLContext::PrepareAsyncSwapChainOptions(
485 WebGLFramebufferJS
* fb
, bool webvr
,
486 const webgl::SwapChainOptions
& options
) {
487 // Currently remote texture ids should only be set internally.
488 MOZ_ASSERT(!options
.remoteTextureOwnerId
.IsValid() &&
489 !options
.remoteTextureId
.IsValid());
490 // Async present only works when out-of-process. It is not supported in WebVR.
491 // Allow it if it is either forced or if the pref is set.
495 if (!IsContextLost() && !mNotLost
->inProcess
&&
496 (options
.forceAsyncPresent
||
497 StaticPrefs::webgl_out_of_process_async_present())) {
498 if (!mRemoteTextureOwnerId
) {
499 mRemoteTextureOwnerId
= Some(layers::RemoteTextureOwnerId::GetNext());
501 mLastRemoteTextureId
= Some(layers::RemoteTextureId::GetNext());
502 webgl::SwapChainOptions asyncOptions
= options
;
503 asyncOptions
.remoteTextureOwnerId
= *mRemoteTextureOwnerId
;
504 asyncOptions
.remoteTextureId
= *mLastRemoteTextureId
;
507 // Clear the current remote texture id so that we disable async.
508 mRemoteTextureOwnerId
= Nothing();
512 void ClientWebGLContext::Present(WebGLFramebufferJS
* const xrFb
,
513 const layers::TextureType type
,
515 const webgl::SwapChainOptions
& options
) {
516 if (!mIsCanvasDirty
&& !xrFb
) return;
518 mIsCanvasDirty
= false;
521 webgl::SwapChainOptions asyncOptions
=
522 PrepareAsyncSwapChainOptions(xrFb
, webvr
, options
);
523 Run
<RPROC(Present
)>(xrFb
? xrFb
->mId
: 0, type
, webvr
, asyncOptions
);
526 void ClientWebGLContext::CopyToSwapChain(
527 WebGLFramebufferJS
* const fb
, const webgl::SwapChainOptions
& options
) {
529 const auto texType
= GetTexTypeForSwapChain();
530 webgl::SwapChainOptions asyncOptions
=
531 PrepareAsyncSwapChainOptions(fb
, false, options
);
532 Run
<RPROC(CopyToSwapChain
)>(fb
? fb
->mId
: 0, texType
, asyncOptions
);
535 void ClientWebGLContext::EndOfFrame() {
537 Run
<RPROC(EndOfFrame
)>();
540 Maybe
<layers::SurfaceDescriptor
> ClientWebGLContext::GetFrontBuffer(
541 WebGLFramebufferJS
* const fb
, bool vr
) {
542 const FuncScope
funcScope(*this, "<GetFrontBuffer>");
543 if (IsContextLost()) return {};
545 const auto& inProcess
= mNotLost
->inProcess
;
547 return inProcess
->GetFrontBuffer(fb
? fb
->mId
: 0, vr
);
550 const auto& child
= mNotLost
->outOfProcess
;
551 child
->FlushPendingCmds();
553 // Always synchronously get the front buffer if not using a remote texture.
554 bool needsSync
= true;
555 Maybe
<layers::SurfaceDescriptor
> syncDesc
;
556 Maybe
<layers::SurfaceDescriptor
> remoteDesc
;
557 auto& info
= child
->GetFlushedCmdInfo();
559 // If valid remote texture data was set for async present, then use it.
560 if (!fb
&& !vr
&& mRemoteTextureOwnerId
&& mLastRemoteTextureId
) {
561 const auto tooManyFlushes
= 10;
562 // If there are many flushed cmds, force synchronous IPC to avoid too many
563 // pending ipc messages. Otherwise don't sync for other cases to avoid any
564 // performance penalty.
565 needsSync
= XRE_IsParentProcess() ||
566 gfx::gfxVars::WebglOopAsyncPresentForceSync() ||
567 info
.flushesSinceLastCongestionCheck
> tooManyFlushes
;
569 // Only send over a remote texture descriptor if the WebGLChild actor is
570 // alive to ensure the remote texture id is valid.
571 if (child
->CanSend()) {
572 remoteDesc
= Some(layers::SurfaceDescriptorRemoteTexture(
573 *mLastRemoteTextureId
, *mRemoteTextureOwnerId
));
578 !child
->SendGetFrontBuffer(fb
? fb
->mId
: 0, vr
, &syncDesc
)) {
582 // Reset flushesSinceLastCongestionCheck
583 info
.flushesSinceLastCongestionCheck
= 0;
584 info
.congestionCheckGeneration
++;
586 // If there is a remote texture descriptor, use that preferentially, as the
587 // sync front buffer descriptor was only created to force a sync first.
588 return remoteDesc
? remoteDesc
: syncDesc
;
591 Maybe
<layers::SurfaceDescriptor
> ClientWebGLContext::PresentFrontBuffer(
592 WebGLFramebufferJS
* const fb
, bool webvr
) {
593 const auto texType
= GetTexTypeForSwapChain();
594 Present(fb
, texType
, webvr
);
595 return GetFrontBuffer(fb
, webvr
);
598 already_AddRefed
<layers::FwdTransactionTracker
>
599 ClientWebGLContext::UseCompositableForwarder(
600 layers::CompositableForwarder
* aForwarder
) {
601 if (mRemoteTextureOwnerId
) {
602 return layers::FwdTransactionTracker::GetOrCreate(mFwdTransactionTracker
);
607 void ClientWebGLContext::OnDestroyChild(dom::WebGLChild
* aChild
) {
608 // Since NotLostData may be destructing at this point, the RefPtr to
609 // WebGLChild may be unreliable. Instead, it must be explicitly passed in.
610 if (mRemoteTextureOwnerId
&& mFwdTransactionTracker
&&
611 mFwdTransactionTracker
->IsUsed()) {
612 (void)aChild
->SendWaitForTxn(
613 *mRemoteTextureOwnerId
,
614 layers::ToRemoteTextureTxnType(mFwdTransactionTracker
),
615 layers::ToRemoteTextureTxnId(mFwdTransactionTracker
));
619 void ClientWebGLContext::ClearVRSwapChain() { Run
<RPROC(ClearVRSwapChain
)>(); }
623 bool ClientWebGLContext::UpdateWebRenderCanvasData(
624 nsDisplayListBuilder
* aBuilder
, WebRenderCanvasData
* aCanvasData
) {
625 CanvasRenderer
* renderer
= aCanvasData
->GetCanvasRenderer();
627 if (!IsContextLost() && !mResetLayer
&& renderer
) {
631 const auto& size
= DrawingBufferSize();
633 if (!IsContextLost() && !renderer
&& mNotLost
->mCanvasRenderer
&&
634 mNotLost
->mCanvasRenderer
->GetSize() == gfx::IntSize(size
.x
, size
.y
) &&
635 aCanvasData
->SetCanvasRenderer(mNotLost
->mCanvasRenderer
)) {
636 mNotLost
->mCanvasRenderer
->SetDirty();
641 renderer
= aCanvasData
->CreateCanvasRenderer();
642 if (!InitializeCanvasRenderer(aBuilder
, renderer
)) {
643 // Clear CanvasRenderer of WebRenderCanvasData
644 aCanvasData
->ClearCanvasRenderer();
648 mNotLost
->mCanvasRenderer
= renderer
;
650 MOZ_ASSERT(renderer
);
656 bool ClientWebGLContext::InitializeCanvasRenderer(
657 nsDisplayListBuilder
* aBuilder
, CanvasRenderer
* aRenderer
) {
658 const FuncScope
funcScope(*this, "<InitializeCanvasRenderer>");
659 if (IsContextLost()) return false;
661 layers::CanvasRendererData data
;
662 data
.mContext
= this;
663 data
.mOriginPos
= gl::OriginPos::BottomLeft
;
665 const auto& options
= *mInitialOptions
;
666 const auto& size
= DrawingBufferSize();
668 if (IsContextLost()) return false;
670 data
.mIsOpaque
= !options
.alpha
;
671 data
.mIsAlphaPremult
= !options
.alpha
|| options
.premultipliedAlpha
;
672 data
.mSize
= {size
.x
, size
.y
};
674 if (aBuilder
->IsPaintingToWindow() && mCanvasElement
) {
675 data
.mDoPaintCallbacks
= true;
678 aRenderer
->Initialize(data
);
679 aRenderer
->SetDirty();
683 void ClientWebGLContext::UpdateCanvasParameters() {
684 if (!mOffscreenCanvas
) {
688 const auto& options
= *mInitialOptions
;
689 const auto& size
= DrawingBufferSize();
691 mozilla::dom::OffscreenCanvasDisplayData data
;
692 data
.mOriginPos
= gl::OriginPos::BottomLeft
;
693 data
.mIsOpaque
= !options
.alpha
;
694 data
.mIsAlphaPremult
= !options
.alpha
|| options
.premultipliedAlpha
;
695 data
.mSize
= {size
.x
, size
.y
};
696 data
.mDoPaintCallbacks
= false;
698 mOffscreenCanvas
->UpdateDisplayData(data
);
701 layers::LayersBackend
ClientWebGLContext::GetCompositorBackendType() const {
702 if (mCanvasElement
) {
703 return mCanvasElement
->GetCompositorBackendType();
704 } else if (mOffscreenCanvas
) {
705 return mOffscreenCanvas
->GetCompositorBackendType();
708 return layers::LayersBackend::LAYERS_NONE
;
711 mozilla::dom::Document
* ClientWebGLContext::GetOwnerDoc() const {
712 MOZ_ASSERT(mCanvasElement
);
713 if (!mCanvasElement
) {
716 return mCanvasElement
->OwnerDoc();
719 void ClientWebGLContext::Commit() {
720 if (mOffscreenCanvas
) {
721 mOffscreenCanvas
->CommitFrameToCompositor();
725 void ClientWebGLContext::GetCanvas(
726 dom::Nullable
<dom::OwningHTMLCanvasElementOrOffscreenCanvas
>& retval
) {
727 if (mCanvasElement
) {
728 MOZ_RELEASE_ASSERT(!mOffscreenCanvas
, "GFX: Canvas is offscreen.");
730 if (mCanvasElement
->IsInNativeAnonymousSubtree()) {
733 retval
.SetValue().SetAsHTMLCanvasElement() = mCanvasElement
;
735 } else if (mOffscreenCanvas
) {
736 retval
.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas
;
742 void ClientWebGLContext::SetDrawingBufferColorSpace(
743 const dom::PredefinedColorSpace val
) {
744 mDrawingBufferColorSpace
= val
;
745 Run
<RPROC(SetDrawingBufferColorSpace
)>(*mDrawingBufferColorSpace
);
748 void ClientWebGLContext::SetUnpackColorSpace(
749 const dom::PredefinedColorSpace val
) {
750 mUnpackColorSpace
= val
;
751 Run
<RPROC(SetUnpackColorSpace
)>(*mUnpackColorSpace
);
754 void ClientWebGLContext::GetContextAttributes(
755 dom::Nullable
<dom::WebGLContextAttributes
>& retval
) {
757 const FuncScope
funcScope(*this, "getContextAttributes");
758 if (IsContextLost()) return;
760 dom::WebGLContextAttributes
& result
= retval
.SetValue();
762 const auto& options
= mNotLost
->info
.options
;
764 result
.mAlpha
.Construct(options
.alpha
);
765 result
.mDepth
= options
.depth
;
766 result
.mStencil
= options
.stencil
;
767 result
.mAntialias
.Construct(options
.antialias
);
768 result
.mPremultipliedAlpha
= options
.premultipliedAlpha
;
769 result
.mPreserveDrawingBuffer
= options
.preserveDrawingBuffer
;
770 result
.mFailIfMajorPerformanceCaveat
= options
.failIfMajorPerformanceCaveat
;
771 result
.mPowerPreference
= options
.powerPreference
;
772 result
.mForceSoftwareRendering
= options
.forceSoftwareRendering
;
775 // -----------------------
778 ClientWebGLContext::SetDimensions(const int32_t signedWidth
,
779 const int32_t signedHeight
) {
780 const FuncScope
funcScope(*this, "<SetDimensions>");
781 MOZ_ASSERT(mInitialOptions
);
783 if (mLossStatus
!= webgl::LossStatus::Ready
) {
784 // Attempted resize of a lost context.
788 uvec2 size
= {static_cast<uint32_t>(signedWidth
),
789 static_cast<uint32_t>(signedHeight
)};
796 const auto prevRequestedSize
= mRequestedSize
;
797 mRequestedSize
= size
;
799 mResetLayer
= true; // Always treat this as resize.
802 auto& state
= State();
804 auto curSize
= prevRequestedSize
;
805 if (state
.mDrawingBufferSize
) {
806 curSize
= *state
.mDrawingBufferSize
;
808 if (size
== curSize
) return NS_OK
; // MUST skip no-op resize
810 state
.mDrawingBufferSize
= Nothing();
811 Run
<RPROC(Resize
)>(size
);
813 UpdateCanvasParameters();
819 // Context (re-)creation
821 if (!CreateHostContext(size
)) {
822 return NS_ERROR_FAILURE
;
827 void ClientWebGLContext::ResetBitmap() {
828 const auto size
= DrawingBufferSize();
829 Run
<RPROC(Resize
)>(size
); // No-change resize still clears/resets everything.
832 static bool IsWebglOutOfProcessEnabled() {
833 if (StaticPrefs::webgl_out_of_process_force()) {
836 if (!gfx::gfxVars::AllowWebglOop()) {
839 if (!NS_IsMainThread()) {
840 return StaticPrefs::webgl_out_of_process_worker();
842 return StaticPrefs::webgl_out_of_process();
845 bool ClientWebGLContext::CreateHostContext(const uvec2
& requestedSize
) {
846 const auto pNotLost
= std::make_shared
<webgl::NotLostData
>(*this);
847 auto& notLost
= *pNotLost
;
849 auto res
= [&]() -> Result
<Ok
, std::string
> {
850 auto options
= *mInitialOptions
;
851 if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) {
852 options
.failIfMajorPerformanceCaveat
= false;
855 if (options
.failIfMajorPerformanceCaveat
) {
856 const auto backend
= GetCompositorBackendType();
857 bool isCompositorSlow
= false;
858 isCompositorSlow
|= (backend
== layers::LayersBackend::LAYERS_WR
&&
859 gfx::gfxVars::UseSoftwareWebRender());
861 if (isCompositorSlow
) {
863 "failIfMajorPerformanceCaveat: Compositor is not"
864 " hardware-accelerated.");
868 const bool resistFingerprinting
=
869 ShouldResistFingerprinting(RFPTarget::WebGLRenderCapability
);
870 const auto principalKey
= GetPrincipalHashValue();
871 const auto initDesc
= webgl::InitContextDesc
{
872 .isWebgl2
= mIsWebGL2
,
873 .resistFingerprinting
= resistFingerprinting
,
874 .principalKey
= principalKey
,
875 .size
= requestedSize
,
881 auto useOop
= IsWebglOutOfProcessEnabled();
882 if (XRE_IsParentProcess()) {
888 HostWebGLContext::Create({this, nullptr}, initDesc
, ¬Lost
.info
);
894 ScopedGfxFeatureReporter
reporter("IpcWebGL");
896 auto* const cm
= gfx::CanvasManagerChild::Get();
897 if (NS_WARN_IF(!cm
)) {
898 return Err("!CanvasManagerChild::Get()");
901 RefPtr
<dom::WebGLChild
> outOfProcess
= new dom::WebGLChild(*this);
903 static_cast<dom::WebGLChild
*>(cm
->SendPWebGLConstructor(outOfProcess
));
905 return Err("SendPWebGLConstructor failed");
908 // Clear RemoteTextureOwnerId. HostWebGLContext is going to be replaced in
910 if (mRemoteTextureOwnerId
.isSome()) {
911 mRemoteTextureOwnerId
= Nothing();
912 mFwdTransactionTracker
= nullptr;
915 if (!outOfProcess
->SendInitialize(initDesc
, ¬Lost
.info
)) {
916 return Err("WebGL actor Initialize failed");
919 notLost
.outOfProcess
= outOfProcess
;
920 reporter
.SetSuccessful();
924 auto str
= res
.unwrapErr();
925 if (StartsWith(str
, "failIfMajorPerformanceCaveat")) {
927 " (about:config override available:"
928 " webgl.disable-fail-if-major-performance-caveat)";
930 notLost
.info
.error
= str
;
932 if (!notLost
.info
.error
->empty()) {
933 ThrowEvent_WebGLContextCreationError(notLost
.info
.error
);
937 UpdateCanvasParameters();
941 const auto& limits
= Limits();
942 auto& state
= State();
943 state
.mIsEnabledMap
= webgl::MakeIsEnabledMap(mIsWebGL2
);
945 state
.mDefaultTfo
= new WebGLTransformFeedbackJS(*this);
946 state
.mDefaultVao
= new WebGLVertexArrayJS(this);
948 state
.mBoundTfo
= state
.mDefaultTfo
;
949 state
.mBoundVao
= state
.mDefaultVao
;
951 (void)state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
];
953 state
.mTexUnits
.resize(limits
.maxTexUnits
);
954 state
.mBoundUbos
.resize(limits
.maxUniformBufferBindings
);
957 webgl::TypedQuad initVal
;
958 const float fData
[4] = {0, 0, 0, 1};
959 memcpy(initVal
.data
.data(), fData
, initVal
.data
.size());
960 state
.mGenericVertexAttribs
.resize(limits
.maxVertexAttribs
, initVal
);
963 const auto& size
= DrawingBufferSize();
964 state
.mViewport
= {0, 0, static_cast<int32_t>(size
.x
),
965 static_cast<int32_t>(size
.y
)};
966 state
.mScissor
= state
.mViewport
;
969 // Insert keys to enable slots:
970 (void)state
.mBoundBufferByTarget
[LOCAL_GL_COPY_READ_BUFFER
];
971 (void)state
.mBoundBufferByTarget
[LOCAL_GL_COPY_WRITE_BUFFER
];
972 (void)state
.mBoundBufferByTarget
[LOCAL_GL_PIXEL_PACK_BUFFER
];
973 (void)state
.mBoundBufferByTarget
[LOCAL_GL_PIXEL_UNPACK_BUFFER
];
974 (void)state
.mBoundBufferByTarget
[LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
];
975 (void)state
.mBoundBufferByTarget
[LOCAL_GL_UNIFORM_BUFFER
];
977 (void)state
.mCurrentQueryByTarget
[LOCAL_GL_ANY_SAMPLES_PASSED
];
978 //(void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE];
979 //// Same slot as ANY_SAMPLES_PASSED.
981 .mCurrentQueryByTarget
[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
];
987 std::unordered_map
<GLenum
, bool> webgl::MakeIsEnabledMap(const bool webgl2
) {
988 auto ret
= std::unordered_map
<GLenum
, bool>{};
990 ret
[LOCAL_GL_BLEND
] = false;
991 ret
[LOCAL_GL_CULL_FACE
] = false;
992 ret
[LOCAL_GL_DEPTH_TEST
] = false;
993 ret
[LOCAL_GL_DITHER
] = true;
994 ret
[LOCAL_GL_POLYGON_OFFSET_FILL
] = false;
995 ret
[LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
] = false;
996 ret
[LOCAL_GL_SAMPLE_COVERAGE
] = false;
997 ret
[LOCAL_GL_SCISSOR_TEST
] = false;
998 ret
[LOCAL_GL_STENCIL_TEST
] = false;
1001 ret
[LOCAL_GL_RASTERIZER_DISCARD
] = false;
1009 uvec2
ClientWebGLContext::DrawingBufferSize() {
1010 if (IsContextLost()) return {};
1011 const auto notLost
=
1012 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1013 auto& state
= State();
1014 auto& size
= state
.mDrawingBufferSize
;
1017 const auto& inProcess
= mNotLost
->inProcess
;
1019 size
= Some(inProcess
->DrawingBufferSize());
1021 const auto& child
= mNotLost
->outOfProcess
;
1022 child
->FlushPendingCmds();
1024 if (!child
->SendDrawingBufferSize(&actual
)) return {};
1025 size
= Some(actual
);
1032 void ClientWebGLContext::OnMemoryPressure() {
1033 if (IsContextLost()) return;
1035 const auto& inProcess
= mNotLost
->inProcess
;
1037 return inProcess
->OnMemoryPressure();
1039 const auto& child
= mNotLost
->outOfProcess
;
1040 (void)child
->SendOnMemoryPressure();
1044 ClientWebGLContext::SetContextOptions(JSContext
* cx
,
1045 JS::Handle
<JS::Value
> options
,
1046 ErrorResult
& aRvForDictionaryInit
) {
1047 if (mInitialOptions
&& options
.isNullOrUndefined()) return NS_OK
;
1049 dom::WebGLContextAttributes attributes
;
1050 if (!attributes
.Init(cx
, options
)) {
1051 aRvForDictionaryInit
.Throw(NS_ERROR_UNEXPECTED
);
1052 return NS_ERROR_UNEXPECTED
;
1055 WebGLContextOptions newOpts
;
1057 newOpts
.stencil
= attributes
.mStencil
;
1058 newOpts
.depth
= attributes
.mDepth
;
1059 newOpts
.premultipliedAlpha
= attributes
.mPremultipliedAlpha
;
1060 newOpts
.preserveDrawingBuffer
= attributes
.mPreserveDrawingBuffer
;
1061 newOpts
.failIfMajorPerformanceCaveat
=
1062 attributes
.mFailIfMajorPerformanceCaveat
;
1063 newOpts
.xrCompatible
= attributes
.mXrCompatible
;
1064 newOpts
.powerPreference
= attributes
.mPowerPreference
;
1065 newOpts
.forceSoftwareRendering
= attributes
.mForceSoftwareRendering
;
1066 newOpts
.enableDebugRendererInfo
=
1067 StaticPrefs::webgl_enable_debug_renderer_info();
1068 MOZ_ASSERT(mCanvasElement
|| mOffscreenCanvas
);
1069 newOpts
.shouldResistFingerprinting
=
1070 ShouldResistFingerprinting(RFPTarget::WebGLRenderCapability
);
1072 if (attributes
.mAlpha
.WasPassed()) {
1073 newOpts
.alpha
= attributes
.mAlpha
.Value();
1075 if (attributes
.mAntialias
.WasPassed()) {
1076 newOpts
.antialias
= attributes
.mAntialias
.Value();
1079 // Don't do antialiasing if we've disabled MSAA.
1080 if (!StaticPrefs::webgl_msaa_samples()) {
1081 newOpts
.antialias
= false;
1086 if (mInitialOptions
&& *mInitialOptions
!= newOpts
) {
1087 // Err if the options asked for aren't the same as what they were
1089 return NS_ERROR_FAILURE
;
1092 mXRCompatible
= attributes
.mXrCompatible
;
1094 mInitialOptions
.emplace(newOpts
);
1098 void ClientWebGLContext::DidRefresh() { Run
<RPROC(DidRefresh
)>(); }
1100 already_AddRefed
<gfx::SourceSurface
> ClientWebGLContext::GetSurfaceSnapshot(
1101 gfxAlphaType
* const out_alphaType
) {
1102 const FuncScope
funcScope(*this, "<GetSurfaceSnapshot>");
1103 if (IsContextLost()) return nullptr;
1104 const auto notLost
=
1105 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1107 auto ret
= BackBufferSnapshot();
1108 if (!ret
) return nullptr;
1112 const auto& options
= mNotLost
->info
.options
;
1114 auto srcAlphaType
= gfxAlphaType::Opaque
;
1115 if (options
.alpha
) {
1116 if (options
.premultipliedAlpha
) {
1117 srcAlphaType
= gfxAlphaType::Premult
;
1119 srcAlphaType
= gfxAlphaType::NonPremult
;
1123 if (out_alphaType
) {
1124 *out_alphaType
= srcAlphaType
;
1126 // Expects Opaque or Premult
1127 if (srcAlphaType
== gfxAlphaType::NonPremult
) {
1128 const gfx::DataSourceSurface::ScopedMap
map(
1129 ret
, gfx::DataSourceSurface::READ_WRITE
);
1130 MOZ_RELEASE_ASSERT(map
.IsMapped(), "Failed to map snapshot surface!");
1132 const auto& size
= ret
->GetSize();
1133 const auto format
= ret
->GetFormat();
1135 gfx::PremultiplyData(map
.GetData(), map
.GetStride(), format
,
1136 map
.GetData(), map
.GetStride(), format
, size
);
1137 MOZ_RELEASE_ASSERT(rv
, "PremultiplyData failed!");
1141 return ret
.forget();
1144 RefPtr
<gfx::SourceSurface
> ClientWebGLContext::GetFrontBufferSnapshot(
1145 const bool requireAlphaPremult
) {
1146 const FuncScope
funcScope(*this, "<GetSurfaceSnapshot>");
1147 if (IsContextLost()) return nullptr;
1148 const auto notLost
=
1149 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1151 const auto& options
= mNotLost
->info
.options
;
1153 const auto surfFormat
= options
.alpha
? gfx::SurfaceFormat::B8G8R8A8
1154 : gfx::SurfaceFormat::B8G8R8X8
;
1156 const auto fnNewSurf
= [&](const uvec2 size
) {
1157 const auto stride
= size
.x
* 4;
1158 return RefPtr
<gfx::DataSourceSurface
>(
1159 gfx::Factory::CreateDataSourceSurfaceWithStride({size
.x
, size
.y
},
1164 const auto& inProcess
= mNotLost
->inProcess
;
1166 const auto maybeSize
= inProcess
->FrontBufferSnapshotInto({});
1167 if (!maybeSize
) return nullptr;
1168 const auto& surfSize
= *maybeSize
;
1169 const auto stride
= surfSize
.x
* 4;
1170 const auto byteSize
= stride
* surfSize
.y
;
1171 const auto surf
= fnNewSurf(surfSize
);
1172 if (!surf
) return nullptr;
1174 const gfx::DataSourceSurface::ScopedMap
map(
1175 surf
, gfx::DataSourceSurface::READ_WRITE
);
1176 if (!map
.IsMapped()) {
1180 MOZ_RELEASE_ASSERT(map
.GetStride() == static_cast<int64_t>(stride
));
1181 auto range
= Range
<uint8_t>{map
.GetData(), byteSize
};
1182 if (!inProcess
->FrontBufferSnapshotInto(Some(range
))) {
1183 gfxCriticalNote
<< "ClientWebGLContext::GetFrontBufferSnapshot: "
1184 "FrontBufferSnapshotInto(some) failed after "
1185 "FrontBufferSnapshotInto(none)";
1188 if (requireAlphaPremult
&& options
.alpha
&& !options
.premultipliedAlpha
) {
1189 bool rv
= gfx::PremultiplyData(
1190 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::R8G8B8A8
,
1191 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1193 MOZ_RELEASE_ASSERT(rv
, "PremultiplyData failed!");
1195 bool rv
= gfx::SwizzleData(
1196 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::R8G8B8A8
,
1197 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1199 MOZ_RELEASE_ASSERT(rv
, "SwizzleData failed!");
1204 const auto& child
= mNotLost
->outOfProcess
;
1205 child
->FlushPendingCmds();
1206 webgl::FrontBufferSnapshotIpc res
;
1207 if (!child
->SendGetFrontBufferSnapshot(&res
)) {
1210 if (!res
.shmem
) return nullptr;
1212 const auto& surfSize
= res
.surfSize
;
1213 const webgl::RaiiShmem shmem
{child
, res
.shmem
.ref()};
1214 if (!shmem
) return nullptr;
1215 const auto& shmemBytes
= shmem
.ByteRange();
1216 if (!surfSize
.x
) return nullptr; // Zero means failure.
1218 const auto stride
= surfSize
.x
* 4;
1219 const auto byteSize
= stride
* surfSize
.y
;
1221 const auto surf
= fnNewSurf(surfSize
);
1222 if (!surf
) return nullptr;
1225 const gfx::DataSourceSurface::ScopedMap
map(
1226 surf
, gfx::DataSourceSurface::READ_WRITE
);
1227 if (!map
.IsMapped()) {
1231 MOZ_RELEASE_ASSERT(shmemBytes
.length() == byteSize
);
1232 if (requireAlphaPremult
&& options
.alpha
&& !options
.premultipliedAlpha
) {
1233 bool rv
= gfx::PremultiplyData(
1234 shmemBytes
.begin().get(), stride
, gfx::SurfaceFormat::R8G8B8A8
,
1235 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1237 MOZ_RELEASE_ASSERT(rv
, "PremultiplyData failed!");
1239 bool rv
= gfx::SwizzleData(shmemBytes
.begin().get(), stride
,
1240 gfx::SurfaceFormat::R8G8B8A8
, map
.GetData(),
1241 map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1243 MOZ_RELEASE_ASSERT(rv
, "SwizzleData failed!");
1249 RefPtr
<gfx::DataSourceSurface
> ClientWebGLContext::BackBufferSnapshot() {
1250 if (IsContextLost()) return nullptr;
1251 const auto notLost
=
1252 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1254 const auto& options
= mNotLost
->info
.options
;
1255 const auto& state
= State();
1257 const auto drawFbWas
= state
.mBoundDrawFb
;
1258 const auto readFbWas
= state
.mBoundReadFb
;
1260 Find(state
.mBoundBufferByTarget
, LOCAL_GL_PIXEL_PACK_BUFFER
);
1262 const auto size
= DrawingBufferSize();
1266 BindFramebuffer(LOCAL_GL_FRAMEBUFFER
, nullptr);
1268 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, nullptr);
1271 auto reset
= MakeScopeExit([&] {
1272 if (drawFbWas
== readFbWas
) {
1273 BindFramebuffer(LOCAL_GL_FRAMEBUFFER
, drawFbWas
);
1275 BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, drawFbWas
);
1276 BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, readFbWas
);
1279 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, pboWas
);
1283 const auto surfFormat
= options
.alpha
? gfx::SurfaceFormat::B8G8R8A8
1284 : gfx::SurfaceFormat::B8G8R8X8
;
1285 const auto stride
= size
.x
* 4;
1286 RefPtr
<gfx::DataSourceSurface
> surf
=
1287 gfx::Factory::CreateDataSourceSurfaceWithStride(
1288 {size
.x
, size
.y
}, surfFormat
, stride
, /*zero=*/true);
1289 if (NS_WARN_IF(!surf
)) {
1290 // Was this an OOM or alloc-limit? (500MB is our default resource size
1292 surf
= gfx::Factory::CreateDataSourceSurfaceWithStride({1, 1}, surfFormat
,
1295 // Still failed for a 1x1 size.
1296 gfxCriticalError() << "CreateDataSourceSurfaceWithStride(surfFormat="
1297 << surfFormat
<< ") failed.";
1303 const gfx::DataSourceSurface::ScopedMap
map(
1304 surf
, gfx::DataSourceSurface::READ_WRITE
);
1305 if (!map
.IsMapped()) {
1309 MOZ_ASSERT(static_cast<uint32_t>(map
.GetStride()) == stride
);
1311 const auto desc
= webgl::ReadPixelsDesc
{{0, 0}, size
};
1312 const auto pixels
= Span
<uint8_t>(map
.GetData(), stride
* size
.y
);
1313 if (!DoReadPixels(desc
, pixels
)) return nullptr;
1315 // RGBA->BGRA and flip-y.
1316 MOZ_RELEASE_ASSERT(gfx::SwizzleYFlipData(
1317 pixels
.data(), stride
, gfx::SurfaceFormat::R8G8B8A8
, pixels
.data(),
1318 stride
, gfx::SurfaceFormat::B8G8R8A8
, {size
.x
, size
.y
}));
1324 UniquePtr
<uint8_t[]> ClientWebGLContext::GetImageBuffer(
1325 int32_t* out_format
, gfx::IntSize
* out_imageSize
) {
1327 *out_imageSize
= {};
1329 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1331 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
1332 if (!snapshot
) return nullptr;
1334 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1336 const auto& premultAlpha
= mNotLost
->info
.options
.premultipliedAlpha
;
1337 *out_imageSize
= dataSurface
->GetSize();
1339 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization
)) {
1340 return gfxUtils::GetImageBufferWithRandomNoise(
1341 dataSurface
, premultAlpha
, GetCookieJarSettings(), out_format
);
1344 return gfxUtils::GetImageBuffer(dataSurface
, premultAlpha
, out_format
);
1348 ClientWebGLContext::GetInputStream(const char* mimeType
,
1349 const nsAString
& encoderOptions
,
1350 nsIInputStream
** out_stream
) {
1351 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1353 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
1354 if (!snapshot
) return NS_ERROR_FAILURE
;
1356 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1357 const auto& premultAlpha
= mNotLost
->info
.options
.premultipliedAlpha
;
1359 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization
)) {
1360 return gfxUtils::GetInputStreamWithRandomNoise(
1361 dataSurface
, premultAlpha
, mimeType
, encoderOptions
,
1362 GetCookieJarSettings(), out_stream
);
1365 return gfxUtils::GetInputStream(dataSurface
, premultAlpha
, mimeType
,
1366 encoderOptions
, out_stream
);
1369 // ------------------------- Client WebGL Objects -------------------------
1370 // ------------------------- Create/Destroy/Is -------------------------
1372 template <typename T
>
1373 static already_AddRefed
<T
> AsAddRefed(T
* ptr
) {
1378 template <typename T
>
1379 static RefPtr
<T
> AsRefPtr(T
* ptr
) {
1383 already_AddRefed
<WebGLBufferJS
> ClientWebGLContext::CreateBuffer() const {
1384 const FuncScope
funcScope(*this, "createBuffer");
1386 auto ret
= AsRefPtr(new WebGLBufferJS(*this));
1387 Run
<RPROC(CreateBuffer
)>(ret
->mId
);
1388 return ret
.forget();
1391 already_AddRefed
<WebGLFramebufferJS
> ClientWebGLContext::CreateFramebuffer()
1393 const FuncScope
funcScope(*this, "createFramebuffer");
1395 auto ret
= AsRefPtr(new WebGLFramebufferJS(*this));
1396 Run
<RPROC(CreateFramebuffer
)>(ret
->mId
);
1397 return ret
.forget();
1400 already_AddRefed
<WebGLFramebufferJS
>
1401 ClientWebGLContext::CreateOpaqueFramebuffer(
1402 const webgl::OpaqueFramebufferOptions
& options
) const {
1403 const FuncScope
funcScope(*this, "createOpaqueFramebuffer");
1405 auto ret
= AsRefPtr(new WebGLFramebufferJS(*this, true));
1408 const auto& inProcess
= mNotLost
->inProcess
;
1410 if (!inProcess
->CreateOpaqueFramebuffer(ret
->mId
, options
)) {
1413 return ret
.forget();
1415 const auto& child
= mNotLost
->outOfProcess
;
1416 child
->FlushPendingCmds();
1418 if (!child
->SendCreateOpaqueFramebuffer(ret
->mId
, options
, &ok
))
1420 if (!ok
) return nullptr;
1423 return ret
.forget();
1426 already_AddRefed
<WebGLProgramJS
> ClientWebGLContext::CreateProgram() const {
1427 const FuncScope
funcScope(*this, "createProgram");
1429 auto ret
= AsRefPtr(new WebGLProgramJS(*this));
1430 Run
<RPROC(CreateProgram
)>(ret
->mId
);
1431 return ret
.forget();
1434 already_AddRefed
<WebGLQueryJS
> ClientWebGLContext::CreateQuery() const {
1435 const FuncScope
funcScope(*this, "createQuery");
1437 auto ret
= AsRefPtr(new WebGLQueryJS(this));
1438 Run
<RPROC(CreateQuery
)>(ret
->mId
);
1439 return ret
.forget();
1442 already_AddRefed
<WebGLRenderbufferJS
> ClientWebGLContext::CreateRenderbuffer()
1444 const FuncScope
funcScope(*this, "createRenderbuffer");
1446 auto ret
= AsRefPtr(new WebGLRenderbufferJS(*this));
1447 Run
<RPROC(CreateRenderbuffer
)>(ret
->mId
);
1448 return ret
.forget();
1451 already_AddRefed
<WebGLSamplerJS
> ClientWebGLContext::CreateSampler() const {
1452 const FuncScope
funcScope(*this, "createSampler");
1454 auto ret
= AsRefPtr(new WebGLSamplerJS(*this));
1455 Run
<RPROC(CreateSampler
)>(ret
->mId
);
1456 return ret
.forget();
1459 already_AddRefed
<WebGLShaderJS
> ClientWebGLContext::CreateShader(
1460 const GLenum type
) const {
1461 const FuncScope
funcScope(*this, "createShader");
1464 case LOCAL_GL_VERTEX_SHADER
:
1465 case LOCAL_GL_FRAGMENT_SHADER
:
1468 EnqueueError_ArgEnum("type", type
);
1472 auto ret
= AsRefPtr(new WebGLShaderJS(*this, type
));
1473 Run
<RPROC(CreateShader
)>(ret
->mId
, ret
->mType
);
1474 return ret
.forget();
1477 already_AddRefed
<WebGLSyncJS
> ClientWebGLContext::FenceSync(
1478 const GLenum condition
, const GLbitfield flags
) const {
1479 const FuncScope
funcScope(*this, "fenceSync");
1481 if (condition
!= LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE
) {
1482 EnqueueError_ArgEnum("condition", condition
);
1487 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`flags` must be 0.");
1491 auto ret
= AsRefPtr(new WebGLSyncJS(*this));
1492 Run
<RPROC(CreateSync
)>(ret
->mId
);
1494 auto& availRunnable
= EnsureAvailabilityRunnable();
1495 availRunnable
.mSyncs
.push_back(ret
.get());
1496 ret
->mCanBeAvailable
= false;
1500 return ret
.forget();
1503 already_AddRefed
<WebGLTextureJS
> ClientWebGLContext::CreateTexture() const {
1504 const FuncScope
funcScope(*this, "createTexture");
1506 auto ret
= AsRefPtr(new WebGLTextureJS(*this));
1507 Run
<RPROC(CreateTexture
)>(ret
->mId
);
1508 return ret
.forget();
1511 already_AddRefed
<WebGLTransformFeedbackJS
>
1512 ClientWebGLContext::CreateTransformFeedback() const {
1513 const FuncScope
funcScope(*this, "createTransformFeedback");
1515 auto ret
= AsRefPtr(new WebGLTransformFeedbackJS(*this));
1516 Run
<RPROC(CreateTransformFeedback
)>(ret
->mId
);
1517 return ret
.forget();
1520 already_AddRefed
<WebGLVertexArrayJS
> ClientWebGLContext::CreateVertexArray()
1522 const FuncScope
funcScope(*this, "createVertexArray");
1524 auto ret
= AsRefPtr(new WebGLVertexArrayJS(this));
1525 Run
<RPROC(CreateVertexArray
)>(ret
->mId
);
1526 return ret
.forget();
1531 static bool ValidateOrSkipForDelete(const ClientWebGLContext
& context
,
1532 const webgl::ObjectJS
* const obj
) {
1533 if (!obj
) return false;
1534 if (!obj
->ValidateForContext(context
, "obj")) return false;
1535 if (obj
->IsDeleted()) return false;
1539 void ClientWebGLContext::DeleteBuffer(WebGLBufferJS
* const obj
) {
1540 const FuncScope
funcScope(*this, "deleteBuffer");
1541 if (IsContextLost()) return;
1542 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1543 auto& state
= State();
1545 // Unbind from all bind points and bound containers
1548 for (const auto i
: IntegerRange(state
.mBoundUbos
.size())) {
1549 if (state
.mBoundUbos
[i
] == obj
) {
1550 BindBufferBase(LOCAL_GL_UNIFORM_BUFFER
, i
, nullptr);
1554 // TFO only if not active
1555 if (!state
.mBoundTfo
->mActiveOrPaused
) {
1556 const auto& buffers
= state
.mBoundTfo
->mAttribBuffers
;
1557 for (const auto i
: IntegerRange(buffers
.size())) {
1558 if (buffers
[i
] == obj
) {
1559 BindBufferBase(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
, i
, nullptr);
1564 // Generic/global bind points
1565 for (const auto& pair
: state
.mBoundBufferByTarget
) {
1566 if (pair
.second
== obj
) {
1567 BindBuffer(pair
.first
, nullptr);
1572 if (state
.mBoundVao
->mIndexBuffer
== obj
) {
1573 BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER
, nullptr);
1576 const auto& vaoBuffers
= state
.mBoundVao
->mAttribBuffers
;
1577 Maybe
<WebGLBufferJS
*> toRestore
;
1578 for (const auto i
: IntegerRange(vaoBuffers
.size())) {
1579 if (vaoBuffers
[i
] == obj
) {
1582 Some(state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
].get());
1584 BindBuffer(LOCAL_GL_ARRAY_BUFFER
, nullptr);
1587 VertexAttribPointer(i
, 4, LOCAL_GL_FLOAT
, false, 0, 0);
1590 if (toRestore
&& *toRestore
) {
1591 BindBuffer(LOCAL_GL_ARRAY_BUFFER
, *toRestore
);
1596 obj
->mDeleteRequested
= true;
1597 Run
<RPROC(DeleteBuffer
)>(obj
->mId
);
1600 void ClientWebGLContext::DeleteFramebuffer(WebGLFramebufferJS
* const obj
,
1601 bool canDeleteOpaque
) {
1602 const FuncScope
funcScope(*this, "deleteFramebuffer");
1603 if (IsContextLost()) return;
1604 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1605 if (!canDeleteOpaque
&& obj
->mOpaque
) {
1607 LOCAL_GL_INVALID_OPERATION
,
1608 "An opaque framebuffer's attachments cannot be inspected or changed.");
1611 const auto& state
= State();
1614 const auto fnDetach
= [&](const GLenum target
,
1615 const WebGLFramebufferJS
* const fb
) {
1617 BindFramebuffer(target
, nullptr);
1620 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1621 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1623 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1624 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1627 obj
->mDeleteRequested
= true;
1628 Run
<RPROC(DeleteFramebuffer
)>(obj
->mId
);
1631 void ClientWebGLContext::DeleteProgram(WebGLProgramJS
* const obj
) const {
1632 const FuncScope
funcScope(*this, "deleteProgram");
1633 if (IsContextLost()) return;
1634 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1638 obj
->mKeepAlive
= nullptr;
1641 webgl::ProgramKeepAlive::~ProgramKeepAlive() {
1642 if (!mParent
) return;
1643 const auto& context
= mParent
->Context();
1644 if (!context
) return;
1645 context
->DoDeleteProgram(*mParent
);
1648 void ClientWebGLContext::DoDeleteProgram(WebGLProgramJS
& obj
) const {
1649 obj
.mNextLink_Shaders
= {};
1650 Run
<RPROC(DeleteProgram
)>(obj
.mId
);
1653 static GLenum
QuerySlotTarget(const GLenum specificTarget
);
1655 void ClientWebGLContext::DeleteQuery(WebGLQueryJS
* const obj
) {
1656 const FuncScope
funcScope(*this, "deleteQuery");
1657 if (IsContextLost()) return;
1658 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1659 const auto& state
= State();
1661 // Unbind if current
1664 // Despite mTarget being set, we may not have called BeginQuery on this
1665 // object. QueryCounter may also set mTarget.
1666 const auto slotTarget
= QuerySlotTarget(obj
->mTarget
);
1667 const auto curForTarget
=
1668 MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
1670 if (curForTarget
&& *curForTarget
== obj
) {
1671 EndQuery(obj
->mTarget
);
1675 obj
->mDeleteRequested
= true;
1676 Run
<RPROC(DeleteQuery
)>(obj
->mId
);
1679 void ClientWebGLContext::DeleteRenderbuffer(WebGLRenderbufferJS
* const obj
) {
1680 const FuncScope
funcScope(*this, "deleteRenderbuffer");
1681 if (IsContextLost()) return;
1682 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1683 const auto& state
= State();
1686 if (state
.mBoundRb
== obj
) {
1687 BindRenderbuffer(LOCAL_GL_RENDERBUFFER
, nullptr);
1690 // Unbind from bound FBs
1691 const auto fnDetach
= [&](const GLenum target
,
1692 const WebGLFramebufferJS
* const fb
) {
1694 for (const auto& pair
: fb
->mAttachments
) {
1695 if (pair
.second
.rb
== obj
) {
1696 FramebufferRenderbuffer(target
, pair
.first
, LOCAL_GL_RENDERBUFFER
,
1701 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1702 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1704 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1705 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1708 obj
->mDeleteRequested
= true;
1709 Run
<RPROC(DeleteRenderbuffer
)>(obj
->mId
);
1712 void ClientWebGLContext::DeleteSampler(WebGLSamplerJS
* const obj
) {
1713 const FuncScope
funcScope(*this, "deleteSampler");
1714 if (IsContextLost()) return;
1715 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1716 const auto& state
= State();
1719 for (const auto i
: IntegerRange(state
.mTexUnits
.size())) {
1720 if (state
.mTexUnits
[i
].sampler
== obj
) {
1721 BindSampler(i
, nullptr);
1725 obj
->mDeleteRequested
= true;
1726 Run
<RPROC(DeleteSampler
)>(obj
->mId
);
1729 void ClientWebGLContext::DeleteShader(WebGLShaderJS
* const obj
) const {
1730 const FuncScope
funcScope(*this, "deleteShader");
1731 if (IsContextLost()) return;
1732 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1736 obj
->mKeepAlive
= nullptr;
1739 webgl::ShaderKeepAlive::~ShaderKeepAlive() {
1740 if (!mParent
) return;
1741 const auto& context
= mParent
->Context();
1742 if (!context
) return;
1743 context
->DoDeleteShader(*mParent
);
1746 void ClientWebGLContext::DoDeleteShader(const WebGLShaderJS
& obj
) const {
1747 Run
<RPROC(DeleteShader
)>(obj
.mId
);
1750 void ClientWebGLContext::DeleteSync(WebGLSyncJS
* const obj
) const {
1751 const FuncScope
funcScope(*this, "deleteSync");
1752 if (IsContextLost()) return;
1753 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1755 // Nothing to unbind
1757 obj
->mDeleteRequested
= true;
1758 Run
<RPROC(DeleteSync
)>(obj
->mId
);
1761 void ClientWebGLContext::DeleteTexture(WebGLTextureJS
* const obj
) {
1762 const FuncScope
funcScope(*this, "deleteTexture");
1763 if (IsContextLost()) return;
1764 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1765 auto& state
= State();
1768 const auto& target
= obj
->mTarget
;
1770 // Unbind from tex units
1771 Maybe
<uint32_t> restoreTexUnit
;
1772 for (const auto i
: IntegerRange(state
.mTexUnits
.size())) {
1773 if (state
.mTexUnits
[i
].texByTarget
[target
] == obj
) {
1774 if (!restoreTexUnit
) {
1775 restoreTexUnit
= Some(state
.mActiveTexUnit
);
1777 ActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
1778 BindTexture(target
, nullptr);
1781 if (restoreTexUnit
) {
1782 ActiveTexture(LOCAL_GL_TEXTURE0
+ *restoreTexUnit
);
1785 // Unbind from bound FBs
1786 const auto fnDetach
= [&](const GLenum target
,
1787 const WebGLFramebufferJS
* const fb
) {
1789 for (const auto& pair
: fb
->mAttachments
) {
1790 if (pair
.second
.tex
== obj
) {
1791 FramebufferRenderbuffer(target
, pair
.first
, LOCAL_GL_RENDERBUFFER
,
1796 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1797 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1799 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1800 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1804 obj
->mDeleteRequested
= true;
1805 Run
<RPROC(DeleteTexture
)>(obj
->mId
);
1808 void ClientWebGLContext::DeleteTransformFeedback(
1809 WebGLTransformFeedbackJS
* const obj
) {
1810 const FuncScope
funcScope(*this, "deleteTransformFeedback");
1811 if (IsContextLost()) return;
1812 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1813 const auto& state
= State();
1815 if (obj
->mActiveOrPaused
) {
1816 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
1817 "Transform Feedback object still active or paused.");
1822 if (state
.mBoundTfo
== obj
) {
1823 BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK
, nullptr);
1826 obj
->mDeleteRequested
= true;
1827 Run
<RPROC(DeleteTransformFeedback
)>(obj
->mId
);
1830 void ClientWebGLContext::DeleteVertexArray(WebGLVertexArrayJS
* const obj
) {
1831 const FuncScope
funcScope(*this, "deleteVertexArray");
1832 if (IsContextLost()) return;
1833 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1834 const auto& state
= State();
1837 if (state
.mBoundVao
== obj
) {
1838 BindVertexArray(nullptr);
1841 obj
->mDeleteRequested
= true;
1842 Run
<RPROC(DeleteVertexArray
)>(obj
->mId
);
1847 bool ClientWebGLContext::IsBuffer(const WebGLBufferJS
* const obj
) const {
1848 const FuncScope
funcScope(*this, "isBuffer");
1849 if (IsContextLost()) return false;
1851 return obj
&& obj
->IsUsable(*this) &&
1852 obj
->mKind
!= webgl::BufferKind::Undefined
;
1855 bool ClientWebGLContext::IsFramebuffer(
1856 const WebGLFramebufferJS
* const obj
) const {
1857 const FuncScope
funcScope(*this, "isFramebuffer");
1858 if (IsContextLost()) return false;
1860 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1863 bool ClientWebGLContext::IsProgram(const WebGLProgramJS
* const obj
) const {
1864 const FuncScope
funcScope(*this, "isProgram");
1865 if (IsContextLost()) return false;
1867 return obj
&& obj
->IsUsable(*this);
1870 bool ClientWebGLContext::IsQuery(const WebGLQueryJS
* const obj
) const {
1871 const FuncScope
funcScope(*this, "isQuery");
1872 if (IsContextLost()) return false;
1874 return obj
&& obj
->IsUsable(*this) && obj
->mTarget
;
1877 bool ClientWebGLContext::IsRenderbuffer(
1878 const WebGLRenderbufferJS
* const obj
) const {
1879 const FuncScope
funcScope(*this, "isRenderbuffer");
1880 if (IsContextLost()) return false;
1882 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1885 bool ClientWebGLContext::IsSampler(const WebGLSamplerJS
* const obj
) const {
1886 const FuncScope
funcScope(*this, "isSampler");
1887 if (IsContextLost()) return false;
1889 return obj
&& obj
->IsUsable(*this);
1892 bool ClientWebGLContext::IsShader(const WebGLShaderJS
* const obj
) const {
1893 const FuncScope
funcScope(*this, "isShader");
1894 if (IsContextLost()) return false;
1896 return obj
&& obj
->IsUsable(*this);
1899 bool ClientWebGLContext::IsSync(const WebGLSyncJS
* const obj
) const {
1900 const FuncScope
funcScope(*this, "isSync");
1901 if (IsContextLost()) return false;
1903 return obj
&& obj
->IsUsable(*this);
1906 bool ClientWebGLContext::IsTexture(const WebGLTextureJS
* const obj
) const {
1907 const FuncScope
funcScope(*this, "isTexture");
1908 if (IsContextLost()) return false;
1910 return obj
&& obj
->IsUsable(*this) && obj
->mTarget
;
1913 bool ClientWebGLContext::IsTransformFeedback(
1914 const WebGLTransformFeedbackJS
* const obj
) const {
1915 const FuncScope
funcScope(*this, "isTransformFeedback");
1916 if (IsContextLost()) return false;
1918 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1921 bool ClientWebGLContext::IsVertexArray(
1922 const WebGLVertexArrayJS
* const obj
) const {
1923 const FuncScope
funcScope(*this, "isVertexArray");
1924 if (IsContextLost()) return false;
1926 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1929 // ------------------------- GL State -------------------------
1931 void ClientWebGLContext::SetEnabledI(const GLenum cap
, const Maybe
<GLuint
> i
,
1932 const bool val
) const {
1933 const FuncScope
funcScope(*this, "enable/disable");
1934 if (IsContextLost()) return;
1936 auto& map
= mNotLost
->state
.mIsEnabledMap
;
1937 auto slot
= MaybeFind(map
, cap
);
1938 if (i
&& cap
!= LOCAL_GL_BLEND
) {
1942 EnqueueError_ArgEnum("cap", cap
);
1946 Run
<RPROC(SetEnabled
)>(cap
, i
, val
);
1948 if (!i
|| *i
== 0) {
1953 bool ClientWebGLContext::IsEnabled(const GLenum cap
) const {
1954 const FuncScope
funcScope(*this, "isEnabled");
1955 if (IsContextLost()) return false;
1957 const auto& map
= mNotLost
->state
.mIsEnabledMap
;
1958 const auto slot
= MaybeFind(map
, cap
);
1960 EnqueueError_ArgEnum("cap", cap
);
1967 template <typename T
, typename S
>
1968 static JS::Value
Create(JSContext
* cx
, nsWrapperCache
* creator
, const S
& src
,
1970 return JS::ObjectOrNullValue(T::Create(cx
, creator
, src
, rv
));
1973 void ClientWebGLContext::GetInternalformatParameter(
1974 JSContext
* cx
, GLenum target
, GLenum internalformat
, GLenum pname
,
1975 JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) {
1976 const FuncScope
funcScope(*this, "getInternalformatParameter");
1977 retval
.set(JS::NullValue());
1978 const auto notLost
=
1979 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1980 if (IsContextLost()) return;
1982 const auto& inProcessContext
= notLost
->inProcess
;
1983 Maybe
<std::vector
<int32_t>> maybe
;
1984 if (inProcessContext
) {
1985 maybe
= inProcessContext
->GetInternalformatParameter(target
, internalformat
,
1988 const auto& child
= notLost
->outOfProcess
;
1989 child
->FlushPendingCmds();
1990 if (!child
->SendGetInternalformatParameter(target
, internalformat
, pname
,
2000 retval
.set(Create
<dom::Int32Array
>(cx
, this, *maybe
, rv
));
2003 static JS::Value
StringValue(JSContext
* cx
, const std::string
& str
,
2005 JSString
* jsStr
= JS_NewStringCopyN(cx
, str
.data(), str
.size());
2007 er
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2008 return JS::NullValue();
2011 return JS::StringValue(jsStr
);
2014 template <typename T
>
2015 bool ToJSValueOrNull(JSContext
* const cx
, const RefPtr
<T
>& ptr
,
2016 JS::MutableHandle
<JS::Value
> retval
) {
2018 retval
.set(JS::NullValue());
2021 return dom::ToJSValue(cx
, ptr
, retval
);
2024 Maybe
<double> ClientWebGLContext::GetNumber(const GLenum pname
) {
2025 MOZ_ASSERT(!IsContextLost());
2027 const auto& inProcess
= mNotLost
->inProcess
;
2029 return inProcess
->GetNumber(pname
);
2032 const auto& child
= mNotLost
->outOfProcess
;
2033 child
->FlushPendingCmds();
2036 if (!child
->SendGetNumber(pname
, &ret
)) {
2042 Maybe
<std::string
> ClientWebGLContext::GetString(const GLenum pname
) {
2043 MOZ_ASSERT(!IsContextLost());
2045 const auto& inProcess
= mNotLost
->inProcess
;
2047 return inProcess
->GetString(pname
);
2050 const auto& child
= mNotLost
->outOfProcess
;
2051 child
->FlushPendingCmds();
2053 Maybe
<std::string
> ret
;
2054 if (!child
->SendGetString(pname
, &ret
)) {
2060 void ClientWebGLContext::GetParameter(JSContext
* cx
, GLenum pname
,
2061 JS::MutableHandle
<JS::Value
> retval
,
2062 ErrorResult
& rv
, const bool debug
) {
2063 retval
.set(JS::NullValue());
2064 const FuncScope
funcScope(*this, "getParameter");
2065 if (IsContextLost()) return;
2066 const auto& limits
= Limits();
2067 const auto& state
= State();
2071 const auto fnSetRetval_Buffer
= [&](const GLenum target
) {
2072 const auto buffer
= *MaybeFind(state
.mBoundBufferByTarget
, target
);
2073 (void)ToJSValueOrNull(cx
, buffer
, retval
);
2075 const auto fnSetRetval_Tex
= [&](const GLenum texTarget
) {
2076 const auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
2077 const auto tex
= Find(texUnit
.texByTarget
, texTarget
, nullptr);
2078 (void)ToJSValueOrNull(cx
, tex
, retval
);
2082 case LOCAL_GL_ARRAY_BUFFER_BINDING
:
2083 fnSetRetval_Buffer(LOCAL_GL_ARRAY_BUFFER
);
2086 case LOCAL_GL_CURRENT_PROGRAM
:
2087 (void)ToJSValueOrNull(cx
, state
.mCurrentProgram
, retval
);
2090 case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING
:
2091 (void)ToJSValueOrNull(cx
, state
.mBoundVao
->mIndexBuffer
, retval
);
2094 case LOCAL_GL_FRAMEBUFFER_BINDING
:
2095 (void)ToJSValueOrNull(cx
, state
.mBoundDrawFb
, retval
);
2098 case LOCAL_GL_RENDERBUFFER_BINDING
:
2099 (void)ToJSValueOrNull(cx
, state
.mBoundRb
, retval
);
2102 case LOCAL_GL_TEXTURE_BINDING_2D
:
2103 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D
);
2106 case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP
:
2107 fnSetRetval_Tex(LOCAL_GL_TEXTURE_CUBE_MAP
);
2110 case LOCAL_GL_VERTEX_ARRAY_BINDING
: {
2112 !IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object
))
2115 auto ret
= state
.mBoundVao
;
2116 if (ret
== state
.mDefaultVao
) {
2119 (void)ToJSValueOrNull(cx
, ret
, retval
);
2123 case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
:
2124 retval
.set(JS::NumberValue(limits
.maxTexUnits
));
2126 case LOCAL_GL_MAX_TEXTURE_SIZE
:
2127 retval
.set(JS::NumberValue(limits
.maxTex2dSize
));
2129 case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE
:
2130 retval
.set(JS::NumberValue(limits
.maxTexCubeSize
));
2132 case LOCAL_GL_MAX_VERTEX_ATTRIBS
:
2133 retval
.set(JS::NumberValue(limits
.maxVertexAttribs
));
2136 case LOCAL_GL_MAX_VIEWS_OVR
:
2137 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
2138 retval
.set(JS::NumberValue(limits
.maxMultiviewLayers
));
2143 case LOCAL_GL_PACK_ALIGNMENT
:
2144 retval
.set(JS::NumberValue(state
.mPixelPackState
.alignmentInTypeElems
));
2146 case LOCAL_GL_UNPACK_ALIGNMENT
:
2147 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.alignmentInTypeElems
));
2150 case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL
:
2151 retval
.set(JS::BooleanValue(state
.mPixelUnpackState
.flipY
));
2153 case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL
:
2154 retval
.set(JS::BooleanValue(state
.mPixelUnpackState
.premultiplyAlpha
));
2156 case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL
:
2157 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.colorspaceConversion
));
2160 case dom::WEBGL_provoking_vertex_Binding::PROVOKING_VERTEX_WEBGL
:
2161 if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_provoking_vertex
)) break;
2162 retval
.set(JS::NumberValue(UnderlyingValue(state
.mProvokingVertex
)));
2165 case LOCAL_GL_DEPTH_CLAMP
:
2166 if (!IsExtensionEnabled(WebGLExtensionID::EXT_depth_clamp
)) break;
2167 retval
.set(JS::BooleanValue(state
.mIsEnabledMap
[LOCAL_GL_DEPTH_CLAMP
]));
2174 case LOCAL_GL_DEPTH_RANGE
:
2175 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mDepthRange
, rv
));
2178 case LOCAL_GL_ALIASED_POINT_SIZE_RANGE
:
2180 Create
<dom::Float32Array
>(cx
, this, limits
.pointSizeRange
, rv
));
2183 case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE
:
2185 Create
<dom::Float32Array
>(cx
, this, limits
.lineWidthRange
, rv
));
2189 case LOCAL_GL_COLOR_CLEAR_VALUE
:
2190 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mClearColor
, rv
));
2193 case LOCAL_GL_BLEND_COLOR
:
2194 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mBlendColor
, rv
));
2198 case LOCAL_GL_MAX_VIEWPORT_DIMS
: {
2199 auto maxViewportDim
= BitwiseCast
<int32_t>(limits
.maxViewportDim
);
2200 const auto dims
= std::array
<int32_t, 2>{maxViewportDim
, maxViewportDim
};
2201 retval
.set(Create
<dom::Int32Array
>(cx
, this, dims
, rv
));
2206 case LOCAL_GL_SCISSOR_BOX
:
2207 retval
.set(Create
<dom::Int32Array
>(cx
, this, state
.mScissor
, rv
));
2210 case LOCAL_GL_VIEWPORT
:
2211 retval
.set(Create
<dom::Int32Array
>(cx
, this, state
.mViewport
, rv
));
2215 case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS
:
2216 retval
.set(Create
<dom::Uint32Array
>(cx
, this,
2217 state
.mCompressedTextureFormats
, rv
));
2223 case LOCAL_GL_COPY_READ_BUFFER_BINDING
:
2224 fnSetRetval_Buffer(LOCAL_GL_COPY_READ_BUFFER
);
2227 case LOCAL_GL_COPY_WRITE_BUFFER_BINDING
:
2228 fnSetRetval_Buffer(LOCAL_GL_COPY_WRITE_BUFFER
);
2231 case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
:
2232 (void)ToJSValueOrNull(cx
, state
.mBoundDrawFb
, retval
);
2235 case LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL
:
2236 retval
.set(JS::NumberValue(webgl::kMaxClientWaitSyncTimeoutNS
));
2239 case LOCAL_GL_PIXEL_PACK_BUFFER_BINDING
:
2240 fnSetRetval_Buffer(LOCAL_GL_PIXEL_PACK_BUFFER
);
2243 case LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING
:
2244 fnSetRetval_Buffer(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
2247 case LOCAL_GL_READ_FRAMEBUFFER_BINDING
:
2248 (void)ToJSValueOrNull(cx
, state
.mBoundReadFb
, retval
);
2251 case LOCAL_GL_SAMPLER_BINDING
: {
2252 const auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
2253 (void)ToJSValueOrNull(cx
, texUnit
.sampler
, retval
);
2257 case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY
:
2258 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D_ARRAY
);
2261 case LOCAL_GL_TEXTURE_BINDING_3D
:
2262 fnSetRetval_Tex(LOCAL_GL_TEXTURE_3D
);
2265 case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING
: {
2266 auto ret
= state
.mBoundTfo
;
2267 if (ret
== state
.mDefaultTfo
) {
2270 (void)ToJSValueOrNull(cx
, ret
, retval
);
2274 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING
:
2275 fnSetRetval_Buffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
);
2278 case LOCAL_GL_UNIFORM_BUFFER_BINDING
:
2279 fnSetRetval_Buffer(LOCAL_GL_UNIFORM_BUFFER
);
2282 case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
:
2284 JS::NumberValue(webgl::kMaxTransformFeedbackSeparateAttribs
));
2286 case LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS
:
2287 retval
.set(JS::NumberValue(limits
.maxUniformBufferBindings
));
2289 case LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
:
2290 retval
.set(JS::NumberValue(limits
.uniformBufferOffsetAlignment
));
2292 case LOCAL_GL_MAX_3D_TEXTURE_SIZE
:
2293 retval
.set(JS::NumberValue(limits
.maxTex3dSize
));
2295 case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS
:
2296 retval
.set(JS::NumberValue(limits
.maxTexArrayLayers
));
2299 case LOCAL_GL_PACK_ROW_LENGTH
:
2300 retval
.set(JS::NumberValue(state
.mPixelPackState
.rowLength
));
2302 case LOCAL_GL_PACK_SKIP_PIXELS
:
2303 retval
.set(JS::NumberValue(state
.mPixelPackState
.skipPixels
));
2305 case LOCAL_GL_PACK_SKIP_ROWS
:
2306 retval
.set(JS::NumberValue(state
.mPixelPackState
.skipRows
));
2309 case LOCAL_GL_UNPACK_IMAGE_HEIGHT
:
2310 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.imageHeight
));
2312 case LOCAL_GL_UNPACK_ROW_LENGTH
:
2313 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.rowLength
));
2315 case LOCAL_GL_UNPACK_SKIP_IMAGES
:
2316 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.skipImages
));
2318 case LOCAL_GL_UNPACK_SKIP_PIXELS
:
2319 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.skipPixels
));
2321 case LOCAL_GL_UNPACK_SKIP_ROWS
:
2322 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.skipRows
));
2330 const auto GetUnmaskedRenderer
= [&]() {
2331 const auto prefLock
= StaticPrefs::webgl_override_unmasked_renderer();
2332 if (!prefLock
->IsEmpty()) {
2333 return Some(ToString(*prefLock
));
2335 return GetString(LOCAL_GL_RENDERER
);
2338 const auto GetUnmaskedVendor
= [&]() {
2339 const auto prefLock
= StaticPrefs::webgl_override_unmasked_vendor();
2340 if (!prefLock
->IsEmpty()) {
2341 return Some(ToString(*prefLock
));
2343 return GetString(LOCAL_GL_VENDOR
);
2348 Maybe
<std::string
> ret
;
2351 case LOCAL_GL_VENDOR
:
2352 ret
= Some(std::string
{"Mozilla"});
2355 case LOCAL_GL_RENDERER
: {
2356 bool allowRenderer
= StaticPrefs::webgl_enable_renderer_query();
2357 if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo
)) {
2358 allowRenderer
= false;
2360 if (allowRenderer
) {
2361 ret
= GetUnmaskedRenderer();
2363 ret
= Some(webgl::SanitizeRenderer(*ret
));
2367 ret
= Some(std::string
{"Mozilla"});
2372 case LOCAL_GL_VERSION
:
2374 ret
= Some(std::string
{"WebGL 2.0"});
2376 ret
= Some(std::string
{"WebGL 1.0"});
2380 case LOCAL_GL_SHADING_LANGUAGE_VERSION
:
2382 ret
= Some(std::string
{"WebGL GLSL ES 3.00"});
2384 ret
= Some(std::string
{"WebGL GLSL ES 1.0"});
2388 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL
:
2389 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL
: {
2390 if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info
)) {
2391 EnqueueError_ArgEnum("pname", pname
);
2396 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL
:
2397 ret
= GetUnmaskedRenderer();
2398 if (ret
&& StaticPrefs::webgl_sanitize_unmasked_renderer()) {
2399 *ret
= webgl::SanitizeRenderer(*ret
);
2403 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL
:
2404 ret
= GetUnmaskedVendor();
2418 retval
.set(StringValue(cx
, *ret
, rv
));
2425 bool debugOnly
= false;
2426 bool asString
= false;
2429 case LOCAL_GL_EXTENSIONS
:
2430 case LOCAL_GL_RENDERER
:
2431 case LOCAL_GL_VENDOR
:
2432 case LOCAL_GL_VERSION
:
2433 case dom::MOZ_debug_Binding::CONTEXT_TYPE
:
2434 case dom::MOZ_debug_Binding::WSI_INFO
:
2439 case dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION
:
2447 if (debugOnly
&& !debug
) {
2448 EnqueueError_ArgEnum("pname", pname
);
2455 const auto maybe
= GetString(pname
);
2457 auto str
= std::string
{};
2458 if (pname
== dom::MOZ_debug_Binding::WSI_INFO
) {
2459 const auto& outOfProcess
= mNotLost
->outOfProcess
;
2460 const auto& inProcess
= mNotLost
->inProcess
;
2461 str
+= PrintfStdString("outOfProcess: %s\ninProcess: %s\n",
2462 ToChars(bool(outOfProcess
)),
2463 ToChars(bool(inProcess
)));
2466 retval
.set(StringValue(cx
, str
.c_str(), rv
));
2469 const auto maybe
= GetNumber(pname
);
2473 case LOCAL_GL_BLEND
:
2474 case LOCAL_GL_CULL_FACE
:
2475 case LOCAL_GL_DEPTH_TEST
:
2476 case LOCAL_GL_DEPTH_WRITEMASK
:
2477 case LOCAL_GL_DITHER
:
2478 case LOCAL_GL_POLYGON_OFFSET_FILL
:
2479 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
:
2480 case LOCAL_GL_SAMPLE_COVERAGE
:
2481 case LOCAL_GL_SAMPLE_COVERAGE_INVERT
:
2482 case LOCAL_GL_SCISSOR_TEST
:
2483 case LOCAL_GL_STENCIL_TEST
:
2485 case LOCAL_GL_RASTERIZER_DISCARD
:
2486 case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE
:
2487 case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED
:
2488 retval
.set(JS::BooleanValue(*maybe
));
2492 case LOCAL_GL_COLOR_WRITEMASK
: {
2493 const auto mask
= uint8_t(*maybe
);
2494 const auto bs
= std::bitset
<4>(mask
);
2495 const auto src
= std::array
<bool, 4>{bs
[0], bs
[1], bs
[2], bs
[3]};
2496 JS::Rooted
<JS::Value
> arr(cx
);
2497 if (!dom::ToJSValue(cx
, src
.data(), src
.size(), &arr
)) {
2498 rv
= NS_ERROR_OUT_OF_MEMORY
;
2505 retval
.set(JS::NumberValue(*maybe
));
2512 void ClientWebGLContext::GetBufferParameter(
2513 JSContext
* cx
, GLenum target
, GLenum pname
,
2514 JS::MutableHandle
<JS::Value
> retval
) const {
2515 retval
.set(JS::NullValue());
2516 if (IsContextLost()) return;
2518 const auto maybe
= [&]() {
2519 const auto& inProcess
= mNotLost
->inProcess
;
2521 return inProcess
->GetBufferParameter(target
, pname
);
2523 const auto& child
= mNotLost
->outOfProcess
;
2524 child
->FlushPendingCmds();
2526 if (!child
->SendGetBufferParameter(target
, pname
, &ret
)) {
2532 retval
.set(JS::NumberValue(*maybe
));
2536 bool IsFramebufferTarget(const bool isWebgl2
, const GLenum target
) {
2538 case LOCAL_GL_FRAMEBUFFER
:
2541 case LOCAL_GL_DRAW_FRAMEBUFFER
:
2542 case LOCAL_GL_READ_FRAMEBUFFER
:
2550 void ClientWebGLContext::GetFramebufferAttachmentParameter(
2551 JSContext
* const cx
, const GLenum target
, const GLenum attachment
,
2552 const GLenum pname
, JS::MutableHandle
<JS::Value
> retval
,
2553 ErrorResult
& rv
) const {
2554 retval
.set(JS::NullValue());
2555 const FuncScope
funcScope(*this, "getFramebufferAttachmentParameter");
2556 if (IsContextLost()) return;
2558 const auto& state
= State();
2560 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
2561 EnqueueError_ArgEnum("target", target
);
2564 auto fb
= state
.mBoundDrawFb
;
2565 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
2566 fb
= state
.mBoundReadFb
;
2569 const auto fnGet
= [&](const GLenum pname
) {
2570 const auto fbId
= fb
? fb
->mId
: 0;
2572 const auto& inProcess
= mNotLost
->inProcess
;
2574 return inProcess
->GetFramebufferAttachmentParameter(fbId
, attachment
,
2577 const auto& child
= mNotLost
->outOfProcess
;
2578 child
->FlushPendingCmds();
2580 if (!child
->SendGetFramebufferAttachmentParameter(fbId
, attachment
, pname
,
2589 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
2590 "An opaque framebuffer's attachments cannot be inspected or "
2594 auto attachmentSlotEnum
= attachment
;
2595 if (mIsWebGL2
&& attachment
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
2596 // In webgl2, DEPTH_STENCIL is valid iff the DEPTH and STENCIL images
2597 // match, so check if the server errors.
2598 const auto maybe
= fnGet(LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
);
2600 attachmentSlotEnum
= LOCAL_GL_DEPTH_ATTACHMENT
;
2603 const auto maybeSlot
= fb
->GetAttachment(attachmentSlotEnum
);
2605 EnqueueError_ArgEnum("attachment", attachment
);
2608 const auto& attached
= *maybeSlot
;
2612 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
) {
2614 (void)ToJSValueOrNull(cx
, attached
.rb
, retval
);
2616 if (!mIsWebGL2
&& !attached
.tex
) {
2617 EnqueueError_ArgEnum("pname", pname
);
2620 (void)ToJSValueOrNull(cx
, attached
.tex
, retval
);
2626 const auto maybe
= fnGet(pname
);
2628 retval
.set(JS::NumberValue(*maybe
));
2632 void ClientWebGLContext::GetRenderbufferParameter(
2633 JSContext
* cx
, GLenum target
, GLenum pname
,
2634 JS::MutableHandle
<JS::Value
> retval
) const {
2635 retval
.set(JS::NullValue());
2636 const FuncScope
funcScope(*this, "getRenderbufferParameter");
2637 if (IsContextLost()) return;
2639 if (target
!= LOCAL_GL_RENDERBUFFER
) {
2640 EnqueueError_ArgEnum("target", target
);
2644 const auto& state
= State();
2645 const auto& rb
= state
.mBoundRb
;
2646 const auto rbId
= rb
? rb
->mId
: 0;
2647 const auto maybe
= [&]() {
2648 const auto& inProcess
= mNotLost
->inProcess
;
2650 return inProcess
->GetRenderbufferParameter(rbId
, pname
);
2652 const auto& child
= mNotLost
->outOfProcess
;
2653 child
->FlushPendingCmds();
2655 if (!child
->SendGetRenderbufferParameter(rbId
, pname
, &ret
)) {
2661 retval
.set(JS::NumberValue(*maybe
));
2665 void ClientWebGLContext::GetIndexedParameter(
2666 JSContext
* cx
, GLenum target
, GLuint index
,
2667 JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) const {
2668 retval
.set(JS::NullValue());
2669 const FuncScope
funcScope(*this, "getIndexedParameter");
2670 if (IsContextLost()) return;
2672 const auto& state
= State();
2675 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING
: {
2676 const auto& list
= state
.mBoundTfo
->mAttribBuffers
;
2677 if (index
>= list
.size()) {
2678 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2679 "`index` (%u) >= MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS",
2683 (void)ToJSValueOrNull(cx
, list
[index
], retval
);
2687 case LOCAL_GL_UNIFORM_BUFFER_BINDING
: {
2688 const auto& list
= state
.mBoundUbos
;
2689 if (index
>= list
.size()) {
2690 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2691 "`index` (%u) >= MAX_UNIFORM_BUFFER_BINDINGS", index
);
2694 (void)ToJSValueOrNull(cx
, list
[index
], retval
);
2699 const auto maybe
= [&]() {
2700 const auto& inProcess
= mNotLost
->inProcess
;
2702 return inProcess
->GetIndexedParameter(target
, index
);
2704 const auto& child
= mNotLost
->outOfProcess
;
2705 child
->FlushPendingCmds();
2707 if (!child
->SendGetIndexedParameter(target
, index
, &ret
)) {
2714 case LOCAL_GL_COLOR_WRITEMASK
: {
2715 const auto bs
= std::bitset
<4>(*maybe
);
2716 const auto src
= std::array
<bool, 4>{bs
[0], bs
[1], bs
[2], bs
[3]};
2717 JS::Rooted
<JS::Value
> arr(cx
);
2718 if (!dom::ToJSValue(cx
, src
.data(), src
.size(), &arr
)) {
2719 rv
= NS_ERROR_OUT_OF_MEMORY
;
2726 retval
.set(JS::NumberValue(*maybe
));
2732 void ClientWebGLContext::GetUniform(JSContext
* const cx
,
2733 const WebGLProgramJS
& prog
,
2734 const WebGLUniformLocationJS
& loc
,
2735 JS::MutableHandle
<JS::Value
> retval
) {
2736 retval
.set(JS::NullValue());
2737 const FuncScope
funcScope(*this, "getUniform");
2738 if (IsContextLost()) return;
2739 if (!prog
.ValidateUsable(*this, "prog")) return;
2740 if (!loc
.ValidateUsable(*this, "loc")) return;
2742 const auto& progLinkResult
= GetLinkResult(prog
);
2743 if (!progLinkResult
.success
) {
2744 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "Program is not linked.");
2747 const auto& uniformLinkResult
= loc
.mParent
.lock();
2748 if (uniformLinkResult
.get() != &progLinkResult
) {
2750 LOCAL_GL_INVALID_OPERATION
,
2751 "UniformLocation is not from the most recent linking of Program.");
2755 const auto res
= [&]() {
2756 const auto& inProcess
= mNotLost
->inProcess
;
2758 return inProcess
->GetUniform(prog
.mId
, loc
.mLocation
);
2760 const auto& child
= mNotLost
->outOfProcess
;
2761 child
->FlushPendingCmds();
2762 webgl::GetUniformData ret
;
2763 if (!child
->SendGetUniform(prog
.mId
, loc
.mLocation
, &ret
)) {
2768 if (!res
.type
) return;
2770 const auto elemCount
= ElemTypeComponents(res
.type
);
2771 MOZ_ASSERT(elemCount
);
2775 retval
.set(JS::BooleanValue(res
.data
[0]));
2778 case LOCAL_GL_FLOAT
: {
2779 const auto ptr
= reinterpret_cast<const float*>(res
.data
);
2780 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2783 case LOCAL_GL_INT
: {
2784 const auto ptr
= reinterpret_cast<const int32_t*>(res
.data
);
2785 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2788 case LOCAL_GL_UNSIGNED_INT
:
2789 case LOCAL_GL_SAMPLER_2D
:
2790 case LOCAL_GL_SAMPLER_3D
:
2791 case LOCAL_GL_SAMPLER_CUBE
:
2792 case LOCAL_GL_SAMPLER_2D_SHADOW
:
2793 case LOCAL_GL_SAMPLER_2D_ARRAY
:
2794 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
2795 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
2796 case LOCAL_GL_INT_SAMPLER_2D
:
2797 case LOCAL_GL_INT_SAMPLER_3D
:
2798 case LOCAL_GL_INT_SAMPLER_CUBE
:
2799 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
2800 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
2801 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
2802 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
2803 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
: {
2804 const auto ptr
= reinterpret_cast<const uint32_t*>(res
.data
);
2805 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2811 case LOCAL_GL_BOOL_VEC2
:
2812 case LOCAL_GL_BOOL_VEC3
:
2813 case LOCAL_GL_BOOL_VEC4
: {
2814 const auto intArr
= reinterpret_cast<const int32_t*>(res
.data
);
2815 bool boolArr
[4] = {};
2816 for (const auto i
: IntegerRange(elemCount
)) {
2817 boolArr
[i
] = bool(intArr
[i
]);
2819 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, boolArr
, elemCount
, retval
));
2823 case LOCAL_GL_FLOAT_VEC2
:
2824 case LOCAL_GL_FLOAT_VEC3
:
2825 case LOCAL_GL_FLOAT_VEC4
:
2826 case LOCAL_GL_FLOAT_MAT2
:
2827 case LOCAL_GL_FLOAT_MAT3
:
2828 case LOCAL_GL_FLOAT_MAT4
:
2829 case LOCAL_GL_FLOAT_MAT2x3
:
2830 case LOCAL_GL_FLOAT_MAT2x4
:
2831 case LOCAL_GL_FLOAT_MAT3x2
:
2832 case LOCAL_GL_FLOAT_MAT3x4
:
2833 case LOCAL_GL_FLOAT_MAT4x2
:
2834 case LOCAL_GL_FLOAT_MAT4x3
: {
2835 const auto ptr
= reinterpret_cast<const float*>(res
.data
);
2836 IgnoredErrorResult error
;
2838 dom::Float32Array::Create(cx
, this, Span(ptr
, elemCount
), error
);
2840 retval
.set(JS::ObjectOrNullValue(obj
));
2844 case LOCAL_GL_INT_VEC2
:
2845 case LOCAL_GL_INT_VEC3
:
2846 case LOCAL_GL_INT_VEC4
: {
2847 const auto ptr
= reinterpret_cast<const int32_t*>(res
.data
);
2848 IgnoredErrorResult error
;
2850 dom::Int32Array::Create(cx
, this, Span(ptr
, elemCount
), error
);
2852 retval
.set(JS::ObjectOrNullValue(obj
));
2856 case LOCAL_GL_UNSIGNED_INT_VEC2
:
2857 case LOCAL_GL_UNSIGNED_INT_VEC3
:
2858 case LOCAL_GL_UNSIGNED_INT_VEC4
: {
2859 const auto ptr
= reinterpret_cast<const uint32_t*>(res
.data
);
2860 IgnoredErrorResult error
;
2862 dom::Uint32Array::Create(cx
, this, Span(ptr
, elemCount
), error
);
2864 retval
.set(JS::ObjectOrNullValue(obj
));
2869 MOZ_CRASH("GFX: Invalid elemType.");
2873 already_AddRefed
<WebGLShaderPrecisionFormatJS
>
2874 ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype
,
2875 const GLenum precisiontype
) {
2876 if (IsContextLost()) return nullptr;
2877 const auto info
= [&]() {
2878 const auto& inProcess
= mNotLost
->inProcess
;
2880 return inProcess
->GetShaderPrecisionFormat(shadertype
, precisiontype
);
2882 const auto& child
= mNotLost
->outOfProcess
;
2883 child
->FlushPendingCmds();
2884 Maybe
<webgl::ShaderPrecisionFormat
> ret
;
2885 if (!child
->SendGetShaderPrecisionFormat(shadertype
, precisiontype
, &ret
)) {
2891 if (!info
) return nullptr;
2892 return AsAddRefed(new WebGLShaderPrecisionFormatJS(*info
));
2895 void ClientWebGLContext::BlendColor(GLclampf r
, GLclampf g
, GLclampf b
,
2897 const FuncScope
funcScope(*this, "blendColor");
2898 if (IsContextLost()) return;
2899 auto& state
= State();
2901 const bool unclamped
=
2903 IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float
) ||
2904 IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float
));
2906 r
= std::clamp(r
, 0.0f
, 1.0f
);
2907 g
= std::clamp(g
, 0.0f
, 1.0f
);
2908 b
= std::clamp(b
, 0.0f
, 1.0f
);
2909 a
= std::clamp(a
, 0.0f
, 1.0f
);
2912 auto& cache
= state
.mBlendColor
;
2918 Run
<RPROC(BlendColor
)>(r
, g
, b
, a
);
2921 void ClientWebGLContext::BlendEquationSeparateI(Maybe
<GLuint
> i
, GLenum modeRGB
,
2923 Run
<RPROC(BlendEquationSeparate
)>(i
, modeRGB
, modeAlpha
);
2926 void ClientWebGLContext::BlendFuncSeparateI(Maybe
<GLuint
> i
, GLenum srcRGB
,
2927 GLenum dstRGB
, GLenum srcAlpha
,
2929 Run
<RPROC(BlendFuncSeparate
)>(i
, srcRGB
, dstRGB
, srcAlpha
, dstAlpha
);
2932 GLenum
ClientWebGLContext::CheckFramebufferStatus(GLenum target
) {
2933 if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
2935 const auto& inProcess
= mNotLost
->inProcess
;
2937 return inProcess
->CheckFramebufferStatus(target
);
2939 const auto& child
= mNotLost
->outOfProcess
;
2940 child
->FlushPendingCmds();
2942 if (!child
->SendCheckFramebufferStatus(target
, &ret
)) {
2948 void ClientWebGLContext::Clear(GLbitfield mask
) {
2949 Run
<RPROC(Clear
)>(mask
);
2956 void ClientWebGLContext::ClearBufferTv(const GLenum buffer
,
2957 const GLint drawBuffer
,
2958 const webgl::AttribBaseType type
,
2959 JS::AutoCheckCannotGC
&& nogc
,
2960 const Span
<const uint8_t>& view
,
2961 const GLuint srcElemOffset
) {
2962 if (IsContextLost()) return;
2964 const auto byteOffset
= CheckedInt
<size_t>(srcElemOffset
) * sizeof(float);
2965 if (!byteOffset
.isValid() || byteOffset
.value() > view
.Length()) {
2967 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`srcOffset` too large for `values`.");
2970 webgl::TypedQuad data
;
2973 auto dataSize
= data
.data
.size();
2975 case LOCAL_GL_COLOR
:
2978 case LOCAL_GL_DEPTH
:
2979 dataSize
= sizeof(float);
2982 case LOCAL_GL_STENCIL
:
2983 dataSize
= sizeof(int32_t);
2988 EnqueueError_ArgEnum("buffer", buffer
);
2992 const auto requiredBytes
= byteOffset
+ dataSize
;
2993 if (!requiredBytes
.isValid() || requiredBytes
.value() > view
.Length()) {
2995 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`values` too small.");
2999 memcpy(data
.data
.data(), view
.data() + byteOffset
.value(), dataSize
);
3000 nogc
.reset(); // Done with `view`.
3001 Run
<RPROC(ClearBufferTv
)>(buffer
, drawBuffer
, data
);
3006 void ClientWebGLContext::ClearBufferfi(GLenum buffer
, GLint drawBuffer
,
3007 GLfloat depth
, GLint stencil
) {
3008 Run
<RPROC(ClearBufferfi
)>(buffer
, drawBuffer
, depth
, stencil
);
3015 void ClientWebGLContext::ClearColor(GLclampf r
, GLclampf g
, GLclampf b
,
3017 const FuncScope
funcScope(*this, "clearColor");
3018 if (IsContextLost()) return;
3019 auto& state
= State();
3021 auto& cache
= state
.mClearColor
;
3027 Run
<RPROC(ClearColor
)>(r
, g
, b
, a
);
3030 void ClientWebGLContext::ClearDepth(GLclampf v
) { Run
<RPROC(ClearDepth
)>(v
); }
3032 void ClientWebGLContext::ClearStencil(GLint v
) { Run
<RPROC(ClearStencil
)>(v
); }
3034 void ClientWebGLContext::ColorMaskI(Maybe
<GLuint
> i
, bool r
, bool g
, bool b
,
3036 const FuncScope
funcScope(*this, "colorMask");
3037 if (IsContextLost()) return;
3039 const uint8_t mask
=
3040 uint8_t(r
<< 0) | uint8_t(g
<< 1) | uint8_t(b
<< 2) | uint8_t(a
<< 3);
3041 Run
<RPROC(ColorMask
)>(i
, mask
);
3044 void ClientWebGLContext::CullFace(GLenum face
) { Run
<RPROC(CullFace
)>(face
); }
3046 void ClientWebGLContext::DepthFunc(GLenum func
) { Run
<RPROC(DepthFunc
)>(func
); }
3048 void ClientWebGLContext::DepthMask(WebGLboolean b
) { Run
<RPROC(DepthMask
)>(b
); }
3050 void ClientWebGLContext::DepthRange(GLclampf zNear
, GLclampf zFar
) {
3051 const FuncScope
funcScope(*this, "depthRange");
3052 if (IsContextLost()) return;
3053 auto& state
= State();
3055 state
.mDepthRange
= {zNear
, zFar
};
3057 Run
<RPROC(DepthRange
)>(zNear
, zFar
);
3060 void ClientWebGLContext::Flush(const bool flushGl
) const {
3061 const FuncScope
funcScope(*this, "flush");
3062 if (IsContextLost()) return;
3065 Run
<RPROC(Flush
)>();
3068 if (mNotLost
->inProcess
) return;
3069 const auto& child
= mNotLost
->outOfProcess
;
3070 child
->FlushPendingCmds();
3073 void ClientWebGLContext::Finish() {
3074 if (IsContextLost()) return;
3076 const auto& inProcess
= mNotLost
->inProcess
;
3078 inProcess
->Finish();
3081 const auto& child
= mNotLost
->outOfProcess
;
3082 child
->FlushPendingCmds();
3083 (void)child
->SendFinish();
3086 void ClientWebGLContext::FrontFace(GLenum mode
) { Run
<RPROC(FrontFace
)>(mode
); }
3088 GLenum
ClientWebGLContext::GetError() {
3089 const FuncScope
funcScope(*this, "getError");
3091 const auto ret
= mNextError
;
3095 if (IsContextLost()) return 0;
3097 const auto& inProcess
= mNotLost
->inProcess
;
3099 return inProcess
->GetError();
3101 const auto& child
= mNotLost
->outOfProcess
;
3102 child
->FlushPendingCmds();
3104 if (!child
->SendGetError(&ret
)) {
3110 void ClientWebGLContext::Hint(GLenum target
, GLenum mode
) {
3111 Run
<RPROC(Hint
)>(target
, mode
);
3114 void ClientWebGLContext::LineWidth(GLfloat width
) {
3115 Run
<RPROC(LineWidth
)>(width
);
3118 Maybe
<webgl::ErrorInfo
> SetPixelUnpack(
3119 const bool isWebgl2
, webgl::PixelUnpackStateWebgl
* const unpacking
,
3120 const GLenum pname
, const GLint param
);
3122 void ClientWebGLContext::PixelStorei(const GLenum pname
, const GLint iparam
) {
3123 const FuncScope
funcScope(*this, "pixelStorei");
3124 if (IsContextLost()) return;
3125 if (!ValidateNonNegative("param", iparam
)) return;
3126 const auto param
= static_cast<uint32_t>(iparam
);
3128 auto& state
= State();
3129 auto& packState
= state
.mPixelPackState
;
3131 case LOCAL_GL_PACK_ALIGNMENT
:
3139 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3140 "PACK_ALIGNMENT must be one of [1,2,4,8], was %i.",
3144 packState
.alignmentInTypeElems
= param
;
3147 case LOCAL_GL_PACK_ROW_LENGTH
:
3148 if (!mIsWebGL2
) break;
3149 packState
.rowLength
= param
;
3152 case LOCAL_GL_PACK_SKIP_PIXELS
:
3153 if (!mIsWebGL2
) break;
3154 packState
.skipPixels
= param
;
3157 case LOCAL_GL_PACK_SKIP_ROWS
:
3158 if (!mIsWebGL2
) break;
3159 packState
.skipRows
= param
;
3162 case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH
:
3163 if (!IsSupported(WebGLExtensionID::MOZ_debug
)) {
3164 EnqueueError_ArgEnum("pname", pname
);
3174 SetPixelUnpack(mIsWebGL2
, &state
.mPixelUnpackState
, pname
, iparam
);
3181 void ClientWebGLContext::PolygonOffset(GLfloat factor
, GLfloat units
) {
3182 Run
<RPROC(PolygonOffset
)>(factor
, units
);
3185 void ClientWebGLContext::SampleCoverage(GLclampf value
, WebGLboolean invert
) {
3186 Run
<RPROC(SampleCoverage
)>(value
, invert
);
3189 void ClientWebGLContext::Scissor(GLint x
, GLint y
, GLsizei width
,
3191 const FuncScope
funcScope(*this, "scissor");
3192 if (IsContextLost()) return;
3193 auto& state
= State();
3195 if (!ValidateNonNegative("width", width
) ||
3196 !ValidateNonNegative("height", height
)) {
3200 state
.mScissor
= {x
, y
, width
, height
};
3202 Run
<RPROC(Scissor
)>(x
, y
, width
, height
);
3205 void ClientWebGLContext::StencilFuncSeparate(GLenum face
, GLenum func
,
3206 GLint ref
, GLuint mask
) {
3207 Run
<RPROC(StencilFuncSeparate
)>(face
, func
, ref
, mask
);
3210 void ClientWebGLContext::StencilMaskSeparate(GLenum face
, GLuint mask
) {
3211 Run
<RPROC(StencilMaskSeparate
)>(face
, mask
);
3214 void ClientWebGLContext::StencilOpSeparate(GLenum face
, GLenum sfail
,
3215 GLenum dpfail
, GLenum dppass
) {
3216 Run
<RPROC(StencilOpSeparate
)>(face
, sfail
, dpfail
, dppass
);
3219 void ClientWebGLContext::Viewport(GLint x
, GLint y
, GLsizei width
,
3221 const FuncScope
funcScope(*this, "viewport");
3222 if (IsContextLost()) return;
3223 auto& state
= State();
3225 if (!ValidateNonNegative("width", width
) ||
3226 !ValidateNonNegative("height", height
)) {
3230 state
.mViewport
= {x
, y
, width
, height
};
3232 Run
<RPROC(Viewport
)>(x
, y
, width
, height
);
3235 // ------------------------- Buffer Objects -------------------------
3237 Maybe
<const webgl::ErrorInfo
> ValidateBindBuffer(
3238 const GLenum target
, const webgl::BufferKind curKind
) {
3239 if (curKind
== webgl::BufferKind::Undefined
) return {};
3241 auto requiredKind
= webgl::BufferKind::NonIndex
;
3243 case LOCAL_GL_COPY_READ_BUFFER
:
3244 case LOCAL_GL_COPY_WRITE_BUFFER
:
3245 return {}; // Always ok
3247 case LOCAL_GL_ELEMENT_ARRAY_BUFFER
:
3248 requiredKind
= webgl::BufferKind::Index
;
3255 if (curKind
!= requiredKind
) {
3256 const auto fnKindStr
= [&](const webgl::BufferKind kind
) {
3257 if (kind
== webgl::BufferKind::Index
) return "ELEMENT_ARRAY_BUFFER";
3258 return "non-ELEMENT_ARRAY_BUFFER";
3260 const auto info
= nsPrintfCString(
3261 "Buffer previously bound to %s cannot be now bound to %s.",
3262 fnKindStr(curKind
), fnKindStr(requiredKind
));
3264 webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
, info
.BeginReading()});
3270 Maybe
<webgl::ErrorInfo
> CheckBindBufferRange(
3271 const GLenum target
, const GLuint index
, const bool isBuffer
,
3272 const uint64_t offset
, const uint64_t size
, const webgl::Limits
& limits
) {
3273 const auto fnSome
= [&](const GLenum type
, const nsACString
& info
) {
3274 return Some(webgl::ErrorInfo
{type
, info
.BeginReading()});
3278 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
:
3279 if (index
>= webgl::kMaxTransformFeedbackSeparateAttribs
) {
3280 const auto info
= nsPrintfCString(
3281 "`index` (%u) must be less than "
3282 "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS (%u).",
3283 index
, webgl::kMaxTransformFeedbackSeparateAttribs
);
3284 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3287 if (offset
% 4 != 0 || size
% 4 != 0) {
3289 nsPrintfCString("`offset` (%" PRIu64
") and `size` (%" PRIu64
3290 ") must both be aligned to 4 for"
3291 " TRANSFORM_FEEDBACK_BUFFER.",
3293 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3297 case LOCAL_GL_UNIFORM_BUFFER
:
3298 if (index
>= limits
.maxUniformBufferBindings
) {
3299 const auto info
= nsPrintfCString(
3300 "`index` (%u) must be less than MAX_UNIFORM_BUFFER_BINDINGS (%u).",
3301 index
, limits
.maxUniformBufferBindings
);
3302 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3305 if (offset
% limits
.uniformBufferOffsetAlignment
!= 0) {
3307 nsPrintfCString("`offset` (%" PRIu64
3308 ") must be aligned to "
3309 "UNIFORM_BUFFER_OFFSET_ALIGNMENT (%u).",
3310 offset
, limits
.uniformBufferOffsetAlignment
);
3311 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3317 nsPrintfCString("Unrecognized `target`: 0x%04x", target
);
3318 return fnSome(LOCAL_GL_INVALID_ENUM
, info
);
3327 void ClientWebGLContext::BindBuffer(const GLenum target
,
3328 WebGLBufferJS
* const buffer
) {
3329 const FuncScope
funcScope(*this, "bindBuffer");
3330 if (IsContextLost()) return;
3331 if (buffer
&& !buffer
->ValidateUsable(*this, "buffer")) return;
3334 // Check for INVALID_ENUM
3336 auto& state
= State();
3337 auto* slot
= &(state
.mBoundVao
->mIndexBuffer
);
3338 if (target
!= LOCAL_GL_ELEMENT_ARRAY_BUFFER
) {
3339 const auto itr
= state
.mBoundBufferByTarget
.find(target
);
3340 if (itr
== state
.mBoundBufferByTarget
.end()) {
3341 EnqueueError_ArgEnum("target", target
);
3344 slot
= &(itr
->second
);
3349 auto kind
= webgl::BufferKind::Undefined
;
3351 kind
= buffer
->mKind
;
3353 const auto err
= ValidateBindBuffer(target
, kind
);
3355 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3360 // Validation complete
3362 if (buffer
&& buffer
->mKind
== webgl::BufferKind::Undefined
) {
3363 if (target
== LOCAL_GL_ELEMENT_ARRAY_BUFFER
) {
3364 buffer
->mKind
= webgl::BufferKind::Index
;
3366 buffer
->mKind
= webgl::BufferKind::NonIndex
;
3373 Run
<RPROC(BindBuffer
)>(target
, buffer
? buffer
->mId
: 0);
3378 void ClientWebGLContext::BindBufferRangeImpl(const GLenum target
,
3380 WebGLBufferJS
* const buffer
,
3381 const uint64_t offset
,
3382 const uint64_t size
) {
3383 if (buffer
&& !buffer
->ValidateUsable(*this, "buffer")) return;
3384 auto& state
= State();
3388 const auto& limits
= Limits();
3390 CheckBindBufferRange(target
, index
, bool(buffer
), offset
, size
, limits
);
3392 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3398 auto kind
= webgl::BufferKind::Undefined
;
3400 kind
= buffer
->mKind
;
3402 err
= ValidateBindBuffer(target
, kind
);
3404 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3408 if (target
== LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
) {
3409 if (state
.mTfActiveAndNotPaused
) {
3410 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3411 "Cannot change TRANSFORM_FEEDBACK_BUFFER while "
3412 "TransformFeedback is active and not paused.");
3418 // Validation complete
3420 if (buffer
&& buffer
->mKind
== webgl::BufferKind::Undefined
) {
3421 buffer
->mKind
= webgl::BufferKind::NonIndex
;
3427 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
:
3428 state
.mBoundTfo
->mAttribBuffers
[index
] = buffer
;
3431 case LOCAL_GL_UNIFORM_BUFFER
:
3432 state
.mBoundUbos
[index
] = buffer
;
3436 MOZ_CRASH("Bad `target`");
3438 state
.mBoundBufferByTarget
[target
] = buffer
;
3442 Run
<RPROC(BindBufferRange
)>(target
, index
, buffer
? buffer
->mId
: 0, offset
,
3446 static inline size_t SizeOfViewElem(const dom::ArrayBufferView
& view
) {
3447 const auto& elemType
= view
.Type();
3448 if (elemType
== js::Scalar::MaxTypedArrayViewType
) // DataViews.
3451 return js::Scalar::byteSize(elemType
);
3454 void ClientWebGLContext::GetBufferSubData(GLenum target
, GLintptr srcByteOffset
,
3455 const dom::ArrayBufferView
& dstData
,
3456 GLuint dstElemOffset
,
3457 GLuint dstElemCountOverride
) {
3458 const FuncScope
funcScope(*this, "getBufferSubData");
3459 if (IsContextLost()) return;
3460 const auto notLost
=
3461 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
3462 if (!ValidateNonNegative("srcByteOffset", srcByteOffset
)) return;
3464 size_t elemSize
= SizeOfViewElem(dstData
);
3465 dstData
.ProcessFixedData([&](const Span
<uint8_t>& aData
) {
3466 const auto& destView
=
3467 ValidateArrayBufferView(aData
, elemSize
, dstElemOffset
,
3468 dstElemCountOverride
, LOCAL_GL_INVALID_VALUE
);
3473 const auto& inProcessContext
= notLost
->inProcess
;
3474 if (inProcessContext
) {
3475 inProcessContext
->GetBufferSubData(target
, srcByteOffset
, *destView
);
3479 const auto& child
= notLost
->outOfProcess
;
3480 child
->FlushPendingCmds();
3481 mozilla::ipc::Shmem rawShmem
;
3482 if (!child
->SendGetBufferSubData(target
, srcByteOffset
, destView
->size(),
3486 const webgl::RaiiShmem shmem
{child
, rawShmem
};
3488 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "Failed to map in sub data buffer.");
3492 const auto shmemView
= Span
{shmem
.ByteRange()};
3493 MOZ_RELEASE_ASSERT(shmemView
.size() == 1 + destView
->size());
3495 const auto ok
= bool(shmemView
[0]);
3496 const auto srcView
= shmemView
.subspan(1);
3498 Memcpy(&*destView
, srcView
);
3505 void ClientWebGLContext::BufferData(GLenum target
, WebGLsizeiptr rawSize
,
3507 const FuncScope
funcScope(*this, "bufferData");
3508 if (!ValidateNonNegative("size", rawSize
)) return;
3510 const auto size
= MaybeAs
<size_t>(rawSize
);
3512 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "`size` too large for platform.");
3515 Run
<RPROC(BufferData_SizeOnly
)>(target
, *size
, usage
);
3518 void ClientWebGLContext::BufferData(
3519 GLenum target
, const dom::Nullable
<dom::ArrayBuffer
>& maybeSrc
,
3521 const FuncScope
funcScope(*this, "bufferData");
3522 if (!ValidateNonNull("src", maybeSrc
)) return;
3523 const auto& src
= maybeSrc
.Value();
3525 src
.ProcessFixedData([&](const Span
<const uint8_t>& aData
) {
3526 Run
<RPROC(BufferData
)>(target
, aData
, usage
);
3530 void ClientWebGLContext::BufferData(GLenum target
,
3531 const dom::ArrayBufferView
& src
,
3532 GLenum usage
, GLuint srcElemOffset
,
3533 GLuint srcElemCountOverride
) {
3534 const FuncScope
funcScope(*this, "bufferData");
3535 size_t elemSize
= SizeOfViewElem(src
);
3536 src
.ProcessFixedData([&](const Span
<uint8_t>& aData
) {
3538 ValidateArrayBufferView(aData
, elemSize
, srcElemOffset
,
3539 srcElemCountOverride
, LOCAL_GL_INVALID_VALUE
);
3543 Run
<RPROC(BufferData
)>(target
, *range
, usage
);
3549 void ClientWebGLContext::BufferSubData(GLenum target
,
3550 WebGLsizeiptr dstByteOffset
,
3551 const dom::ArrayBuffer
& src
) {
3552 const FuncScope
funcScope(*this, "bufferSubData");
3553 src
.ProcessFixedData([&](const Span
<const uint8_t>& aData
) {
3554 Run
<RPROC(BufferSubData
)>(target
, dstByteOffset
, aData
,
3555 /* unsynchronized */ false);
3559 void ClientWebGLContext::BufferSubData(GLenum target
,
3560 WebGLsizeiptr dstByteOffset
,
3561 const dom::ArrayBufferView
& src
,
3562 GLuint srcElemOffset
,
3563 GLuint srcElemCountOverride
) {
3564 const FuncScope
funcScope(*this, "bufferSubData");
3565 size_t elemSize
= SizeOfViewElem(src
);
3566 src
.ProcessFixedData([&](const Span
<uint8_t>& aData
) {
3568 ValidateArrayBufferView(aData
, elemSize
, srcElemOffset
,
3569 srcElemCountOverride
, LOCAL_GL_INVALID_VALUE
);
3573 Run
<RPROC(BufferSubData
)>(target
, dstByteOffset
, *range
,
3574 /* unsynchronized */ false);
3578 void ClientWebGLContext::CopyBufferSubData(GLenum readTarget
,
3580 GLintptr readOffset
,
3581 GLintptr writeOffset
,
3583 const FuncScope
funcScope(*this, "copyBufferSubData");
3584 if (!ValidateNonNegative("readOffset", readOffset
) ||
3585 !ValidateNonNegative("writeOffset", writeOffset
) ||
3586 !ValidateNonNegative("size", size
)) {
3589 Run
<RPROC(CopyBufferSubData
)>(
3590 readTarget
, writeTarget
, static_cast<uint64_t>(readOffset
),
3591 static_cast<uint64_t>(writeOffset
), static_cast<uint64_t>(size
));
3594 // -------------------------- Framebuffer Objects --------------------------
3596 void ClientWebGLContext::BindFramebuffer(const GLenum target
,
3597 WebGLFramebufferJS
* const fb
) {
3598 const FuncScope
funcScope(*this, "bindFramebuffer");
3599 if (IsContextLost()) return;
3600 if (fb
&& !fb
->ValidateUsable(*this, "fb")) return;
3602 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
3603 EnqueueError_ArgEnum("target", target
);
3609 auto& state
= State();
3612 case LOCAL_GL_FRAMEBUFFER
:
3613 state
.mBoundDrawFb
= fb
;
3614 state
.mBoundReadFb
= fb
;
3617 case LOCAL_GL_DRAW_FRAMEBUFFER
:
3618 state
.mBoundDrawFb
= fb
;
3620 case LOCAL_GL_READ_FRAMEBUFFER
:
3621 state
.mBoundReadFb
= fb
;
3631 fb
->mHasBeenBound
= true;
3634 Run
<RPROC(BindFramebuffer
)>(target
, fb
? fb
->mId
: 0);
3639 void ClientWebGLContext::FramebufferTexture2D(GLenum target
, GLenum attachSlot
,
3640 GLenum bindImageTarget
,
3641 WebGLTextureJS
* const tex
,
3642 GLint mipLevel
) const {
3643 const FuncScope
funcScope(*this, "framebufferTexture2D");
3644 if (IsContextLost()) return;
3646 const auto bindTexTarget
= ImageToTexTarget(bindImageTarget
);
3647 uint32_t zLayer
= 0;
3648 switch (bindTexTarget
) {
3649 case LOCAL_GL_TEXTURE_2D
:
3651 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3652 zLayer
= bindImageTarget
- LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
;
3655 EnqueueError_ArgEnum("imageTarget", bindImageTarget
);
3660 !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap
)) {
3661 if (mipLevel
!= 0) {
3662 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3663 "mipLevel != 0 requires OES_fbo_render_mipmap.");
3668 FramebufferAttach(target
, attachSlot
, bindImageTarget
, nullptr, tex
,
3669 static_cast<uint32_t>(mipLevel
), zLayer
, 0);
3672 Maybe
<webgl::ErrorInfo
> CheckFramebufferAttach(const GLenum bindImageTarget
,
3673 const GLenum curTexTarget
,
3674 const uint32_t mipLevel
,
3675 const uint32_t zLayerBase
,
3676 const uint32_t zLayerCount
,
3677 const webgl::Limits
& limits
) {
3678 if (!curTexTarget
) {
3680 webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3681 "`tex` not yet bound. Call bindTexture first."});
3684 auto texTarget
= curTexTarget
;
3685 if (bindImageTarget
) {
3686 // FramebufferTexture2D
3687 const auto bindTexTarget
= ImageToTexTarget(bindImageTarget
);
3688 if (curTexTarget
!= bindTexTarget
) {
3689 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3690 "`tex` cannot be rebound to a new target."});
3693 switch (bindTexTarget
) {
3694 case LOCAL_GL_TEXTURE_2D
:
3695 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3698 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_ENUM
,
3699 "`tex` must have been bound to target "
3700 "TEXTURE_2D or TEXTURE_CUBE_MAP."});
3702 texTarget
= bindTexTarget
;
3704 // FramebufferTextureLayer/Multiview
3705 switch (curTexTarget
) {
3706 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3707 case LOCAL_GL_TEXTURE_3D
:
3710 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3711 "`tex` must have been bound to target "
3712 "TEXTURE_2D_ARRAY or TEXTURE_3D."});
3715 MOZ_ASSERT(texTarget
);
3718 switch (texTarget
) {
3719 case LOCAL_GL_TEXTURE_2D
:
3720 maxSize
= limits
.maxTex2dSize
;
3723 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3724 maxSize
= limits
.maxTexCubeSize
;
3727 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3728 maxSize
= limits
.maxTex2dSize
;
3729 maxZ
= limits
.maxTexArrayLayers
;
3731 case LOCAL_GL_TEXTURE_3D
:
3732 maxSize
= limits
.maxTex3dSize
;
3733 maxZ
= limits
.maxTex3dSize
;
3738 const auto maxMipLevel
= FloorLog2(maxSize
);
3739 if (mipLevel
> maxMipLevel
) {
3740 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
,
3741 "`mipLevel` too large for texture target."});
3743 const auto requiredZLayers
= CheckedInt
<uint32_t>(zLayerBase
) + zLayerCount
;
3744 if (!requiredZLayers
.isValid() || requiredZLayers
.value() > maxZ
) {
3745 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
,
3746 "`zLayer` too large for texture target."});
3752 void ClientWebGLContext::FramebufferAttach(
3753 const GLenum target
, const GLenum attachSlot
, const GLenum bindImageTarget
,
3754 WebGLRenderbufferJS
* const rb
, WebGLTextureJS
* const tex
,
3755 const uint32_t mipLevel
, const uint32_t zLayerBase
,
3756 const uint32_t numViewLayers
) const {
3757 if (rb
&& !rb
->ValidateUsable(*this, "rb")) return;
3758 if (tex
&& !tex
->ValidateUsable(*this, "tex")) return;
3759 const auto& state
= State();
3760 const auto& limits
= Limits();
3762 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
3763 EnqueueError_ArgEnum("target", target
);
3766 auto fb
= state
.mBoundDrawFb
;
3767 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
3768 fb
= state
.mBoundReadFb
;
3771 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No framebuffer bound.");
3777 LOCAL_GL_INVALID_OPERATION
,
3778 "An opaque framebuffer's attachments cannot be inspected or changed.");
3783 // Multiview-specific validation skipped by Host.
3785 if (tex
&& numViewLayers
) {
3786 if (tex
->mTarget
!= LOCAL_GL_TEXTURE_2D_ARRAY
) {
3787 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3788 "`tex` must have been bound to target TEXTURE_2D_ARRAY.");
3791 if (numViewLayers
> limits
.maxMultiviewLayers
) {
3792 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3793 "`numViews` (%u) must be <= MAX_VIEWS (%u).", numViewLayers
,
3794 limits
.maxMultiviewLayers
);
3801 webgl::ObjectId id
= 0;
3803 auto zLayerCount
= numViewLayers
;
3808 CheckFramebufferAttach(bindImageTarget
, tex
->mTarget
, mipLevel
,
3809 zLayerBase
, zLayerCount
, limits
);
3811 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3816 if (!rb
->mHasBeenBound
) {
3817 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3818 "`rb` has not yet been bound with BindRenderbuffer.");
3825 // But DEPTH_STENCIL in webgl2 is actually two slots!
3827 const auto fnAttachTo
= [&](const GLenum actualAttachSlot
) {
3828 const auto slot
= fb
->GetAttachment(actualAttachSlot
);
3830 EnqueueError_ArgEnum("attachment", actualAttachSlot
);
3837 Run
<RPROC(FramebufferAttach
)>(target
, actualAttachSlot
, bindImageTarget
, id
,
3838 mipLevel
, zLayerBase
, numViewLayers
);
3841 if (mIsWebGL2
&& attachSlot
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
3842 fnAttachTo(LOCAL_GL_DEPTH_ATTACHMENT
);
3843 fnAttachTo(LOCAL_GL_STENCIL_ATTACHMENT
);
3845 fnAttachTo(attachSlot
);
3848 if (bindImageTarget
) {
3850 rb
->mHasBeenBound
= true;
3853 tex
->mTarget
= ImageToTexTarget(bindImageTarget
);
3860 void ClientWebGLContext::BlitFramebuffer(GLint srcX0
, GLint srcY0
, GLint srcX1
,
3861 GLint srcY1
, GLint dstX0
, GLint dstY0
,
3862 GLint dstX1
, GLint dstY1
,
3863 GLbitfield mask
, GLenum filter
) {
3864 Run
<RPROC(BlitFramebuffer
)>(srcX0
, srcY0
, srcX1
, srcY1
, dstX0
, dstY0
, dstX1
,
3865 dstY1
, mask
, filter
);
3870 void ClientWebGLContext::InvalidateFramebuffer(
3871 GLenum target
, const dom::Sequence
<GLenum
>& attachments
,
3872 ErrorResult
& unused
) {
3873 Run
<RPROC(InvalidateFramebuffer
)>(target
, Span
{attachments
});
3875 // Never invalidate the backbuffer, so never needs AfterDrawCall.
3878 void ClientWebGLContext::InvalidateSubFramebuffer(
3879 GLenum target
, const dom::Sequence
<GLenum
>& attachments
, GLint x
, GLint y
,
3880 GLsizei width
, GLsizei height
, ErrorResult
& unused
) {
3881 Run
<RPROC(InvalidateSubFramebuffer
)>(target
, Span
{attachments
}, x
, y
, width
,
3884 // Never invalidate the backbuffer, so never needs AfterDrawCall.
3887 void ClientWebGLContext::ReadBuffer(GLenum mode
) {
3888 Run
<RPROC(ReadBuffer
)>(mode
);
3891 // ----------------------- Renderbuffer objects -----------------------
3893 void ClientWebGLContext::BindRenderbuffer(const GLenum target
,
3894 WebGLRenderbufferJS
* const rb
) {
3895 const FuncScope
funcScope(*this, "bindRenderbuffer");
3896 if (IsContextLost()) return;
3897 if (rb
&& !rb
->ValidateUsable(*this, "rb")) return;
3898 auto& state
= State();
3900 if (target
!= LOCAL_GL_RENDERBUFFER
) {
3901 EnqueueError_ArgEnum("target", target
);
3905 state
.mBoundRb
= rb
;
3907 rb
->mHasBeenBound
= true;
3911 void ClientWebGLContext::RenderbufferStorageMultisample(GLenum target
,
3913 GLenum internalFormat
,
3915 GLsizei height
) const {
3916 const FuncScope
funcScope(*this, "renderbufferStorageMultisample");
3917 if (IsContextLost()) return;
3919 if (target
!= LOCAL_GL_RENDERBUFFER
) {
3920 EnqueueError_ArgEnum("target", target
);
3924 const auto& state
= State();
3926 const auto& rb
= state
.mBoundRb
;
3928 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No renderbuffer bound");
3932 if (!ValidateNonNegative("width", width
) ||
3933 !ValidateNonNegative("height", height
) ||
3934 !ValidateNonNegative("samples", samples
)) {
3938 if (internalFormat
== LOCAL_GL_DEPTH_STENCIL
&& samples
> 0) {
3939 // While our backend supports it trivially, the spec forbids it.
3940 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3941 "WebGL 1's DEPTH_STENCIL format may not be multisampled. Use "
3942 "DEPTH24_STENCIL8 when `samples > 0`.");
3946 Run
<RPROC(RenderbufferStorageMultisample
)>(
3947 rb
->mId
, static_cast<uint32_t>(samples
), internalFormat
,
3948 static_cast<uint32_t>(width
), static_cast<uint32_t>(height
));
3951 // --------------------------- Texture objects ---------------------------
3953 void ClientWebGLContext::ActiveTexture(const GLenum texUnitEnum
) {
3954 const FuncScope
funcScope(*this, "activeTexture");
3955 if (IsContextLost()) return;
3957 if (texUnitEnum
< LOCAL_GL_TEXTURE0
) {
3958 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3959 "`texture` (0x%04x) must be >= TEXTURE0 (0x%04x).",
3960 texUnitEnum
, LOCAL_GL_TEXTURE0
);
3964 const auto texUnit
= texUnitEnum
- LOCAL_GL_TEXTURE0
;
3966 auto& state
= State();
3967 if (texUnit
>= state
.mTexUnits
.size()) {
3968 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3969 "TEXTURE%u must be < MAX_COMBINED_TEXTURE_IMAGE_UNITS (%zu).",
3970 texUnit
, state
.mTexUnits
.size());
3976 state
.mActiveTexUnit
= texUnit
;
3977 Run
<RPROC(ActiveTexture
)>(texUnit
);
3980 static bool IsTexTarget(const GLenum texTarget
, const bool webgl2
) {
3981 switch (texTarget
) {
3982 case LOCAL_GL_TEXTURE_2D
:
3983 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3986 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3987 case LOCAL_GL_TEXTURE_3D
:
3995 void ClientWebGLContext::BindTexture(const GLenum texTarget
,
3996 WebGLTextureJS
* const tex
) {
3997 const FuncScope
funcScope(*this, "bindTexture");
3998 if (IsContextLost()) return;
3999 if (tex
&& !tex
->ValidateUsable(*this, "tex")) return;
4001 if (!IsTexTarget(texTarget
, mIsWebGL2
)) {
4002 EnqueueError_ArgEnum("texTarget", texTarget
);
4006 if (tex
&& tex
->mTarget
) {
4007 if (texTarget
!= tex
->mTarget
) {
4008 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4009 "Texture previously bound to %s cannot be bound now to %s.",
4010 EnumString(tex
->mTarget
).c_str(),
4011 EnumString(texTarget
).c_str());
4016 auto& state
= State();
4017 auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
4018 texUnit
.texByTarget
[texTarget
] = tex
;
4020 tex
->mTarget
= texTarget
;
4023 Run
<RPROC(BindTexture
)>(texTarget
, tex
? tex
->mId
: 0);
4026 void ClientWebGLContext::GenerateMipmap(GLenum texTarget
) const {
4027 Run
<RPROC(GenerateMipmap
)>(texTarget
);
4030 void ClientWebGLContext::GetTexParameter(
4031 JSContext
* cx
, GLenum texTarget
, GLenum pname
,
4032 JS::MutableHandle
<JS::Value
> retval
) const {
4033 retval
.set(JS::NullValue());
4034 const FuncScope
funcScope(*this, "getTexParameter");
4035 if (IsContextLost()) return;
4036 auto& state
= State();
4038 auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
4040 const auto& tex
= Find(texUnit
.texByTarget
, texTarget
, nullptr);
4042 if (!IsTexTarget(texTarget
, mIsWebGL2
)) {
4043 EnqueueError_ArgEnum("texTarget", texTarget
);
4045 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No texture bound to %s[%u].",
4046 EnumString(texTarget
).c_str(), state
.mActiveTexUnit
);
4051 const auto maybe
= [&]() {
4052 const auto& inProcess
= mNotLost
->inProcess
;
4054 return inProcess
->GetTexParameter(tex
->mId
, pname
);
4056 const auto& child
= mNotLost
->outOfProcess
;
4057 child
->FlushPendingCmds();
4059 if (!child
->SendGetTexParameter(tex
->mId
, pname
, &ret
)) {
4067 case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT
:
4068 retval
.set(JS::BooleanValue(*maybe
));
4072 retval
.set(JS::NumberValue(*maybe
));
4078 void ClientWebGLContext::TexParameterf(GLenum texTarget
, GLenum pname
,
4080 Run
<RPROC(TexParameter_base
)>(texTarget
, pname
, FloatOrInt(param
));
4083 void ClientWebGLContext::TexParameteri(GLenum texTarget
, GLenum pname
,
4085 Run
<RPROC(TexParameter_base
)>(texTarget
, pname
, FloatOrInt(param
));
4088 ////////////////////////////////////
4090 static GLenum
JSTypeMatchUnpackTypeError(GLenum unpackType
,
4091 js::Scalar::Type jsType
) {
4092 bool matches
= false;
4093 switch (unpackType
) {
4095 matches
= (jsType
== js::Scalar::Type::Int8
);
4098 case LOCAL_GL_UNSIGNED_BYTE
:
4099 matches
= (jsType
== js::Scalar::Type::Uint8
||
4100 jsType
== js::Scalar::Type::Uint8Clamped
);
4103 case LOCAL_GL_SHORT
:
4104 matches
= (jsType
== js::Scalar::Type::Int16
);
4107 case LOCAL_GL_UNSIGNED_SHORT
:
4108 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
4109 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
4110 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
4111 case LOCAL_GL_HALF_FLOAT
:
4112 case LOCAL_GL_HALF_FLOAT_OES
:
4113 matches
= (jsType
== js::Scalar::Type::Uint16
);
4117 matches
= (jsType
== js::Scalar::Type::Int32
);
4120 case LOCAL_GL_UNSIGNED_INT
:
4121 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
:
4122 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
4123 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV
:
4124 case LOCAL_GL_UNSIGNED_INT_24_8
:
4125 matches
= (jsType
== js::Scalar::Type::Uint32
);
4128 case LOCAL_GL_FLOAT
:
4129 matches
= (jsType
== js::Scalar::Type::Float32
);
4132 case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV
:
4133 matches
= false; // No valid jsType, but we allow uploads with null.
4137 return LOCAL_GL_INVALID_ENUM
;
4139 if (!matches
) return LOCAL_GL_INVALID_OPERATION
;
4143 /////////////////////////////////////////////////
4145 static inline uvec2
CastUvec2(const ivec2
& val
) {
4146 return {static_cast<uint32_t>(val
.x
), static_cast<uint32_t>(val
.y
)};
4149 static inline uvec3
CastUvec3(const ivec3
& val
) {
4150 return {static_cast<uint32_t>(val
.x
), static_cast<uint32_t>(val
.y
),
4151 static_cast<uint32_t>(val
.z
)};
4154 template <typename T
>
4155 Range
<T
> SubRange(const Range
<T
>& full
, const size_t offset
,
4156 const size_t length
) {
4157 const auto newBegin
= full
.begin() + offset
;
4158 return Range
<T
>{newBegin
, newBegin
+ length
};
4161 Maybe
<Span
<const uint8_t>> GetRangeFromData(const Span
<uint8_t>& data
,
4162 size_t bytesPerElem
,
4164 GLuint elemCountOverride
) {
4165 auto elemCount
= data
.size() / bytesPerElem
;
4166 if (elemOffset
> elemCount
) return {};
4167 elemCount
-= elemOffset
;
4169 if (elemCountOverride
) {
4170 if (elemCountOverride
> elemCount
) return {};
4171 elemCount
= elemCountOverride
;
4174 data
.subspan(elemOffset
* bytesPerElem
, elemCount
* bytesPerElem
));
4179 static bool IsTexTargetForDims(const GLenum texTarget
, const bool webgl2
,
4180 const uint8_t funcDims
) {
4181 if (!IsTexTarget(texTarget
, webgl2
)) return false;
4182 switch (texTarget
) {
4183 case LOCAL_GL_TEXTURE_2D
:
4184 case LOCAL_GL_TEXTURE_CUBE_MAP
:
4185 return funcDims
== 2;
4188 return funcDims
== 3;
4192 void ClientWebGLContext::TexStorage(uint8_t funcDims
, GLenum texTarget
,
4193 GLsizei levels
, GLenum internalFormat
,
4194 const ivec3
& size
) const {
4195 const FuncScope
funcScope(*this, "texStorage[23]D");
4196 if (IsContextLost()) return;
4197 if (!IsTexTargetForDims(texTarget
, mIsWebGL2
, funcDims
)) {
4198 EnqueueError_ArgEnum("texTarget", texTarget
);
4201 Run
<RPROC(TexStorage
)>(texTarget
, static_cast<uint32_t>(levels
),
4202 internalFormat
, CastUvec3(size
));
4207 void webgl::TexUnpackBlobDesc::Shrink(const webgl::PackingInfo
& pi
) {
4209 if (!size
.x
|| !size
.y
|| !size
.z
) return;
4211 const auto unpackRes
= ExplicitUnpacking(pi
, {});
4212 if (!unpackRes
.isOk()) {
4215 const auto& unpack
= unpackRes
.inspect();
4217 const auto bytesUpperBound
=
4218 CheckedInt
<size_t>(unpack
.metrics
.bytesPerRowStride
) *
4219 unpack
.metrics
.totalRows
;
4220 if (bytesUpperBound
.isValid()) {
4221 auto& span
= *cpuData
;
4222 span
= span
.subspan(0, std::min(span
.size(), bytesUpperBound
.value()));
4229 void ClientWebGLContext::TexImage(uint8_t funcDims
, GLenum imageTarget
,
4230 GLint level
, GLenum respecFormat
,
4231 const ivec3
& offset
,
4232 const Maybe
<ivec3
>& isize
, GLint border
,
4233 const webgl::PackingInfo
& pi
,
4234 const TexImageSource
& src
) const {
4235 const FuncScope
funcScope(*this, "tex(Sub)Image[23]D");
4236 if (IsContextLost()) return;
4237 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4238 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4242 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4248 size
= Some(CastUvec3(isize
.value()));
4254 bool isDataUpload
= false;
4255 auto desc
= [&]() -> Maybe
<webgl::TexUnpackBlobDesc
> {
4256 if (src
.mPboOffset
) {
4257 isDataUpload
= true;
4258 const auto offset
= static_cast<uint64_t>(*src
.mPboOffset
);
4259 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
4261 gfxAlphaType::NonPremult
,
4267 isDataUpload
= true;
4268 const auto& view
= *src
.mView
;
4269 const auto& jsType
= view
.Type();
4270 const auto err
= JSTypeMatchUnpackTypeError(pi
.type
, jsType
);
4272 case LOCAL_GL_INVALID_ENUM
:
4273 EnqueueError_ArgEnum("unpackType", pi
.type
);
4275 case LOCAL_GL_INVALID_OPERATION
:
4276 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4277 "ArrayBufferView type %s not compatible with `type` %s.",
4278 name(jsType
), EnumString(pi
.type
).c_str());
4284 return view
.ProcessData(
4285 [&](const Span
<uint8_t>& aData
,
4286 JS::AutoCheckCannotGC
&& nogc
) -> Maybe
<webgl::TexUnpackBlobDesc
> {
4287 const auto range
= GetRangeFromData(aData
, SizeOfViewElem(view
),
4288 src
.mViewElemOffset
,
4289 src
.mViewElemLengthOverride
);
4292 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`source` too small.");
4295 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
4297 gfxAlphaType::NonPremult
,
4303 if (src
.mImageBitmap
) {
4304 return webgl::FromImageBitmap(imageTarget
, size
, *(src
.mImageBitmap
),
4308 if (src
.mImageData
) {
4309 const auto& imageData
= *src
.mImageData
;
4310 dom::Uint8ClampedArray scopedArr
;
4311 MOZ_RELEASE_ASSERT(scopedArr
.Init(imageData
.GetDataObject()));
4313 return scopedArr
.ProcessData(
4314 [&](const Span
<uint8_t>& aData
,
4315 JS::AutoCheckCannotGC
&& nogc
) -> Maybe
<webgl::TexUnpackBlobDesc
> {
4316 const auto dataSize
= aData
.Length();
4317 const auto data
= aData
.Elements();
4318 if (dataSize
== 0) {
4319 nogc
.reset(); // aData will not be used.
4321 LOCAL_GL_INVALID_VALUE
,
4322 "ImageData.data.buffer is Detached. (Maybe you Transfered "
4329 const gfx::IntSize
imageSize(imageData
.Width(), imageData
.Height());
4330 const auto sizeFromDims
=
4331 CheckedInt
<size_t>(imageSize
.width
) * imageSize
.height
* 4;
4332 MOZ_RELEASE_ASSERT(sizeFromDims
.isValid() &&
4333 sizeFromDims
.value() == dataSize
);
4335 const RefPtr
<gfx::DataSourceSurface
> surf
=
4336 gfx::Factory::CreateWrappingDataSourceSurface(
4337 data
, imageSize
.width
* 4, imageSize
,
4338 gfx::SurfaceFormat::R8G8B8A8
);
4343 const auto imageUSize
= *uvec2::FromSize(imageSize
);
4344 const auto concreteSize
=
4345 size
.valueOr(uvec3
{imageUSize
.x
, imageUSize
.y
, 1});
4347 // WhatWG "HTML Living Standard" (30 October 2015):
4348 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be
4349 // returned as non-premultiplied alpha values."
4351 Some(webgl::TexUnpackBlobDesc
{imageTarget
,
4353 gfxAlphaType::NonPremult
,
4360 nogc
.reset(); // Done with aData
4365 if (src
.mOffscreenCanvas
) {
4366 return webgl::FromOffscreenCanvas(
4367 *this, imageTarget
, size
, *(src
.mOffscreenCanvas
), src
.mOut_error
);
4370 if (src
.mVideoFrame
) {
4371 return webgl::FromVideoFrame(*this, imageTarget
, size
, *(src
.mVideoFrame
),
4376 return webgl::FromDomElem(*this, imageTarget
, size
, *(src
.mDomElem
),
4380 return Some(webgl::TexUnpackBlobDesc
{
4381 imageTarget
, size
.value(), gfxAlphaType::NonPremult
, {}, {}});
4389 const auto& rawUnpacking
= State().mPixelUnpackState
;
4391 auto defaultSubrectState
= webgl::PixelPackingState
{};
4392 defaultSubrectState
.alignmentInTypeElems
=
4393 rawUnpacking
.alignmentInTypeElems
;
4394 const bool isSubrect
= (rawUnpacking
!= defaultSubrectState
);
4395 if (isDataUpload
&& isSubrect
) {
4396 if (rawUnpacking
.flipY
|| rawUnpacking
.premultiplyAlpha
) {
4397 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4398 "Non-DOM-Element uploads with alpha-premult"
4399 " or y-flip do not support subrect selection.");
4404 desc
->unpacking
= rawUnpacking
;
4406 if (desc
->structuredSrcSize
) {
4408 // ### 5.35 Pixel store parameters for uploads from TexImageSource
4409 // UNPACK_ALIGNMENT and UNPACK_ROW_LENGTH are ignored.
4410 const auto& elemSize
= *desc
->structuredSrcSize
;
4411 desc
->unpacking
.alignmentInTypeElems
= 1;
4412 desc
->unpacking
.rowLength
= elemSize
.x
;
4414 if (!desc
->unpacking
.rowLength
) {
4415 desc
->unpacking
.rowLength
= desc
->size
.x
;
4417 if (!desc
->unpacking
.imageHeight
) {
4418 desc
->unpacking
.imageHeight
= desc
->size
.y
;
4425 mozilla::ipc::Shmem
* pShmem
= nullptr;
4426 // Image to release after WebGLContext::TexImage().
4427 RefPtr
<layers::Image
> keepAliveImage
;
4428 RefPtr
<gfx::DataSourceSurface
> keepAliveSurf
;
4431 const auto& sd
= *(desc
->sd
);
4432 const auto sdType
= sd
.type();
4433 const auto& contextInfo
= mNotLost
->info
;
4435 // TODO (Bug 754256): Figure out the source colorSpace.
4436 const auto& webgl
= this;
4437 dom::PredefinedColorSpace srcColorSpace
= dom::PredefinedColorSpace::Srgb
;
4438 dom::PredefinedColorSpace dstColorSpace
=
4439 webgl
->mUnpackColorSpace
? *webgl
->mUnpackColorSpace
4440 : dom::PredefinedColorSpace::Srgb
;
4441 bool sameColorSpace
= (srcColorSpace
== dstColorSpace
);
4443 const auto fallbackReason
= [&]() -> Maybe
<std::string
> {
4444 auto fallbackReason
= BlitPreventReason(
4445 level
, offset
, respecFormat
, pi
, *desc
,
4446 contextInfo
.optionalRenderableFormatBits
, sameColorSpace
);
4447 if (fallbackReason
) return fallbackReason
;
4449 const bool canUploadViaSd
= contextInfo
.uploadableSdTypes
[sdType
];
4450 if (!canUploadViaSd
) {
4451 const nsPrintfCString
msg(
4452 "Fast uploads for resource type %i not implemented.", int(sdType
));
4453 return Some(ToString(msg
));
4459 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
: {
4460 const auto& sdb
= sd
.get_SurfaceDescriptorBuffer();
4461 const auto& data
= sdb
.data();
4462 if (data
.type() == layers::MemoryOrShmem::TShmem
) {
4463 pShmem
= &data
.get_Shmem();
4466 std::string
{"SurfaceDescriptorBuffer data is not Shmem."});
4469 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10
: {
4470 const auto& sdD3D
= sd
.get_SurfaceDescriptorD3D10();
4471 const auto& inProcess
= mNotLost
->inProcess
;
4472 MOZ_ASSERT(desc
->image
);
4473 keepAliveImage
= desc
->image
;
4475 if (sdD3D
.gpuProcessTextureId().isSome() && inProcess
) {
4477 std::string
{"gpuProcessTextureId works only in GPU process."});
4480 case layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo
: {
4481 const auto& inProcess
= mNotLost
->inProcess
;
4482 MOZ_ASSERT(desc
->image
);
4483 keepAliveImage
= desc
->image
;
4485 return Some(std::string
{
4486 "SurfaceDescriptorGPUVideo works only in GPU process."});
4488 const auto& sdv
= sd
.get_SurfaceDescriptorGPUVideo();
4489 if (sdv
.type() != layers::SurfaceDescriptorGPUVideo::
4490 TSurfaceDescriptorRemoteDecoder
) {
4491 return Some(std::string
{
4492 "SurfaceDescriptorGPUVideo does not contain RemoteDecoder."});
4494 const auto& sdrd
= sdv
.get_SurfaceDescriptorRemoteDecoder();
4495 const auto& subdesc
= sdrd
.subdesc();
4496 if (subdesc
.type() !=
4497 layers::RemoteDecoderVideoSubDescriptor::Tnull_t
) {
4499 std::string
{"SurfaceDescriptorGPUVideo does not contain "
4500 "RemoteDecoder null subdesc."});
4503 case layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage
: {
4504 const auto& inProcess
= mNotLost
->inProcess
;
4505 MOZ_ASSERT(desc
->dataSurf
);
4506 keepAliveSurf
= desc
->dataSurf
;
4508 return Some(std::string
{
4509 "SurfaceDescriptorExternalImage works only in GPU process."});
4514 switch (respecFormat
) {
4516 case LOCAL_GL_SRGB8
:
4517 case LOCAL_GL_SRGB_ALPHA
:
4518 case LOCAL_GL_SRGB8_ALPHA8
: {
4519 const nsPrintfCString
msg(
4520 "srgb-encoded formats (like %s) are not supported.",
4521 EnumString(respecFormat
).c_str());
4522 return Some(ToString(msg
));
4526 if (StaticPrefs::webgl_disable_DOM_blit_uploads()) {
4527 return Some(std::string
{"DOM blit uploads are disabled."});
4532 if (fallbackReason
) {
4533 EnqueuePerfWarning("Missed GPU-copy fast-path: %s",
4534 fallbackReason
->c_str());
4536 const auto& image
= desc
->image
;
4538 const RefPtr
<gfx::SourceSurface
> surf
= image
->GetAsSourceSurface();
4540 // WARNING: OSX can lose our MakeCurrent here.
4541 desc
->dataSurf
= surf
->GetDataSurface();
4544 if (!desc
->dataSurf
) {
4545 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
,
4546 "Failed to retrieve source bytes for CPU upload.");
4549 desc
->sd
= Nothing();
4552 desc
->image
= nullptr;
4554 desc
->dataSurf
= nullptr;
4561 std::shared_ptr
<webgl::RaiiShmem
> tempShmem
;
4563 const bool doInlineUpload
= !desc
->sd
;
4564 // Why always de-inline SDs here?
4565 // 1. This way we always send SDs down the same handling path, which
4566 // should keep things from breaking if things flip between paths because of
4567 // what we get handed by SurfaceFromElement etc.
4568 // 2. We don't actually always grab strong-refs to the resources in the SDs,
4569 // so we should try to use them sooner rather than later. Yes we should fix
4570 // this, but for now let's give the SDs the best chance of lucking out, eh?
4572 // 3. It means we don't need to write QueueParamTraits<SurfaceDescriptor>.
4573 if (doInlineUpload
) {
4574 // We definitely want e.g. TexImage(PBO) here.
4575 Run
<RPROC(TexImage
)>(static_cast<uint32_t>(level
), respecFormat
,
4576 CastUvec3(offset
), pi
, std::move(*desc
));
4578 // We can't handle shmems like SurfaceDescriptorBuffer inline, so use ipdl.
4579 const auto& inProcess
= mNotLost
->inProcess
;
4581 return inProcess
->TexImage(static_cast<uint32_t>(level
), respecFormat
,
4582 CastUvec3(offset
), pi
, *desc
);
4584 const auto& child
= mNotLost
->outOfProcess
;
4585 child
->FlushPendingCmds();
4587 // The shmem we're handling was only shared from RDD to Content, and
4588 // immediately on Content receiving it, it was closed! RIP
4589 // Eventually we'll be able to make shmems that can traverse multiple
4590 // endpoints, but for now we need to make a new Content->WebGLParent shmem
4591 // and memcpy into it. We don't use `desc` elsewhere, so just replace the
4592 // Shmem buried within it with one that's valid for WebGLChild->Parent
4595 MOZ_ASSERT(desc
->sd
);
4596 const auto srcBytes
= ShmemRange
<uint8_t>(*pShmem
);
4597 tempShmem
= std::make_shared
<webgl::RaiiShmem
>();
4599 // We need Unsafe because we want to dictate when to destroy it from the
4601 *tempShmem
= webgl::RaiiShmem::AllocUnsafe(child
, srcBytes
.length());
4603 NS_WARNING("AllocShmem failed in TexImage");
4606 const auto dstBytes
= ShmemRange
<uint8_t>(tempShmem
->Shmem());
4607 Memcpy(&dstBytes
, srcBytes
.begin());
4609 *pShmem
= tempShmem
->Shmem();
4610 // Not Extract, because we free tempShmem manually below, after the remote
4611 // side has finished executing SendTexImage.
4614 (void)child
->SendTexImage(static_cast<uint32_t>(level
), respecFormat
,
4615 CastUvec3(offset
), pi
, std::move(*desc
));
4617 if (tempShmem
|| keepAliveImage
|| keepAliveSurf
) {
4618 const auto eventTarget
= GetCurrentSerialEventTarget();
4619 MOZ_ASSERT(eventTarget
);
4620 child
->SendPing()->Then(eventTarget
, __func__
,
4621 [tempShmem
, keepAliveImage
, keepAliveSurf
]() {
4622 // Cleans up when (our copy of)
4623 // sendableShmem/image goes out of scope.
4631 void ClientWebGLContext::CompressedTexImage(bool sub
, uint8_t funcDims
,
4632 GLenum imageTarget
, GLint level
,
4633 GLenum format
, const ivec3
& offset
,
4634 const ivec3
& isize
, GLint border
,
4635 const TexImageSource
& src
,
4636 GLsizei pboImageSize
) const {
4637 const FuncScope
funcScope(*this, "compressedTex(Sub)Image[23]D");
4638 if (IsContextLost()) return;
4639 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4640 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4644 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4649 src
.mView
->ProcessData([&](const Span
<uint8_t>& aData
,
4650 JS::AutoCheckCannotGC
&& aNoGC
) {
4652 GetRangeFromData(aData
, SizeOfViewElem(*src
.mView
),
4653 src
.mViewElemOffset
, src
.mViewElemLengthOverride
);
4656 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`source` too small.");
4660 // We don't need to shrink `range` because valid calls require
4661 // `range` to match requirements exactly.
4663 RunWithGCData
<RPROC(CompressedTexImage
)>(
4664 std::move(aNoGC
), sub
, imageTarget
, static_cast<uint32_t>(level
),
4665 format
, CastUvec3(offset
), CastUvec3(isize
), *range
,
4666 static_cast<uint32_t>(pboImageSize
), Maybe
<uint64_t>());
4671 if (!src
.mPboOffset
) {
4672 MOZ_CRASH("impossible");
4674 if (!ValidateNonNegative("offset", *src
.mPboOffset
)) {
4678 Run
<RPROC(CompressedTexImage
)>(
4679 sub
, imageTarget
, static_cast<uint32_t>(level
), format
, CastUvec3(offset
),
4680 CastUvec3(isize
), Span
<const uint8_t>{},
4681 static_cast<uint32_t>(pboImageSize
), Some(*src
.mPboOffset
));
4684 void ClientWebGLContext::CopyTexImage(uint8_t funcDims
, GLenum imageTarget
,
4685 GLint level
, GLenum respecFormat
,
4686 const ivec3
& dstOffset
,
4687 const ivec2
& srcOffset
, const ivec2
& size
,
4688 GLint border
) const {
4689 const FuncScope
funcScope(*this, "copy(Sub)Image[23]D");
4690 if (IsContextLost()) return;
4691 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4692 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4696 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4699 Run
<RPROC(CopyTexImage
)>(imageTarget
, static_cast<uint32_t>(level
),
4700 respecFormat
, CastUvec3(dstOffset
), srcOffset
,
4704 // ------------------- Programs and shaders --------------------------------
4706 void ClientWebGLContext::UseProgram(WebGLProgramJS
* const prog
) {
4707 const FuncScope
funcScope(*this, "useProgram");
4708 if (IsContextLost()) return;
4709 if (prog
&& !prog
->ValidateUsable(*this, "prog")) return;
4711 auto& state
= State();
4713 if (state
.mTfActiveAndNotPaused
) {
4714 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4715 "Transform feedback is active and not paused.");
4720 const auto& res
= GetLinkResult(*prog
);
4722 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4723 "Program must be linked successfully.");
4730 state
.mCurrentProgram
= prog
;
4731 state
.mProgramKeepAlive
= prog
? prog
->mKeepAliveWeak
.lock() : nullptr;
4732 state
.mActiveLinkResult
= prog
? prog
->mResult
: nullptr;
4734 Run
<RPROC(UseProgram
)>(prog
? prog
->mId
: 0);
4737 void ClientWebGLContext::ValidateProgram(WebGLProgramJS
& prog
) const {
4738 const FuncScope
funcScope(*this, "validateProgram");
4739 if (IsContextLost()) return;
4740 if (!prog
.ValidateUsable(*this, "prog")) return;
4742 prog
.mLastValidate
= [&]() {
4743 const auto& inProcess
= mNotLost
->inProcess
;
4745 return inProcess
->ValidateProgram(prog
.mId
);
4747 const auto& child
= mNotLost
->outOfProcess
;
4748 child
->FlushPendingCmds();
4750 if (!child
->SendValidateProgram(prog
.mId
, &ret
)) {
4757 // ------------------------ Uniforms and attributes ------------------------
4759 Maybe
<double> ClientWebGLContext::GetVertexAttribPriv(const GLuint index
,
4760 const GLenum pname
) {
4761 const auto& inProcess
= mNotLost
->inProcess
;
4763 return inProcess
->GetVertexAttrib(index
, pname
);
4765 const auto& child
= mNotLost
->outOfProcess
;
4766 child
->FlushPendingCmds();
4768 if (!child
->SendGetVertexAttrib(index
, pname
, &ret
)) {
4774 void ClientWebGLContext::GetVertexAttrib(JSContext
* cx
, GLuint index
,
4776 JS::MutableHandle
<JS::Value
> retval
,
4778 retval
.set(JS::NullValue());
4779 const FuncScope
funcScope(*this, "getVertexAttrib");
4780 if (IsContextLost()) return;
4781 const auto& state
= State();
4783 const auto& genericAttribs
= state
.mGenericVertexAttribs
;
4784 if (index
>= genericAttribs
.size()) {
4785 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` (%u) >= MAX_VERTEX_ATTRIBS",
4791 case LOCAL_GL_CURRENT_VERTEX_ATTRIB
: {
4792 const auto& attrib
= genericAttribs
[index
];
4793 switch (attrib
.type
) {
4794 case webgl::AttribBaseType::Float
: {
4795 const auto ptr
= reinterpret_cast<const float*>(attrib
.data
.data());
4796 retval
.setObjectOrNull(
4797 dom::Float32Array::Create(cx
, this, Span(ptr
, 4), rv
));
4800 case webgl::AttribBaseType::Int
: {
4801 const auto ptr
= reinterpret_cast<const int32_t*>(attrib
.data
.data());
4802 retval
.setObjectOrNull(
4803 dom::Int32Array::Create(cx
, this, Span(ptr
, 4), rv
));
4806 case webgl::AttribBaseType::Uint
: {
4808 reinterpret_cast<const uint32_t*>(attrib
.data
.data());
4809 retval
.setObjectOrNull(
4810 dom::Uint32Array::Create(cx
, this, Span(ptr
, 4), rv
));
4813 case webgl::AttribBaseType::Boolean
:
4814 MOZ_CRASH("impossible");
4820 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
: {
4821 const auto& buffers
= state
.mBoundVao
->mAttribBuffers
;
4822 const auto& buffer
= buffers
[index
];
4823 (void)ToJSValueOrNull(cx
, buffer
, retval
);
4827 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
:
4828 // Disallowed from JS, but allowed in Host.
4829 EnqueueError_ArgEnum("pname", pname
);
4836 const auto maybe
= GetVertexAttribPriv(index
, pname
);
4839 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED
:
4840 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
:
4841 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER
:
4842 retval
.set(JS::BooleanValue(*maybe
));
4846 retval
.set(JS::NumberValue(*maybe
));
4852 void ClientWebGLContext::UniformData(const GLenum funcElemType
,
4853 const WebGLUniformLocationJS
* const loc
,
4855 const Range
<const uint8_t>& bytes
,
4856 JS::AutoCheckCannotGC
&& nogc
,
4858 GLuint elemCountOverride
) const {
4859 // FuncScope::~FuncScope() can GC in a failure case, so all `return`
4860 // statements need to `nogc.reset()` up until the `nogc` is consumed by
4862 const FuncScope
funcScope(*this, "uniform setter");
4863 if (IsContextLost()) {
4868 const auto& activeLinkResult
= GetActiveLinkResult();
4869 if (!activeLinkResult
) {
4871 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No active linked Program.");
4877 auto availCount
= bytes
.length() / sizeof(float);
4878 if (elemOffset
> availCount
) {
4880 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`elemOffset` too large for `data`.");
4883 availCount
-= elemOffset
;
4884 if (elemCountOverride
) {
4885 if (elemCountOverride
> availCount
) {
4887 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4888 "`elemCountOverride` too large for `data`.");
4891 availCount
= elemCountOverride
;
4896 const auto channels
= ElemTypeComponents(funcElemType
);
4897 if (!availCount
|| availCount
% channels
!= 0) {
4899 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4900 "`values` length (%u) must be a positive "
4901 "integer multiple of size of %s.",
4902 availCount
, EnumString(funcElemType
).c_str());
4908 uint32_t locId
= -1;
4909 if (MOZ_LIKELY(loc
)) {
4910 locId
= loc
->mLocation
;
4911 if (!loc
->ValidateUsable(*this, "location")) {
4918 const auto& reqLinkInfo
= loc
->mParent
.lock();
4919 if (reqLinkInfo
.get() != activeLinkResult
) {
4921 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4922 "UniformLocation is not from the current active Program.");
4928 bool funcMatchesLocation
= false;
4929 for (const auto allowed
: loc
->mValidUploadElemTypes
) {
4930 funcMatchesLocation
|= (funcElemType
== allowed
);
4932 if (MOZ_UNLIKELY(!funcMatchesLocation
)) {
4933 std::string validSetters
;
4934 for (const auto allowed
: loc
->mValidUploadElemTypes
) {
4935 validSetters
+= EnumString(allowed
);
4936 validSetters
+= '/';
4938 validSetters
.pop_back(); // Cheekily discard the extra trailing '/'.
4941 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4942 "Uniform's `type` requires uniform setter of type %s.",
4943 validSetters
.c_str());
4951 reinterpret_cast<const webgl::UniformDataVal
*>(bytes
.begin().get()) +
4953 const auto range
= Span
{begin
, availCount
};
4954 RunWithGCData
<RPROC(UniformData
)>(std::move(nogc
), locId
, transpose
, range
);
4959 void ClientWebGLContext::BindVertexArray(WebGLVertexArrayJS
* const vao
) {
4960 const FuncScope
funcScope(*this, "bindVertexArray");
4961 if (IsContextLost()) return;
4962 if (vao
&& !vao
->ValidateUsable(*this, "vao")) return;
4963 auto& state
= State();
4966 vao
->mHasBeenBound
= true;
4967 state
.mBoundVao
= vao
;
4969 state
.mBoundVao
= state
.mDefaultVao
;
4972 Run
<RPROC(BindVertexArray
)>(vao
? vao
->mId
: 0);
4975 void ClientWebGLContext::EnableVertexAttribArray(GLuint index
) {
4976 Run
<RPROC(EnableVertexAttribArray
)>(index
);
4979 void ClientWebGLContext::DisableVertexAttribArray(GLuint index
) {
4980 Run
<RPROC(DisableVertexAttribArray
)>(index
);
4983 WebGLsizeiptr
ClientWebGLContext::GetVertexAttribOffset(GLuint index
,
4985 const FuncScope
funcScope(*this, "getVertexAttribOffset");
4986 if (IsContextLost()) return 0;
4988 if (pname
!= LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
) {
4989 EnqueueError_ArgEnum("pname", pname
);
4993 const auto maybe
= GetVertexAttribPriv(index
, pname
);
4994 if (!maybe
) return 0;
4998 void ClientWebGLContext::VertexAttrib4Tv(GLuint index
, webgl::AttribBaseType t
,
4999 const Range
<const uint8_t>& src
) {
5000 const FuncScope
funcScope(*this, "vertexAttrib[1234]u?[fi]{v}");
5001 if (IsContextLost()) return;
5002 auto& state
= State();
5004 if (src
.length() / sizeof(float) < 4) {
5005 EnqueueError(LOCAL_GL_INVALID_VALUE
, "Array must have >=4 elements.");
5009 auto& list
= state
.mGenericVertexAttribs
;
5010 if (index
>= list
.size()) {
5011 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5012 "`index` must be < MAX_VERTEX_ATTRIBS.");
5016 auto& attrib
= list
[index
];
5018 memcpy(attrib
.data
.data(), src
.begin().get(), attrib
.data
.size());
5020 Run
<RPROC(VertexAttrib4T
)>(index
, attrib
);
5025 void ClientWebGLContext::VertexAttribDivisor(GLuint index
, GLuint divisor
) {
5026 Run
<RPROC(VertexAttribDivisor
)>(index
, divisor
);
5031 void ClientWebGLContext::VertexAttribPointerImpl(bool isFuncInt
, GLuint index
,
5032 GLint rawChannels
, GLenum type
,
5034 GLsizei rawByteStrideOrZero
,
5035 WebGLintptr rawByteOffset
) {
5036 const FuncScope
funcScope(*this, "vertexAttribI?Pointer");
5037 if (IsContextLost()) return;
5038 auto& state
= State();
5040 const auto channels
= MaybeAs
<uint8_t>(rawChannels
);
5042 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5043 "Channel count `size` must be within [1,4].");
5047 const auto byteStrideOrZero
= MaybeAs
<uint8_t>(rawByteStrideOrZero
);
5048 if (!byteStrideOrZero
) {
5049 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`stride` must be within [0,255].");
5053 if (!ValidateNonNegative("byteOffset", rawByteOffset
)) return;
5054 const auto byteOffset
= static_cast<uint64_t>(rawByteOffset
);
5058 const webgl::VertAttribPointerDesc desc
{
5059 isFuncInt
, *channels
, normalized
, *byteStrideOrZero
, type
, byteOffset
};
5061 const auto res
= CheckVertexAttribPointer(mIsWebGL2
, desc
);
5063 const auto& err
= res
.inspectErr();
5064 EnqueueError(err
.type
, "%s", err
.info
.c_str());
5068 auto& list
= state
.mBoundVao
->mAttribBuffers
;
5069 if (index
>= list
.size()) {
5070 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5071 "`index` (%u) must be < MAX_VERTEX_ATTRIBS.", index
);
5075 const auto buffer
= state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
];
5076 if (!buffer
&& byteOffset
) {
5077 return EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5078 "If ARRAY_BUFFER is null, byteOffset must be zero.");
5081 Run
<RPROC(VertexAttribPointer
)>(index
, desc
);
5083 list
[index
] = buffer
;
5086 // -------------------------------- Drawing -------------------------------
5088 void ClientWebGLContext::DrawArraysInstanced(GLenum mode
, GLint first
,
5089 GLsizei count
, GLsizei primcount
) {
5090 Run
<RPROC(DrawArraysInstanced
)>(mode
, first
, count
, primcount
);
5094 void ClientWebGLContext::DrawElementsInstanced(GLenum mode
, GLsizei count
,
5095 GLenum type
, WebGLintptr offset
,
5096 GLsizei primcount
) {
5097 Run
<RPROC(DrawElementsInstanced
)>(mode
, count
, type
, offset
, primcount
);
5101 // ------------------------------ Readback -------------------------------
5102 void ClientWebGLContext::ReadPixels(GLint x
, GLint y
, GLsizei width
,
5103 GLsizei height
, GLenum format
, GLenum type
,
5104 WebGLsizeiptr offset
,
5105 dom::CallerType aCallerType
,
5106 ErrorResult
& out_error
) const {
5107 const FuncScope
funcScope(*this, "readPixels");
5108 if (!ReadPixels_SharedPrecheck(aCallerType
, out_error
)) return;
5109 const auto& state
= State();
5110 if (!ValidateNonNegative("width", width
)) return;
5111 if (!ValidateNonNegative("height", height
)) return;
5112 if (!ValidateNonNegative("offset", offset
)) return;
5114 const auto desc
= webgl::ReadPixelsDesc
{{x
, y
},
5115 *uvec2::From(width
, height
),
5117 state
.mPixelPackState
};
5118 Run
<RPROC(ReadPixelsPbo
)>(desc
, static_cast<uint64_t>(offset
));
5121 void ClientWebGLContext::ReadPixels(GLint x
, GLint y
, GLsizei width
,
5122 GLsizei height
, GLenum format
, GLenum type
,
5123 const dom::ArrayBufferView
& dstData
,
5124 GLuint dstElemOffset
,
5125 dom::CallerType aCallerType
,
5126 ErrorResult
& out_error
) const {
5127 const FuncScope
funcScope(*this, "readPixels");
5128 if (!ReadPixels_SharedPrecheck(aCallerType
, out_error
)) return;
5129 const auto& state
= State();
5130 if (!ValidateNonNegative("width", width
)) return;
5131 if (!ValidateNonNegative("height", height
)) return;
5135 js::Scalar::Type reqScalarType
;
5136 if (!GetJSScalarFromGLType(type
, &reqScalarType
)) {
5138 WebGLContext::EnumName(type
, &name
);
5139 EnqueueError(LOCAL_GL_INVALID_ENUM
, "type: invalid enum value %s",
5140 name
.BeginReading());
5144 auto viewElemType
= dstData
.Type();
5145 if (viewElemType
== js::Scalar::Uint8Clamped
) {
5146 viewElemType
= js::Scalar::Uint8
;
5148 if (viewElemType
!= reqScalarType
) {
5149 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5150 "`pixels` type does not match `type`.");
5154 size_t elemSize
= SizeOfViewElem(dstData
);
5155 dstData
.ProcessFixedData([&](const Span
<uint8_t>& aData
) {
5156 const auto& range
= ValidateArrayBufferView(aData
, elemSize
, dstElemOffset
,
5157 0, LOCAL_GL_INVALID_VALUE
);
5162 const auto desc
= webgl::ReadPixelsDesc
{{x
, y
},
5163 *uvec2::From(width
, height
),
5165 state
.mPixelPackState
};
5166 (void)DoReadPixels(desc
, *range
);
5170 bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc
& desc
,
5171 const Span
<uint8_t> dest
) const {
5172 const auto notLost
=
5173 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
5174 if (!notLost
) return false;
5175 const auto& inProcess
= notLost
->inProcess
;
5177 inProcess
->ReadPixelsInto(desc
, dest
);
5180 const auto& child
= notLost
->outOfProcess
;
5181 child
->FlushPendingCmds();
5182 webgl::ReadPixelsResultIpc res
= {};
5183 if (!child
->SendReadPixels(desc
, dest
.size(), &res
)) {
5186 if (!res
.byteStride
|| !res
.shmem
) return false;
5187 const auto& byteStride
= res
.byteStride
;
5188 const auto& subrect
= res
.subrect
;
5189 const webgl::RaiiShmem shmem
{child
, res
.shmem
.ref()};
5191 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "Failed to map in back buffer.");
5195 const auto& shmemBytes
= Span
{shmem
.ByteRange()};
5197 const auto pii
= webgl::PackingInfoInfo::For(desc
.pi
);
5199 gfxCriticalError() << "ReadPixels: Bad " << desc
.pi
;
5202 const auto bpp
= pii
->BytesPerPixel();
5204 const auto& packing
= desc
.packState
;
5205 auto packRect
= *uvec2::From(subrect
.x
, subrect
.y
);
5206 packRect
.x
+= packing
.skipPixels
;
5207 packRect
.y
+= packing
.skipRows
;
5209 const auto xByteSize
= bpp
* static_cast<uint32_t>(subrect
.width
);
5210 const ptrdiff_t byteOffset
= packRect
.y
* byteStride
+ packRect
.x
* bpp
;
5212 const auto srcSubrect
= shmemBytes
.subspan(byteOffset
);
5213 const auto destSubrect
= dest
.subspan(byteOffset
);
5215 for (const auto i
: IntegerRange(subrect
.height
)) {
5216 const auto srcRow
= srcSubrect
.subspan(i
* byteStride
, xByteSize
);
5217 const auto destRow
= destSubrect
.subspan(i
* byteStride
, xByteSize
);
5218 Memcpy(&destRow
, srcRow
);
5224 bool ClientWebGLContext::ReadPixels_SharedPrecheck(
5225 dom::CallerType aCallerType
, ErrorResult
& out_error
) const {
5226 if (IsContextLost()) return false;
5228 if (mCanvasElement
&& mCanvasElement
->IsWriteOnly() &&
5229 aCallerType
!= dom::CallerType::System
) {
5230 JsWarning("readPixels: Not allowed");
5231 out_error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
5238 // --------------------------------- GL Query ---------------------------------
5240 static inline GLenum
QuerySlotTarget(const GLenum specificTarget
) {
5241 if (specificTarget
== LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE
) {
5242 return LOCAL_GL_ANY_SAMPLES_PASSED
;
5244 return specificTarget
;
5247 void ClientWebGLContext::GetQuery(JSContext
* cx
, GLenum specificTarget
,
5249 JS::MutableHandle
<JS::Value
> retval
) const {
5250 retval
.set(JS::NullValue());
5251 const FuncScope
funcScope(*this, "getQuery");
5252 if (IsContextLost()) return;
5253 const auto& limits
= Limits();
5254 auto& state
= State();
5256 if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query
)) {
5257 if (pname
== LOCAL_GL_QUERY_COUNTER_BITS
) {
5258 switch (specificTarget
) {
5259 case LOCAL_GL_TIME_ELAPSED_EXT
:
5260 retval
.set(JS::NumberValue(limits
.queryCounterBitsTimeElapsed
));
5263 case LOCAL_GL_TIMESTAMP_EXT
:
5264 retval
.set(JS::NumberValue(limits
.queryCounterBitsTimestamp
));
5268 EnqueueError_ArgEnum("target", specificTarget
);
5274 if (pname
!= LOCAL_GL_CURRENT_QUERY
) {
5275 EnqueueError_ArgEnum("pname", pname
);
5279 const auto slotTarget
= QuerySlotTarget(specificTarget
);
5280 const auto& slot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
5282 EnqueueError_ArgEnum("target", specificTarget
);
5287 if (query
&& query
->mTarget
!= specificTarget
) {
5291 (void)ToJSValueOrNull(cx
, query
, retval
);
5294 void ClientWebGLContext::GetQueryParameter(
5295 JSContext
*, WebGLQueryJS
& query
, const GLenum pname
,
5296 JS::MutableHandle
<JS::Value
> retval
) const {
5297 retval
.set(JS::NullValue());
5298 const FuncScope
funcScope(*this, "getQueryParameter");
5299 if (IsContextLost()) return;
5300 if (!query
.ValidateUsable(*this, "query")) return;
5302 auto maybe
= [&]() {
5303 const auto& inProcess
= mNotLost
->inProcess
;
5305 return inProcess
->GetQueryParameter(query
.mId
, pname
);
5307 const auto& child
= mNotLost
->outOfProcess
;
5308 child
->FlushPendingCmds();
5310 if (!child
->SendGetQueryParameter(query
.mId
, pname
, &ret
)) {
5317 // We must usually wait for an event loop before the query can be available.
5318 const bool canBeAvailable
=
5319 (query
.mCanBeAvailable
|| StaticPrefs::webgl_allow_immediate_queries());
5320 if (!canBeAvailable
) {
5321 if (pname
!= LOCAL_GL_QUERY_RESULT_AVAILABLE
) {
5328 case LOCAL_GL_QUERY_RESULT_AVAILABLE
:
5329 retval
.set(JS::BooleanValue(*maybe
));
5333 retval
.set(JS::NumberValue(*maybe
));
5338 void ClientWebGLContext::BeginQuery(const GLenum specificTarget
,
5339 WebGLQueryJS
& query
) {
5340 const FuncScope
funcScope(*this, "beginQuery");
5341 if (IsContextLost()) return;
5342 if (!query
.ValidateUsable(*this, "query")) return;
5343 auto& state
= State();
5345 const auto slotTarget
= QuerySlotTarget(specificTarget
);
5346 const auto& slot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
5348 EnqueueError_ArgEnum("target", specificTarget
);
5353 auto enumStr
= EnumString(slotTarget
);
5354 if (slotTarget
== LOCAL_GL_ANY_SAMPLES_PASSED
) {
5355 enumStr
+= "/ANY_SAMPLES_PASSED_CONSERVATIVE";
5357 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5358 "A Query is already active for %s.", enumStr
.c_str());
5362 if (query
.mTarget
&& query
.mTarget
!= specificTarget
) {
5363 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5364 "`query` cannot be changed to a different target.");
5369 query
.mTarget
= specificTarget
;
5371 Run
<RPROC(BeginQuery
)>(specificTarget
, query
.mId
);
5374 void ClientWebGLContext::EndQuery(const GLenum specificTarget
) {
5375 const FuncScope
funcScope(*this, "endQuery");
5376 if (IsContextLost()) return;
5377 auto& state
= State();
5379 const auto slotTarget
= QuerySlotTarget(specificTarget
);
5380 const auto& maybeSlot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
5382 EnqueueError_ArgEnum("target", specificTarget
);
5385 auto& slot
= *maybeSlot
;
5386 if (!slot
|| slot
->mTarget
!= specificTarget
) {
5387 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No Query is active for %s.",
5388 EnumString(specificTarget
).c_str());
5391 const auto query
= slot
;
5394 Run
<RPROC(EndQuery
)>(specificTarget
);
5396 auto& availRunnable
= EnsureAvailabilityRunnable();
5397 availRunnable
.mQueries
.push_back(query
.get());
5398 query
->mCanBeAvailable
= false;
5401 void ClientWebGLContext::QueryCounter(WebGLQueryJS
& query
,
5402 const GLenum target
) const {
5403 const FuncScope
funcScope(*this, "queryCounter");
5404 if (IsContextLost()) return;
5405 if (!query
.ValidateUsable(*this, "query")) return;
5407 if (target
!= LOCAL_GL_TIMESTAMP
) {
5408 EnqueueError(LOCAL_GL_INVALID_ENUM
, "`target` must be TIMESTAMP.");
5412 if (query
.mTarget
&& query
.mTarget
!= target
) {
5413 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5414 "`query` cannot be changed to a different target.");
5417 query
.mTarget
= target
;
5419 Run
<RPROC(QueryCounter
)>(query
.mId
);
5421 auto& availRunnable
= EnsureAvailabilityRunnable();
5422 availRunnable
.mQueries
.push_back(&query
);
5423 query
.mCanBeAvailable
= false;
5426 // -------------------------------- Sampler -------------------------------
5427 void ClientWebGLContext::GetSamplerParameter(
5428 JSContext
* cx
, const WebGLSamplerJS
& sampler
, const GLenum pname
,
5429 JS::MutableHandle
<JS::Value
> retval
) const {
5430 retval
.set(JS::NullValue());
5431 const FuncScope
funcScope(*this, "getSamplerParameter");
5432 if (IsContextLost()) return;
5433 if (!sampler
.ValidateUsable(*this, "sampler")) return;
5435 const auto maybe
= [&]() {
5436 const auto& inProcess
= mNotLost
->inProcess
;
5438 return inProcess
->GetSamplerParameter(sampler
.mId
, pname
);
5440 const auto& child
= mNotLost
->outOfProcess
;
5441 child
->FlushPendingCmds();
5443 if (!child
->SendGetSamplerParameter(sampler
.mId
, pname
, &ret
)) {
5449 retval
.set(JS::NumberValue(*maybe
));
5453 void ClientWebGLContext::BindSampler(const GLuint unit
,
5454 WebGLSamplerJS
* const sampler
) {
5455 const FuncScope
funcScope(*this, "bindSampler");
5456 if (IsContextLost()) return;
5457 if (sampler
&& !sampler
->ValidateUsable(*this, "sampler")) return;
5458 auto& state
= State();
5460 auto& texUnits
= state
.mTexUnits
;
5461 if (unit
>= texUnits
.size()) {
5462 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`unit` (%u) larger than %zu.", unit
,
5469 texUnits
[unit
].sampler
= sampler
;
5471 Run
<RPROC(BindSampler
)>(unit
, sampler
? sampler
->mId
: 0);
5474 void ClientWebGLContext::SamplerParameteri(WebGLSamplerJS
& sampler
,
5476 const GLint param
) const {
5477 const FuncScope
funcScope(*this, "samplerParameteri");
5478 if (IsContextLost()) return;
5479 if (!sampler
.ValidateUsable(*this, "sampler")) return;
5481 Run
<RPROC(SamplerParameteri
)>(sampler
.mId
, pname
, param
);
5484 void ClientWebGLContext::SamplerParameterf(WebGLSamplerJS
& sampler
,
5486 const GLfloat param
) const {
5487 const FuncScope
funcScope(*this, "samplerParameterf");
5488 if (IsContextLost()) return;
5489 if (!sampler
.ValidateUsable(*this, "sampler")) return;
5491 Run
<RPROC(SamplerParameterf
)>(sampler
.mId
, pname
, param
);
5494 // ------------------------------- GL Sync ---------------------------------
5496 void ClientWebGLContext::GetSyncParameter(
5497 JSContext
* const cx
, WebGLSyncJS
& sync
, const GLenum pname
,
5498 JS::MutableHandle
<JS::Value
> retval
) const {
5499 retval
.set(JS::NullValue());
5500 const FuncScope
funcScope(*this, "getSyncParameter");
5501 if (IsContextLost()) return;
5502 if (!sync
.ValidateUsable(*this, "sync")) return;
5504 retval
.set([&]() -> JS::Value
{
5506 case LOCAL_GL_OBJECT_TYPE
:
5507 return JS::NumberValue(LOCAL_GL_SYNC_FENCE
);
5508 case LOCAL_GL_SYNC_CONDITION
:
5509 return JS::NumberValue(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE
);
5510 case LOCAL_GL_SYNC_FLAGS
:
5511 return JS::NumberValue(0);
5512 case LOCAL_GL_SYNC_STATUS
: {
5513 const auto res
= ClientWaitSync(sync
, 0, 0);
5514 const auto signaled
= (res
== LOCAL_GL_ALREADY_SIGNALED
||
5515 res
== LOCAL_GL_CONDITION_SATISFIED
);
5516 return JS::NumberValue(signaled
? LOCAL_GL_SIGNALED
5517 : LOCAL_GL_UNSIGNALED
);
5520 EnqueueError_ArgEnum("pname", pname
);
5521 return JS::NullValue();
5528 GLenum
ClientWebGLContext::ClientWaitSync(WebGLSyncJS
& sync
,
5529 const GLbitfield flags
,
5530 const GLuint64 timeout
) const {
5531 const FuncScope
funcScope(*this, "clientWaitSync");
5532 if (IsContextLost()) return LOCAL_GL_WAIT_FAILED
;
5533 if (!sync
.ValidateUsable(*this, "sync")) return LOCAL_GL_WAIT_FAILED
;
5535 static constexpr auto VALID_BITS
= LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT
;
5536 if ((flags
| VALID_BITS
) != VALID_BITS
) {
5537 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5538 "`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.");
5539 return LOCAL_GL_WAIT_FAILED
;
5542 if (timeout
> webgl::kMaxClientWaitSyncTimeoutNS
) {
5544 LOCAL_GL_INVALID_OPERATION
,
5545 "`timeout` (%sns) must be less than MAX_CLIENT_WAIT_TIMEOUT_WEBGL "
5547 ToStringWithCommas(timeout
).c_str(),
5548 ToStringWithCommas(webgl::kMaxClientWaitSyncTimeoutNS
).c_str());
5549 return LOCAL_GL_WAIT_FAILED
;
5552 const bool canBeAvailable
=
5553 (sync
.mCanBeAvailable
|| StaticPrefs::webgl_allow_immediate_queries());
5554 if (!canBeAvailable
) {
5555 constexpr uint8_t WARN_AT
= 100;
5556 if (sync
.mNumQueriesBeforeFirstFrameBoundary
<= WARN_AT
) {
5557 sync
.mNumQueriesBeforeFirstFrameBoundary
+= 1;
5558 if (sync
.mNumQueriesBeforeFirstFrameBoundary
== WARN_AT
) {
5560 "ClientWaitSync must return TIMEOUT_EXPIRED until control has"
5561 " returned to the user agent's main loop, but was polled %hhu "
5562 "times. Are you spin-locking? (only warns once)",
5563 sync
.mNumQueriesBeforeFirstFrameBoundary
);
5566 return LOCAL_GL_TIMEOUT_EXPIRED
;
5569 if (mCompletedSyncId
>= sync
.mId
) {
5570 return LOCAL_GL_ALREADY_SIGNALED
;
5572 if (flags
& LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT
) {
5575 constexpr uint8_t WARN_AT
= 100;
5576 if (sync
.mNumQueriesWithoutFlushCommandsBit
<= WARN_AT
) {
5577 sync
.mNumQueriesWithoutFlushCommandsBit
+= 1;
5578 if (sync
.mNumQueriesWithoutFlushCommandsBit
== WARN_AT
) {
5580 "ClientWaitSync with timeout=0 (or GetSyncParameter(SYNC_STATUS)) "
5581 "called %hhu times without SYNC_FLUSH_COMMANDS_BIT. If you do not "
5582 "flush, this sync object is not guaranteed to ever complete.",
5583 sync
.mNumQueriesWithoutFlushCommandsBit
);
5587 if (!timeout
) return LOCAL_GL_TIMEOUT_EXPIRED
;
5590 // Fine, time to block:
5592 const auto ret
= [&]() {
5593 const auto& inProcess
= mNotLost
->inProcess
;
5595 return inProcess
->ClientWaitSync(sync
.mId
, flags
, timeout
);
5597 const auto& child
= mNotLost
->outOfProcess
;
5598 child
->FlushPendingCmds();
5600 if (!child
->SendClientWaitSync(sync
.mId
, flags
, timeout
, &ret
)) {
5607 case LOCAL_GL_CONDITION_SATISFIED
:
5608 case LOCAL_GL_ALREADY_SIGNALED
:
5609 OnSyncComplete(sync
.mId
);
5616 void ClientWebGLContext::WaitSync(const WebGLSyncJS
& sync
,
5617 const GLbitfield flags
,
5618 const GLint64 timeout
) const {
5619 const FuncScope
funcScope(*this, "waitSync");
5620 if (IsContextLost()) return;
5621 if (!sync
.ValidateUsable(*this, "sync")) return;
5624 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`flags` must be 0.");
5627 if (timeout
!= -1) {
5628 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`timeout` must be TIMEOUT_IGNORED.");
5632 JsWarning("waitSync is a no-op.");
5635 // -------------------------- Transform Feedback ---------------------------
5637 void ClientWebGLContext::BindTransformFeedback(
5638 const GLenum target
, WebGLTransformFeedbackJS
* const tf
) {
5639 const FuncScope
funcScope(*this, "bindTransformFeedback");
5640 if (IsContextLost()) return;
5641 if (tf
&& !tf
->ValidateUsable(*this, "tf")) return;
5642 auto& state
= State();
5644 if (target
!= LOCAL_GL_TRANSFORM_FEEDBACK
) {
5645 EnqueueError(LOCAL_GL_INVALID_ENUM
, "`target` must be TRANSFORM_FEEDBACK.");
5648 if (state
.mTfActiveAndNotPaused
) {
5649 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5650 "Current Transform Feedback object is active and not paused.");
5655 tf
->mHasBeenBound
= true;
5656 state
.mBoundTfo
= tf
;
5658 state
.mBoundTfo
= state
.mDefaultTfo
;
5661 Run
<RPROC(BindTransformFeedback
)>(tf
? tf
->mId
: 0);
5664 void ClientWebGLContext::BeginTransformFeedback(const GLenum primMode
) {
5665 const FuncScope
funcScope(*this, "beginTransformFeedback");
5666 if (IsContextLost()) return;
5667 auto& state
= State();
5668 auto& tfo
= *(state
.mBoundTfo
);
5670 if (tfo
.mActiveOrPaused
) {
5671 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5672 "Transform Feedback is already active or paused.");
5675 MOZ_ASSERT(!state
.mTfActiveAndNotPaused
);
5677 auto& prog
= state
.mCurrentProgram
;
5679 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No program in use.");
5682 const auto& linkResult
= GetLinkResult(*prog
);
5683 if (!linkResult
.success
) {
5684 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5685 "Program is not successfully linked.");
5689 auto tfBufferCount
= linkResult
.active
.activeTfVaryings
.size();
5690 if (tfBufferCount
&&
5691 linkResult
.tfBufferMode
== LOCAL_GL_INTERLEAVED_ATTRIBS
) {
5694 if (!tfBufferCount
) {
5695 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5696 "Program does not use Transform Feedback.");
5700 const auto& buffers
= tfo
.mAttribBuffers
;
5701 for (const auto i
: IntegerRange(tfBufferCount
)) {
5703 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5704 "Transform Feedback buffer %u is null.", i
);
5710 case LOCAL_GL_POINTS
:
5711 case LOCAL_GL_LINES
:
5712 case LOCAL_GL_TRIANGLES
:
5715 EnqueueError(LOCAL_GL_INVALID_ENUM
,
5716 "`primitiveMode` must be POINTS, LINES< or TRIANGLES.");
5722 tfo
.mActiveOrPaused
= true;
5723 tfo
.mActiveProgram
= prog
;
5724 tfo
.mActiveProgramKeepAlive
= prog
->mKeepAliveWeak
.lock();
5725 prog
->mActiveTfos
.insert(&tfo
);
5726 state
.mTfActiveAndNotPaused
= true;
5728 Run
<RPROC(BeginTransformFeedback
)>(primMode
);
5731 void ClientWebGLContext::EndTransformFeedback() {
5732 const FuncScope
funcScope(*this, "endTransformFeedback");
5733 if (IsContextLost()) return;
5734 auto& state
= State();
5735 auto& tfo
= *(state
.mBoundTfo
);
5737 if (!tfo
.mActiveOrPaused
) {
5738 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5739 "Transform Feedback is not active or paused.");
5743 tfo
.mActiveOrPaused
= false;
5744 tfo
.mActiveProgram
->mActiveTfos
.erase(&tfo
);
5745 tfo
.mActiveProgram
= nullptr;
5746 tfo
.mActiveProgramKeepAlive
= nullptr;
5747 state
.mTfActiveAndNotPaused
= false;
5748 Run
<RPROC(EndTransformFeedback
)>();
5751 void ClientWebGLContext::PauseTransformFeedback() {
5752 const FuncScope
funcScope(*this, "pauseTransformFeedback");
5753 if (IsContextLost()) return;
5754 auto& state
= State();
5755 auto& tfo
= *(state
.mBoundTfo
);
5757 if (!tfo
.mActiveOrPaused
) {
5758 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5759 "Transform Feedback is not active.");
5762 if (!state
.mTfActiveAndNotPaused
) {
5763 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5764 "Transform Feedback is already paused.");
5768 state
.mTfActiveAndNotPaused
= false;
5769 Run
<RPROC(PauseTransformFeedback
)>();
5772 void ClientWebGLContext::ResumeTransformFeedback() {
5773 const FuncScope
funcScope(*this, "resumeTransformFeedback");
5774 if (IsContextLost()) return;
5775 auto& state
= State();
5776 auto& tfo
= *(state
.mBoundTfo
);
5778 if (!tfo
.mActiveOrPaused
) {
5779 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5780 "Transform Feedback is not active and paused.");
5783 if (state
.mTfActiveAndNotPaused
) {
5784 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5785 "Transform Feedback is not paused.");
5788 if (state
.mCurrentProgram
!= tfo
.mActiveProgram
) {
5790 LOCAL_GL_INVALID_OPERATION
,
5791 "Cannot Resume Transform Feedback with a program link result different"
5792 " from when Begin was called.");
5796 state
.mTfActiveAndNotPaused
= true;
5797 Run
<RPROC(ResumeTransformFeedback
)>();
5800 void ClientWebGLContext::SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS
* fb
,
5802 fb
->mInOpaqueRAF
= value
;
5803 Run
<RPROC(SetFramebufferIsInOpaqueRAF
)>(fb
->mId
, value
);
5806 // ---------------------------- Misc Extensions ----------------------------
5807 void ClientWebGLContext::DrawBuffers(const dom::Sequence
<GLenum
>& buffers
) {
5808 const auto range
= MakeRange(buffers
);
5809 const auto vec
= std::vector
<GLenum
>(range
.begin().get(), range
.end().get());
5810 Run
<RPROC(DrawBuffers
)>(vec
);
5813 void ClientWebGLContext::EnqueueErrorImpl(const GLenum error
,
5814 const nsACString
& text
) const {
5815 if (!mNotLost
) return; // Ignored if context is lost.
5817 Run
<RPROC(GenerateError
)>(error
, ToString(text
));
5820 void ClientWebGLContext::RequestExtension(const WebGLExtensionID ext
) const {
5821 Run
<RPROC(RequestExtension
)>(ext
);
5826 bool ClientWebGLContext::IsExtensionForbiddenForCaller(
5827 const WebGLExtensionID ext
, const dom::CallerType callerType
) const {
5828 if (callerType
== dom::CallerType::System
) {
5832 if (StaticPrefs::webgl_enable_privileged_extensions()) {
5837 case WebGLExtensionID::MOZ_debug
:
5840 case WebGLExtensionID::WEBGL_debug_renderer_info
:
5841 return ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo
) ||
5842 !StaticPrefs::webgl_enable_debug_renderer_info();
5844 case WebGLExtensionID::WEBGL_debug_shaders
:
5845 return ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo
);
5852 bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext
,
5853 const dom::CallerType callerType
) const {
5854 if (IsExtensionForbiddenForCaller(ext
, callerType
)) {
5858 const auto& limits
= Limits();
5859 return limits
.supportedExtensions
[ext
];
5862 void ClientWebGLContext::GetSupportedExtensions(
5863 dom::Nullable
<nsTArray
<nsString
>>& retval
,
5864 const dom::CallerType callerType
) const {
5866 if (!mNotLost
) return;
5868 auto& retarr
= retval
.SetValue();
5869 for (const auto i
: MakeEnumeratedRange(WebGLExtensionID::Max
)) {
5870 if (!IsSupported(i
, callerType
)) continue;
5872 const auto& extStr
= GetExtensionName(i
);
5873 retarr
.AppendElement(NS_ConvertUTF8toUTF16(extStr
));
5879 void ClientWebGLContext::GetSupportedProfilesASTC(
5880 dom::Nullable
<nsTArray
<nsString
>>& retval
) const {
5882 if (!mNotLost
) return;
5883 const auto& limits
= Limits();
5885 auto& retarr
= retval
.SetValue();
5886 retarr
.AppendElement(u
"ldr"_ns
);
5887 if (limits
.astcHdr
) {
5888 retarr
.AppendElement(u
"hdr"_ns
);
5892 void ClientWebGLContext::ProvokingVertex(const GLenum rawMode
) const {
5893 const FuncScope
funcScope(*this, "provokingVertex");
5894 if (IsContextLost()) return;
5896 const auto mode
= AsEnumCase
<webgl::ProvokingVertex
>(rawMode
);
5898 EnqueueError_ArgEnum("mode", rawMode
);
5902 Run
<RPROC(ProvokingVertex
)>(*mode
);
5904 funcScope
.mKeepNotLostOrNull
->state
.mProvokingVertex
= *mode
;
5909 uint32_t ClientWebGLContext::GetPrincipalHashValue() const {
5910 if (mCanvasElement
) {
5911 return mCanvasElement
->NodePrincipal()->GetHashValue();
5913 if (mOffscreenCanvas
) {
5914 nsIGlobalObject
* global
= mOffscreenCanvas
->GetOwnerGlobal();
5916 nsIPrincipal
* principal
= global
->PrincipalOrNull();
5918 return principal
->GetHashValue();
5925 // ---------------------------
5927 void ClientWebGLContext::EnqueueError_ArgEnum(const char* const argName
,
5928 const GLenum val
) const {
5929 EnqueueError(LOCAL_GL_INVALID_ENUM
, "Bad `%s`: 0x%04x", argName
, val
);
5935 void ClientWebGLContext::AttachShader(WebGLProgramJS
& prog
,
5936 WebGLShaderJS
& shader
) const {
5937 const FuncScope
funcScope(*this, "attachShader");
5938 if (IsContextLost()) return;
5939 if (!prog
.ValidateUsable(*this, "program")) return;
5940 if (!shader
.ValidateUsable(*this, "shader")) return;
5942 auto& slot
= *MaybeFind(prog
.mNextLink_Shaders
, shader
.mType
);
5944 if (&shader
== slot
.shader
) {
5945 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`shader` is already attached.");
5947 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5948 "Only one of each type of"
5949 " shader may be attached to a program.");
5953 slot
= {&shader
, shader
.mKeepAliveWeak
.lock()};
5955 Run
<RPROC(AttachShader
)>(prog
.mId
, shader
.mId
);
5958 void ClientWebGLContext::BindAttribLocation(WebGLProgramJS
& prog
,
5959 const GLuint location
,
5960 const nsAString
& name
) const {
5961 const FuncScope
funcScope(*this, "bindAttribLocation");
5962 if (IsContextLost()) return;
5963 if (!prog
.ValidateUsable(*this, "program")) return;
5965 const auto& nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5966 Run
<RPROC(BindAttribLocation
)>(prog
.mId
, location
, nameU8
);
5969 void ClientWebGLContext::DetachShader(WebGLProgramJS
& prog
,
5970 const WebGLShaderJS
& shader
) const {
5971 const FuncScope
funcScope(*this, "detachShader");
5972 if (IsContextLost()) return;
5973 if (!prog
.ValidateUsable(*this, "program")) return;
5974 if (!shader
.ValidateUsable(*this, "shader")) return;
5976 auto& slot
= *MaybeFind(prog
.mNextLink_Shaders
, shader
.mType
);
5978 if (slot
.shader
!= &shader
) {
5979 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`shader` is not attached.");
5984 Run
<RPROC(DetachShader
)>(prog
.mId
, shader
.mId
);
5987 void ClientWebGLContext::GetAttachedShaders(
5988 const WebGLProgramJS
& prog
,
5989 dom::Nullable
<nsTArray
<RefPtr
<WebGLShaderJS
>>>& retval
) const {
5990 const FuncScope
funcScope(*this, "getAttachedShaders");
5991 if (IsContextLost()) return;
5992 if (!prog
.ValidateUsable(*this, "program")) return;
5994 auto& arr
= retval
.SetValue();
5995 for (const auto& pair
: prog
.mNextLink_Shaders
) {
5996 const auto& attachment
= pair
.second
;
5997 if (!attachment
.shader
) continue;
5998 arr
.AppendElement(attachment
.shader
);
6002 void ClientWebGLContext::LinkProgram(WebGLProgramJS
& prog
) const {
6003 const FuncScope
funcScope(*this, "linkProgram");
6004 if (IsContextLost()) return;
6005 if (!prog
.ValidateUsable(*this, "program")) return;
6007 if (!prog
.mActiveTfos
.empty()) {
6008 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
6009 "Program still in use by active or paused"
6010 " Transform Feedback objects.");
6014 prog
.mResult
= std::make_shared
<webgl::LinkResult
>();
6015 prog
.mUniformLocByName
= Nothing();
6016 prog
.mUniformBlockBindings
= {};
6017 Run
<RPROC(LinkProgram
)>(prog
.mId
);
6020 void ClientWebGLContext::TransformFeedbackVaryings(
6021 WebGLProgramJS
& prog
, const dom::Sequence
<nsString
>& varyings
,
6022 const GLenum bufferMode
) const {
6023 const FuncScope
funcScope(*this, "transformFeedbackVaryings");
6024 if (IsContextLost()) return;
6025 if (!prog
.ValidateUsable(*this, "program")) return;
6027 std::vector
<std::string
> varyingsU8
;
6028 varyingsU8
.reserve(varyings
.Length());
6029 for (const auto& cur
: varyings
) {
6030 const auto curU8
= ToString(NS_ConvertUTF16toUTF8(cur
));
6031 varyingsU8
.push_back(curU8
);
6034 Run
<RPROC(TransformFeedbackVaryings
)>(prog
.mId
, varyingsU8
, bufferMode
);
6037 void ClientWebGLContext::UniformBlockBinding(WebGLProgramJS
& prog
,
6038 const GLuint blockIndex
,
6039 const GLuint blockBinding
) const {
6040 const FuncScope
funcScope(*this, "uniformBlockBinding");
6041 if (IsContextLost()) return;
6042 if (!prog
.ValidateUsable(*this, "program")) return;
6043 const auto& state
= State();
6045 (void)GetLinkResult(prog
);
6046 auto& list
= prog
.mUniformBlockBindings
;
6047 if (blockIndex
>= list
.size()) {
6049 LOCAL_GL_INVALID_VALUE
,
6050 "`blockIndex` (%u) must be less than ACTIVE_UNIFORM_BLOCKS (%zu).",
6051 blockIndex
, list
.size());
6054 if (blockBinding
>= state
.mBoundUbos
.size()) {
6055 EnqueueError(LOCAL_GL_INVALID_VALUE
,
6056 "`blockBinding` (%u) must be less than "
6057 "MAX_UNIFORM_BUFFER_BINDINGS (%zu).",
6058 blockBinding
, state
.mBoundUbos
.size());
6062 list
[blockIndex
] = blockBinding
;
6063 Run
<RPROC(UniformBlockBinding
)>(prog
.mId
, blockIndex
, blockBinding
);
6066 // WebGLProgramJS link result reflection
6068 already_AddRefed
<WebGLActiveInfoJS
> ClientWebGLContext::GetActiveAttrib(
6069 const WebGLProgramJS
& prog
, const GLuint index
) {
6070 const FuncScope
funcScope(*this, "getActiveAttrib");
6071 if (IsContextLost()) return nullptr;
6072 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
6074 const auto& res
= GetLinkResult(prog
);
6075 const auto& list
= res
.active
.activeAttribs
;
6076 if (index
>= list
.size()) {
6077 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
6081 const auto& info
= list
[index
];
6082 return AsAddRefed(new WebGLActiveInfoJS(info
));
6085 already_AddRefed
<WebGLActiveInfoJS
> ClientWebGLContext::GetActiveUniform(
6086 const WebGLProgramJS
& prog
, const GLuint index
) {
6087 const FuncScope
funcScope(*this, "getActiveUniform");
6088 if (IsContextLost()) return nullptr;
6089 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
6091 const auto& res
= GetLinkResult(prog
);
6092 const auto& list
= res
.active
.activeUniforms
;
6093 if (index
>= list
.size()) {
6094 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
6098 const auto& info
= list
[index
];
6099 return AsAddRefed(new WebGLActiveInfoJS(info
));
6102 void ClientWebGLContext::GetActiveUniformBlockName(const WebGLProgramJS
& prog
,
6104 nsAString
& retval
) const {
6105 retval
.SetIsVoid(true);
6106 const FuncScope
funcScope(*this, "getActiveUniformBlockName");
6107 if (IsContextLost()) return;
6108 if (!prog
.ValidateUsable(*this, "program")) return;
6110 const auto& res
= GetLinkResult(prog
);
6112 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "Program has not been linked.");
6116 const auto& list
= res
.active
.activeUniformBlocks
;
6117 if (index
>= list
.size()) {
6118 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
6122 const auto& block
= list
[index
];
6123 CopyUTF8toUTF16(block
.name
, retval
);
6126 void ClientWebGLContext::GetActiveUniformBlockParameter(
6127 JSContext
* const cx
, const WebGLProgramJS
& prog
, const GLuint index
,
6128 const GLenum pname
, JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) {
6129 retval
.set(JS::NullValue());
6130 const FuncScope
funcScope(*this, "getActiveUniformBlockParameter");
6131 if (IsContextLost()) return;
6132 if (!prog
.ValidateUsable(*this, "program")) return;
6134 const auto& res
= GetLinkResult(prog
);
6135 const auto& list
= res
.active
.activeUniformBlocks
;
6136 if (index
>= list
.size()) {
6137 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
6140 const auto& block
= list
[index
];
6142 retval
.set([&]() -> JS::Value
{
6144 case LOCAL_GL_UNIFORM_BLOCK_BINDING
:
6145 return JS::NumberValue(prog
.mUniformBlockBindings
[index
]);
6147 case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE
:
6148 return JS::NumberValue(block
.dataSize
);
6150 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
:
6151 return JS::NumberValue(block
.activeUniformIndices
.size());
6153 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
: {
6154 const auto& indices
= block
.activeUniformIndices
;
6155 return Create
<dom::Uint32Array
>(cx
, this, indices
, rv
);
6158 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
:
6159 return JS::BooleanValue(block
.referencedByVertexShader
);
6161 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER
:
6162 return JS::BooleanValue(block
.referencedByFragmentShader
);
6165 EnqueueError_ArgEnum("pname", pname
);
6166 return JS::NullValue();
6171 void ClientWebGLContext::GetActiveUniforms(
6172 JSContext
* const cx
, const WebGLProgramJS
& prog
,
6173 const dom::Sequence
<GLuint
>& uniformIndices
, const GLenum pname
,
6174 JS::MutableHandle
<JS::Value
> retval
) const {
6175 retval
.set(JS::NullValue());
6176 const FuncScope
funcScope(*this, "getActiveUniforms");
6177 if (IsContextLost()) return;
6178 if (!prog
.ValidateUsable(*this, "program")) return;
6180 const auto& res
= GetLinkResult(prog
);
6181 const auto& list
= res
.active
.activeUniforms
;
6183 const auto count
= uniformIndices
.Length();
6184 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, count
));
6185 if (!array
) return; // Just bail.
6187 for (const auto i
: IntegerRange(count
)) {
6188 const auto index
= uniformIndices
[i
];
6189 if (index
>= list
.size()) {
6190 EnqueueError(LOCAL_GL_INVALID_VALUE
,
6191 "`uniformIndices[%u]`: `%u` too large.", i
, index
);
6194 const auto& uniform
= list
[index
];
6196 JS::Rooted
<JS::Value
> value(cx
);
6198 case LOCAL_GL_UNIFORM_TYPE
:
6199 value
= JS::NumberValue(uniform
.elemType
);
6202 case LOCAL_GL_UNIFORM_SIZE
:
6203 value
= JS::NumberValue(uniform
.elemCount
);
6206 case LOCAL_GL_UNIFORM_BLOCK_INDEX
:
6207 value
= JS::NumberValue(uniform
.block_index
);
6210 case LOCAL_GL_UNIFORM_OFFSET
:
6211 value
= JS::NumberValue(uniform
.block_offset
);
6214 case LOCAL_GL_UNIFORM_ARRAY_STRIDE
:
6215 value
= JS::NumberValue(uniform
.block_arrayStride
);
6218 case LOCAL_GL_UNIFORM_MATRIX_STRIDE
:
6219 value
= JS::NumberValue(uniform
.block_matrixStride
);
6222 case LOCAL_GL_UNIFORM_IS_ROW_MAJOR
:
6223 value
= JS::BooleanValue(uniform
.block_isRowMajor
);
6227 EnqueueError_ArgEnum("pname", pname
);
6230 if (!JS_DefineElement(cx
, array
, i
, value
, JSPROP_ENUMERATE
)) return;
6233 retval
.setObject(*array
);
6236 already_AddRefed
<WebGLActiveInfoJS
>
6237 ClientWebGLContext::GetTransformFeedbackVarying(const WebGLProgramJS
& prog
,
6238 const GLuint index
) {
6239 const FuncScope
funcScope(*this, "getTransformFeedbackVarying");
6240 if (IsContextLost()) return nullptr;
6241 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
6243 const auto& res
= GetLinkResult(prog
);
6244 const auto& list
= res
.active
.activeTfVaryings
;
6245 if (index
>= list
.size()) {
6246 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
6250 const auto& info
= list
[index
];
6251 return AsAddRefed(new WebGLActiveInfoJS(info
));
6254 GLint
ClientWebGLContext::GetAttribLocation(const WebGLProgramJS
& prog
,
6255 const nsAString
& name
) const {
6256 const FuncScope
funcScope(*this, "getAttribLocation");
6257 if (IsContextLost()) return -1;
6258 if (!prog
.ValidateUsable(*this, "program")) return -1;
6260 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
6261 const auto& res
= GetLinkResult(prog
);
6262 for (const auto& cur
: res
.active
.activeAttribs
) {
6263 if (cur
.name
== nameU8
) return cur
.location
;
6266 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
6268 EnqueueError(err
->type
, "%s", err
->info
.c_str());
6273 GLint
ClientWebGLContext::GetFragDataLocation(const WebGLProgramJS
& prog
,
6274 const nsAString
& name
) const {
6275 const FuncScope
funcScope(*this, "getFragDataLocation");
6276 if (IsContextLost()) return -1;
6277 if (!prog
.ValidateUsable(*this, "program")) return -1;
6279 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
6281 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
6288 const auto& inProcess
= mNotLost
->inProcess
;
6290 return inProcess
->GetFragDataLocation(prog
.mId
, nameU8
);
6292 const auto& child
= mNotLost
->outOfProcess
;
6293 child
->FlushPendingCmds();
6295 if (!child
->SendGetFragDataLocation(prog
.mId
, nameU8
, &ret
)) {
6302 GLuint
ClientWebGLContext::GetUniformBlockIndex(
6303 const WebGLProgramJS
& prog
, const nsAString
& blockName
) const {
6304 const FuncScope
funcScope(*this, "getUniformBlockIndex");
6305 if (IsContextLost()) return LOCAL_GL_INVALID_INDEX
;
6306 if (!prog
.ValidateUsable(*this, "program")) return LOCAL_GL_INVALID_INDEX
;
6308 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(blockName
));
6310 const auto& res
= GetLinkResult(prog
);
6311 const auto& list
= res
.active
.activeUniformBlocks
;
6312 for (const auto i
: IntegerRange(list
.size())) {
6313 const auto& cur
= list
[i
];
6314 if (cur
.name
== nameU8
) {
6318 return LOCAL_GL_INVALID_INDEX
;
6321 void ClientWebGLContext::GetUniformIndices(
6322 const WebGLProgramJS
& prog
, const dom::Sequence
<nsString
>& uniformNames
,
6323 dom::Nullable
<nsTArray
<GLuint
>>& retval
) const {
6324 const FuncScope
funcScope(*this, "getUniformIndices");
6325 if (IsContextLost()) return;
6326 if (!prog
.ValidateUsable(*this, "program")) return;
6328 const auto& res
= GetLinkResult(prog
);
6329 auto ret
= nsTArray
<GLuint
>(uniformNames
.Length());
6331 for (const auto& queriedNameU16
: uniformNames
) {
6332 const auto queriedName
= ToString(NS_ConvertUTF16toUTF8(queriedNameU16
));
6333 const auto impliedProperArrayQueriedName
= queriedName
+ "[0]";
6335 GLuint activeId
= LOCAL_GL_INVALID_INDEX
;
6336 for (const auto i
: IntegerRange(res
.active
.activeUniforms
.size())) {
6337 // O(N^2) ok for small N.
6338 const auto& activeInfoForI
= res
.active
.activeUniforms
[i
];
6339 if (queriedName
== activeInfoForI
.name
||
6340 impliedProperArrayQueriedName
== activeInfoForI
.name
) {
6345 ret
.AppendElement(activeId
);
6348 retval
.SetValue(std::move(ret
));
6351 already_AddRefed
<WebGLUniformLocationJS
> ClientWebGLContext::GetUniformLocation(
6352 const WebGLProgramJS
& prog
, const nsAString
& name
) const {
6353 const FuncScope
funcScope(*this, "getUniformLocation");
6354 if (IsContextLost()) return nullptr;
6355 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
6357 const auto& res
= GetLinkResult(prog
);
6359 if (!prog
.mUniformLocByName
) {
6360 // Cache a map from name->location.
6361 // Since the only way to set uniforms requires calling GetUniformLocation,
6362 // we expect apps to query most active uniforms once for each scalar or
6363 // array. NB: Uniform array setters do have overflow semantics, even though
6364 // uniform locations aren't guaranteed contiguous, but GetUniformLocation
6365 // must still be called once per array.
6366 prog
.mUniformLocByName
.emplace();
6368 for (const auto& activeUniform
: res
.active
.activeUniforms
) {
6369 if (activeUniform
.block_index
!= -1) continue;
6371 auto locName
= activeUniform
.name
;
6372 const auto indexed
= webgl::ParseIndexed(locName
);
6374 locName
= indexed
->name
;
6377 const auto err
= CheckGLSLVariableName(mIsWebGL2
, locName
);
6380 const auto baseLength
= locName
.size();
6381 for (const auto& pair
: activeUniform
.locByIndex
) {
6383 locName
.erase(baseLength
); // Erase previous "[N]".
6385 locName
+= std::to_string(pair
.first
);
6388 const auto locInfo
=
6389 WebGLProgramJS::UniformLocInfo
{pair
.second
, activeUniform
.elemType
};
6390 prog
.mUniformLocByName
->insert({locName
, locInfo
});
6394 const auto& locByName
= *(prog
.mUniformLocByName
);
6396 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
6397 auto loc
= MaybeFind(locByName
, nameU8
);
6399 loc
= MaybeFind(locByName
, nameU8
+ "[0]");
6402 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
6404 EnqueueError(err
->type
, "%s", err
->info
.c_str());
6409 return AsAddRefed(new WebGLUniformLocationJS(*this, prog
.mResult
,
6410 loc
->location
, loc
->elemType
));
6413 std::array
<uint16_t, 3> ValidUploadElemTypes(const GLenum elemType
) {
6414 std::vector
<GLenum
> ret
;
6417 ret
= {LOCAL_GL_FLOAT
, LOCAL_GL_INT
, LOCAL_GL_UNSIGNED_INT
};
6419 case LOCAL_GL_BOOL_VEC2
:
6420 ret
= {LOCAL_GL_FLOAT_VEC2
, LOCAL_GL_INT_VEC2
,
6421 LOCAL_GL_UNSIGNED_INT_VEC2
};
6423 case LOCAL_GL_BOOL_VEC3
:
6424 ret
= {LOCAL_GL_FLOAT_VEC3
, LOCAL_GL_INT_VEC3
,
6425 LOCAL_GL_UNSIGNED_INT_VEC3
};
6427 case LOCAL_GL_BOOL_VEC4
:
6428 ret
= {LOCAL_GL_FLOAT_VEC4
, LOCAL_GL_INT_VEC4
,
6429 LOCAL_GL_UNSIGNED_INT_VEC4
};
6432 case LOCAL_GL_SAMPLER_2D
:
6433 case LOCAL_GL_SAMPLER_3D
:
6434 case LOCAL_GL_SAMPLER_CUBE
:
6435 case LOCAL_GL_SAMPLER_2D_SHADOW
:
6436 case LOCAL_GL_SAMPLER_2D_ARRAY
:
6437 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
6438 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
6439 case LOCAL_GL_INT_SAMPLER_2D
:
6440 case LOCAL_GL_INT_SAMPLER_3D
:
6441 case LOCAL_GL_INT_SAMPLER_CUBE
:
6442 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
6443 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
6444 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
6445 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
6446 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
:
6447 ret
= {LOCAL_GL_INT
};
6455 std::array
<uint16_t, 3> arr
= {};
6456 MOZ_ASSERT(arr
[2] == 0);
6457 for (const auto i
: IntegerRange(ret
.size())) {
6458 arr
[i
] = AssertedCast
<uint16_t>(ret
[i
]);
6463 void ClientWebGLContext::GetProgramInfoLog(const WebGLProgramJS
& prog
,
6464 nsAString
& retval
) const {
6465 retval
.SetIsVoid(true);
6466 const FuncScope
funcScope(*this, "getProgramInfoLog");
6467 if (IsContextLost()) return;
6468 if (!prog
.ValidateUsable(*this, "program")) return;
6470 const auto& res
= GetLinkResult(prog
);
6471 CopyUTF8toUTF16(res
.log
, retval
);
6474 void ClientWebGLContext::GetProgramParameter(
6475 JSContext
* const js
, const WebGLProgramJS
& prog
, const GLenum pname
,
6476 JS::MutableHandle
<JS::Value
> retval
) const {
6477 retval
.set(JS::NullValue());
6478 const FuncScope
funcScope(*this, "getProgramParameter");
6479 if (IsContextLost()) return;
6480 if (!prog
.ValidateUsable(*this, "program")) return;
6482 retval
.set([&]() -> JS::Value
{
6484 case LOCAL_GL_DELETE_STATUS
:
6485 // "Is flagged for deletion?"
6486 return JS::BooleanValue(!prog
.mKeepAlive
);
6487 case LOCAL_GL_VALIDATE_STATUS
:
6488 return JS::BooleanValue(prog
.mLastValidate
);
6489 case LOCAL_GL_ATTACHED_SHADERS
: {
6491 for (const auto& pair
: prog
.mNextLink_Shaders
) {
6492 const auto& slot
= pair
.second
;
6497 return JS::NumberValue(shaders
);
6503 const auto& res
= GetLinkResult(prog
);
6506 case LOCAL_GL_LINK_STATUS
:
6507 return JS::BooleanValue(res
.success
);
6509 case LOCAL_GL_ACTIVE_ATTRIBUTES
:
6510 return JS::NumberValue(res
.active
.activeAttribs
.size());
6512 case LOCAL_GL_ACTIVE_UNIFORMS
:
6513 return JS::NumberValue(res
.active
.activeUniforms
.size());
6515 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE
:
6516 if (!mIsWebGL2
) break;
6517 return JS::NumberValue(res
.tfBufferMode
);
6519 case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS
:
6520 if (!mIsWebGL2
) break;
6521 return JS::NumberValue(res
.active
.activeTfVaryings
.size());
6523 case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS
:
6524 if (!mIsWebGL2
) break;
6525 return JS::NumberValue(res
.active
.activeUniformBlocks
.size());
6530 EnqueueError_ArgEnum("pname", pname
);
6531 return JS::NullValue();
6538 void ClientWebGLContext::CompileShader(WebGLShaderJS
& shader
) const {
6539 const FuncScope
funcScope(*this, "compileShader");
6540 if (IsContextLost()) return;
6541 if (!shader
.ValidateUsable(*this, "shader")) return;
6543 shader
.mResult
= {};
6544 Run
<RPROC(CompileShader
)>(shader
.mId
);
6547 void ClientWebGLContext::GetShaderInfoLog(const WebGLShaderJS
& shader
,
6548 nsAString
& retval
) const {
6549 retval
.SetIsVoid(true);
6550 const FuncScope
funcScope(*this, "getShaderInfoLog");
6551 if (IsContextLost()) return;
6552 if (!shader
.ValidateUsable(*this, "shader")) return;
6554 const auto& result
= GetCompileResult(shader
);
6555 CopyUTF8toUTF16(result
.log
, retval
);
6558 void ClientWebGLContext::GetShaderParameter(
6559 JSContext
* const cx
, const WebGLShaderJS
& shader
, const GLenum pname
,
6560 JS::MutableHandle
<JS::Value
> retval
) const {
6561 retval
.set(JS::NullValue());
6562 const FuncScope
funcScope(*this, "getShaderParameter");
6563 if (IsContextLost()) return;
6564 if (!shader
.ValidateUsable(*this, "shader")) return;
6566 retval
.set([&]() -> JS::Value
{
6568 case LOCAL_GL_SHADER_TYPE
:
6569 return JS::NumberValue(shader
.mType
);
6571 case LOCAL_GL_DELETE_STATUS
: // "Is flagged for deletion?"
6572 return JS::BooleanValue(!shader
.mKeepAlive
);
6574 case LOCAL_GL_COMPILE_STATUS
: {
6575 const auto& result
= GetCompileResult(shader
);
6576 return JS::BooleanValue(result
.success
);
6580 EnqueueError_ArgEnum("pname", pname
);
6581 return JS::NullValue();
6586 void ClientWebGLContext::GetShaderSource(const WebGLShaderJS
& shader
,
6587 nsAString
& retval
) const {
6588 retval
.SetIsVoid(true);
6589 const FuncScope
funcScope(*this, "getShaderSource");
6590 if (IsContextLost()) return;
6591 if (!shader
.ValidateUsable(*this, "shader")) return;
6593 CopyUTF8toUTF16(shader
.mSource
, retval
);
6596 void ClientWebGLContext::GetTranslatedShaderSource(const WebGLShaderJS
& shader
,
6597 nsAString
& retval
) const {
6598 retval
.SetIsVoid(true);
6599 const FuncScope
funcScope(*this, "getTranslatedShaderSource");
6600 if (IsContextLost()) return;
6601 if (!shader
.ValidateUsable(*this, "shader")) return;
6603 const auto& result
= GetCompileResult(shader
);
6604 CopyUTF8toUTF16(result
.translatedSource
, retval
);
6607 void ClientWebGLContext::ShaderSource(WebGLShaderJS
& shader
,
6608 const nsAString
& sourceU16
) const {
6609 const FuncScope
funcScope(*this, "shaderSource");
6610 if (IsContextLost()) return;
6611 if (!shader
.ValidateUsable(*this, "shader")) return;
6613 shader
.mSource
= ToString(NS_ConvertUTF16toUTF8(sourceU16
));
6614 Run
<RPROC(ShaderSource
)>(shader
.mId
, shader
.mSource
);
6619 const webgl::CompileResult
& ClientWebGLContext::GetCompileResult(
6620 const WebGLShaderJS
& shader
) const {
6621 if (shader
.mResult
.pending
) {
6622 shader
.mResult
= [&]() {
6623 const auto& inProcess
= mNotLost
->inProcess
;
6625 return inProcess
->GetCompileResult(shader
.mId
);
6627 const auto& child
= mNotLost
->outOfProcess
;
6628 child
->FlushPendingCmds();
6629 webgl::CompileResult ret
= {};
6630 if (!child
->SendGetCompileResult(shader
.mId
, &ret
)) {
6636 return shader
.mResult
;
6639 const webgl::LinkResult
& ClientWebGLContext::GetLinkResult(
6640 const WebGLProgramJS
& prog
) const {
6641 if (prog
.mResult
->pending
) {
6642 const auto notLost
=
6643 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
6644 if (!notLost
) return *(prog
.mResult
);
6646 *(prog
.mResult
) = [&]() {
6647 const auto& inProcess
= mNotLost
->inProcess
;
6649 return inProcess
->GetLinkResult(prog
.mId
);
6651 const auto& child
= mNotLost
->outOfProcess
;
6652 child
->FlushPendingCmds();
6653 webgl::LinkResult ret
;
6654 if (!child
->SendGetLinkResult(prog
.mId
, &ret
)) {
6660 prog
.mUniformBlockBindings
.resize(
6661 prog
.mResult
->active
.activeUniformBlocks
.size());
6663 auto& state
= State();
6664 if (state
.mCurrentProgram
== &prog
&& prog
.mResult
->success
) {
6665 state
.mActiveLinkResult
= prog
.mResult
;
6668 return *(prog
.mResult
);
6673 // ---------------------------
6675 Maybe
<Span
<uint8_t>> ClientWebGLContext::ValidateArrayBufferView(
6676 const Span
<uint8_t>& bytes
, size_t elemSize
, GLuint elemOffset
,
6677 GLuint elemCountOverride
, const GLenum errorEnum
) const {
6678 size_t elemCount
= bytes
.Length() / elemSize
;
6679 if (elemOffset
> elemCount
) {
6680 EnqueueError(errorEnum
, "Invalid offset into ArrayBufferView.");
6683 elemCount
-= elemOffset
;
6685 if (elemCountOverride
) {
6686 if (elemCountOverride
> elemCount
) {
6687 EnqueueError(errorEnum
, "Invalid sub-length for ArrayBufferView.");
6690 elemCount
= elemCountOverride
;
6693 return Some(bytes
.Subspan(elemOffset
* elemSize
, elemCount
* elemSize
));
6696 // ---------------------------
6698 webgl::ObjectJS::ObjectJS(const ClientWebGLContext
* const webgl
)
6699 : mGeneration(webgl
? webgl
->mNotLost
: nullptr),
6700 mId(webgl
? webgl
->NextId() : 0) {}
6704 WebGLFramebufferJS::WebGLFramebufferJS(const ClientWebGLContext
& webgl
,
6706 : webgl::ObjectJS(&webgl
), mOpaque(opaque
) {
6707 (void)mAttachments
[LOCAL_GL_DEPTH_ATTACHMENT
];
6708 (void)mAttachments
[LOCAL_GL_STENCIL_ATTACHMENT
];
6709 if (!webgl
.mIsWebGL2
) {
6710 (void)mAttachments
[LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
];
6713 EnsureColorAttachments();
6716 void WebGLFramebufferJS::EnsureColorAttachments() {
6717 const auto& webgl
= Context();
6718 if (!webgl
) return; // Context is lost.
6720 const auto& limits
= webgl
->Limits();
6721 auto maxColorDrawBuffers
= limits
.maxColorDrawBuffers
;
6722 if (!webgl
->mIsWebGL2
&&
6723 !webgl
->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
)) {
6724 maxColorDrawBuffers
= 1;
6726 for (const auto i
: IntegerRange(maxColorDrawBuffers
)) {
6727 (void)mAttachments
[LOCAL_GL_COLOR_ATTACHMENT0
+ i
];
6731 WebGLProgramJS::WebGLProgramJS(const ClientWebGLContext
& webgl
)
6732 : webgl::ObjectJS(&webgl
),
6733 mKeepAlive(std::make_shared
<webgl::ProgramKeepAlive
>(*this)),
6734 mKeepAliveWeak(mKeepAlive
) {
6735 (void)mNextLink_Shaders
[LOCAL_GL_VERTEX_SHADER
];
6736 (void)mNextLink_Shaders
[LOCAL_GL_FRAGMENT_SHADER
];
6738 mResult
= std::make_shared
<webgl::LinkResult
>();
6741 WebGLShaderJS::WebGLShaderJS(const ClientWebGLContext
& webgl
, const GLenum type
)
6742 : webgl::ObjectJS(&webgl
),
6744 mKeepAlive(std::make_shared
<webgl::ShaderKeepAlive
>(*this)),
6745 mKeepAliveWeak(mKeepAlive
) {}
6747 WebGLTransformFeedbackJS::WebGLTransformFeedbackJS(
6748 const ClientWebGLContext
& webgl
)
6749 : webgl::ObjectJS(&webgl
),
6750 mAttribBuffers(webgl::kMaxTransformFeedbackSeparateAttribs
) {}
6752 WebGLVertexArrayJS::WebGLVertexArrayJS(const ClientWebGLContext
* const webgl
)
6753 : webgl::ObjectJS(webgl
),
6754 mAttribBuffers(Context() ? Context()->Limits().maxVertexAttribs
: 0) {}
6758 #define _(WebGLType) \
6759 JSObject* WebGLType##JS::WrapObject(JSContext* const cx, \
6760 JS::Handle<JSObject*> givenProto) { \
6761 return dom::WebGLType##_Binding::Wrap(cx, this, givenProto); \
6768 _(WebGLRenderbuffer
)
6773 _(WebGLTransformFeedback
)
6774 _(WebGLUniformLocation
)
6775 //_(WebGLVertexArray) // The webidl is `WebGLVertexArrayObject` :(
6779 JSObject
* WebGLVertexArrayJS::WrapObject(JSContext
* const cx
,
6780 JS::Handle
<JSObject
*> givenProto
) {
6781 return dom::WebGLVertexArrayObject_Binding::Wrap(cx
, this, givenProto
);
6784 bool WebGLActiveInfoJS::WrapObject(JSContext
* const cx
,
6785 JS::Handle
<JSObject
*> givenProto
,
6786 JS::MutableHandle
<JSObject
*> reflector
) {
6787 return dom::WebGLActiveInfo_Binding::Wrap(cx
, this, givenProto
, reflector
);
6790 bool WebGLShaderPrecisionFormatJS::WrapObject(
6791 JSContext
* const cx
, JS::Handle
<JSObject
*> givenProto
,
6792 JS::MutableHandle
<JSObject
*> reflector
) {
6793 return dom::WebGLShaderPrecisionFormat_Binding::Wrap(cx
, this, givenProto
,
6797 // ---------------------
6799 template <typename T
>
6800 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6801 const std::vector
<RefPtr
<T
>>& field
,
6802 const char* name
, uint32_t flags
) {
6803 for (const auto& cur
: field
) {
6804 ImplCycleCollectionTraverse(callback
, cur
, name
, flags
);
6808 template <typename T
>
6809 void ImplCycleCollectionUnlink(std::vector
<RefPtr
<T
>>& field
) {
6815 template <typename T
, size_t N
>
6816 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6817 const std::array
<RefPtr
<T
>, N
>& field
,
6818 const char* name
, uint32_t flags
) {
6819 for (const auto& cur
: field
) {
6820 ImplCycleCollectionTraverse(callback
, cur
, name
, flags
);
6824 template <typename T
, size_t N
>
6825 void ImplCycleCollectionUnlink(std::array
<RefPtr
<T
>, N
>& field
) {
6831 template <typename T
>
6832 void ImplCycleCollectionTraverse(
6833 nsCycleCollectionTraversalCallback
& callback
,
6834 const std::unordered_map
<GLenum
, RefPtr
<T
>>& field
, const char* name
,
6836 for (const auto& pair
: field
) {
6837 ImplCycleCollectionTraverse(callback
, pair
.second
, name
, flags
);
6841 template <typename T
>
6842 void ImplCycleCollectionUnlink(std::unordered_map
<GLenum
, RefPtr
<T
>>& field
) {
6848 void ImplCycleCollectionTraverse(
6849 nsCycleCollectionTraversalCallback
& callback
,
6850 const std::unordered_map
<GLenum
, WebGLFramebufferJS::Attachment
>& field
,
6851 const char* name
, uint32_t flags
) {
6852 for (const auto& pair
: field
) {
6853 const auto& attach
= pair
.second
;
6854 ImplCycleCollectionTraverse(callback
, attach
.rb
, name
, flags
);
6855 ImplCycleCollectionTraverse(callback
, attach
.tex
, name
, flags
);
6859 void ImplCycleCollectionUnlink(
6860 std::unordered_map
<GLenum
, WebGLFramebufferJS::Attachment
>& field
) {
6866 void ImplCycleCollectionTraverse(
6867 nsCycleCollectionTraversalCallback
& callback
,
6868 const std::unordered_map
<GLenum
, WebGLProgramJS::Attachment
>& field
,
6869 const char* name
, uint32_t flags
) {
6870 for (const auto& pair
: field
) {
6871 const auto& attach
= pair
.second
;
6872 ImplCycleCollectionTraverse(callback
, attach
.shader
, name
, flags
);
6876 void ImplCycleCollectionUnlink(
6877 std::unordered_map
<GLenum
, WebGLProgramJS::Attachment
>& field
) {
6883 void ImplCycleCollectionUnlink(
6884 const RefPtr
<ClientWebGLExtensionLoseContext
>& field
) {
6885 const_cast<RefPtr
<ClientWebGLExtensionLoseContext
>&>(field
) = nullptr;
6887 void ImplCycleCollectionUnlink(const RefPtr
<WebGLProgramJS
>& field
) {
6888 const_cast<RefPtr
<WebGLProgramJS
>&>(field
) = nullptr;
6890 void ImplCycleCollectionUnlink(const RefPtr
<WebGLShaderJS
>& field
) {
6891 const_cast<RefPtr
<WebGLShaderJS
>&>(field
) = nullptr;
6894 // ----------------------
6896 void ImplCycleCollectionTraverse(
6897 nsCycleCollectionTraversalCallback
& callback
,
6898 const std::shared_ptr
<webgl::NotLostData
>& field
, const char* name
,
6902 ImplCycleCollectionTraverse(callback
, field
->extensions
,
6903 "NotLostData.extensions", flags
);
6905 const auto& state
= field
->state
;
6907 ImplCycleCollectionTraverse(callback
, state
.mDefaultTfo
, "state.mDefaultTfo",
6909 ImplCycleCollectionTraverse(callback
, state
.mDefaultVao
, "state.mDefaultVao",
6912 ImplCycleCollectionTraverse(callback
, state
.mCurrentProgram
,
6913 "state.mCurrentProgram", flags
);
6915 ImplCycleCollectionTraverse(callback
, state
.mBoundBufferByTarget
,
6916 "state.mBoundBufferByTarget", flags
);
6917 ImplCycleCollectionTraverse(callback
, state
.mBoundUbos
, "state.mBoundUbos",
6919 ImplCycleCollectionTraverse(callback
, state
.mBoundDrawFb
,
6920 "state.mBoundDrawFb", flags
);
6921 ImplCycleCollectionTraverse(callback
, state
.mBoundReadFb
,
6922 "state.mBoundReadFb", flags
);
6923 ImplCycleCollectionTraverse(callback
, state
.mBoundRb
, "state.mBoundRb",
6925 ImplCycleCollectionTraverse(callback
, state
.mBoundTfo
, "state.mBoundTfo",
6927 ImplCycleCollectionTraverse(callback
, state
.mBoundVao
, "state.mBoundVao",
6929 ImplCycleCollectionTraverse(callback
, state
.mCurrentQueryByTarget
,
6930 "state.state.mCurrentQueryByTarget", flags
);
6932 for (const auto& texUnit
: state
.mTexUnits
) {
6933 ImplCycleCollectionTraverse(callback
, texUnit
.sampler
,
6934 "state.mTexUnits[].sampler", flags
);
6935 ImplCycleCollectionTraverse(callback
, texUnit
.texByTarget
,
6936 "state.mTexUnits[].texByTarget", flags
);
6940 void ImplCycleCollectionUnlink(std::shared_ptr
<webgl::NotLostData
>& field
) {
6942 const auto keepAlive
= field
;
6943 keepAlive
->extensions
= {};
6944 keepAlive
->state
= {};
6948 // -----------------------------------------------------
6950 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBufferJS
)
6951 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebufferJS
, mAttachments
)
6952 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgramJS
, mNextLink_Shaders
)
6953 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQueryJS
)
6954 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbufferJS
)
6955 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSamplerJS
)
6956 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShaderJS
)
6957 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSyncJS
)
6958 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTextureJS
)
6959 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLTransformFeedbackJS
, mAttribBuffers
,
6961 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocationJS
)
6962 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArrayJS
, mIndexBuffer
,
6967 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClientWebGLContext
)
6968 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
6969 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
6970 NS_INTERFACE_MAP_ENTRY(nsISupports
)
6971 NS_INTERFACE_MAP_END
6973 NS_IMPL_CYCLE_COLLECTING_ADDREF(ClientWebGLContext
)
6974 NS_IMPL_CYCLE_COLLECTING_RELEASE(ClientWebGLContext
)
6976 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(
6977 ClientWebGLContext
, mExtLoseContext
, mNotLost
,
6978 // Don't forget nsICanvasRenderingContextInternal:
6979 mCanvasElement
, mOffscreenCanvas
)
6981 // -----------------------------
6983 } // namespace mozilla