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"
12 #include "gfxCrashReporterUtils.h"
13 #include "HostWebGLContext.h"
14 #include "js/PropertyAndElement.h" // JS_DefineElement
15 #include "js/ScalarType.h" // js::Scalar::Type
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/ToJSValue.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/CompositorBridgeChild.h"
26 #include "mozilla/layers/ImageBridgeChild.h"
27 #include "mozilla/layers/OOPCanvasRenderer.h"
28 #include "mozilla/layers/TextureClientSharedSurface.h"
29 #include "mozilla/layers/WebRenderUserData.h"
30 #include "mozilla/layers/WebRenderCanvasRenderer.h"
31 #include "mozilla/ResultVariant.h"
32 #include "mozilla/ScopeExit.h"
33 #include "mozilla/StaticPrefs_webgl.h"
34 #include "nsContentUtils.h"
35 #include "nsDisplayList.h"
36 #include "TexUnpackBlob.h"
37 #include "WebGLMethodDispatcher.h"
38 #include "WebGLChild.h"
39 #include "WebGLValidateStrings.h"
44 std::string
SanitizeRenderer(const std::string
&);
49 webgl::NotLostData::NotLostData(ClientWebGLContext
& _context
)
50 : context(_context
) {}
52 webgl::NotLostData::~NotLostData() {
54 Unused
<< dom::WebGLChild::Send__delete__(outOfProcess
.get());
60 bool webgl::ObjectJS::ValidateForContext(
61 const ClientWebGLContext
& targetContext
, const char* const argName
) const {
62 if (!IsForContext(targetContext
)) {
63 targetContext
.EnqueueError(
64 LOCAL_GL_INVALID_OPERATION
,
65 "`%s` is from a different (or lost) WebGL context.", argName
);
71 void webgl::ObjectJS::WarnInvalidUse(const ClientWebGLContext
& targetContext
,
72 const char* const argName
) const {
73 if (!ValidateForContext(targetContext
, argName
)) return;
75 const auto errEnum
= ErrorOnDeleted();
76 targetContext
.EnqueueError(errEnum
, "Object `%s` is already deleted.",
80 static bool GetJSScalarFromGLType(GLenum type
,
81 js::Scalar::Type
* const out_scalarType
) {
84 *out_scalarType
= js::Scalar::Int8
;
87 case LOCAL_GL_UNSIGNED_BYTE
:
88 *out_scalarType
= js::Scalar::Uint8
;
92 *out_scalarType
= js::Scalar::Int16
;
95 case LOCAL_GL_HALF_FLOAT
:
96 case LOCAL_GL_HALF_FLOAT_OES
:
97 case LOCAL_GL_UNSIGNED_SHORT
:
98 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
99 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
100 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
101 *out_scalarType
= js::Scalar::Uint16
;
104 case LOCAL_GL_UNSIGNED_INT
:
105 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
:
106 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV
:
107 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
108 case LOCAL_GL_UNSIGNED_INT_24_8
:
109 *out_scalarType
= js::Scalar::Uint32
;
112 *out_scalarType
= js::Scalar::Int32
;
116 *out_scalarType
= js::Scalar::Float32
;
124 ClientWebGLContext::ClientWebGLContext(const bool webgl2
)
126 mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {}
128 ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); }
130 void ClientWebGLContext::JsWarning(const std::string
& utf8
) const {
131 nsIGlobalObject
* global
= nullptr;
132 if (mCanvasElement
) {
133 mozilla::dom::Document
* doc
= mCanvasElement
->OwnerDoc();
135 global
= doc
->GetScopeObject();
137 } else if (mOffscreenCanvas
) {
138 global
= mOffscreenCanvas
->GetOwnerGlobal();
142 if (!api
.Init(global
)) {
145 const auto& cx
= api
.cx();
146 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
149 void AutoJsWarning(const std::string
& utf8
) {
150 if (NS_IsMainThread()) {
151 const AutoJSContext cx
;
152 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
156 JSContext
* cx
= dom::GetCurrentWorkerThreadJSContext();
157 if (NS_WARN_IF(!cx
)) {
161 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
166 bool ClientWebGLContext::DispatchEvent(const nsAString
& eventName
) const {
167 const auto kCanBubble
= CanBubble::eYes
;
168 const auto kIsCancelable
= Cancelable::eYes
;
169 bool useDefaultHandler
= true;
171 if (mCanvasElement
) {
172 nsContentUtils::DispatchTrustedEvent(
173 mCanvasElement
->OwnerDoc(), static_cast<nsIContent
*>(mCanvasElement
),
174 eventName
, kCanBubble
, kIsCancelable
, &useDefaultHandler
);
175 } else if (mOffscreenCanvas
) {
176 // OffscreenCanvas case
177 RefPtr
<dom::Event
> event
=
178 new dom::Event(mOffscreenCanvas
, nullptr, nullptr);
179 event
->InitEvent(eventName
, kCanBubble
, kIsCancelable
);
180 event
->SetTrusted(true);
181 useDefaultHandler
= mOffscreenCanvas
->DispatchEvent(
182 *event
, dom::CallerType::System
, IgnoreErrors());
184 return useDefaultHandler
;
189 void ClientWebGLContext::EmulateLoseContext() const {
190 const FuncScope
funcScope(*this, "loseContext");
191 if (mLossStatus
!= webgl::LossStatus::Ready
) {
192 JsWarning("loseContext: Already lost.");
194 mNextError
= LOCAL_GL_INVALID_OPERATION
;
198 OnContextLoss(webgl::ContextLossReason::Manual
);
201 void ClientWebGLContext::OnContextLoss(
202 const webgl::ContextLossReason reason
) const {
203 JsWarning("WebGL context was lost.");
206 for (const auto& ext
: mNotLost
->extensions
) {
208 ext
->mContext
= nullptr; // Detach.
210 mNotLost
= {}; // Lost now!
211 mNextError
= LOCAL_GL_CONTEXT_LOST_WEBGL
;
215 case webgl::ContextLossReason::Guilty
:
216 mLossStatus
= webgl::LossStatus::LostForever
;
219 case webgl::ContextLossReason::None
:
220 mLossStatus
= webgl::LossStatus::Lost
;
223 case webgl::ContextLossReason::Manual
:
224 mLossStatus
= webgl::LossStatus::LostManually
;
228 const auto weak
= WeakPtr
<const ClientWebGLContext
>(this);
229 const auto fnRun
= [weak
]() {
230 const auto strong
= RefPtr
<const ClientWebGLContext
>(weak
);
232 strong
->Event_webglcontextlost();
234 already_AddRefed
<mozilla::CancelableRunnable
> runnable
=
235 NS_NewCancelableRunnableFunction("enqueue Event_webglcontextlost", fnRun
);
236 NS_DispatchToCurrentThread(std::move(runnable
));
239 void ClientWebGLContext::Event_webglcontextlost() const {
240 const bool useDefaultHandler
= DispatchEvent(u
"webglcontextlost"_ns
);
241 if (useDefaultHandler
) {
242 mLossStatus
= webgl::LossStatus::LostForever
;
245 if (mLossStatus
== webgl::LossStatus::Lost
) {
246 RestoreContext(webgl::LossStatus::Lost
);
250 void ClientWebGLContext::RestoreContext(
251 const webgl::LossStatus requiredStatus
) const {
252 if (requiredStatus
!= mLossStatus
) {
254 "restoreContext: Only valid iff context lost with loseContext().");
256 mNextError
= LOCAL_GL_INVALID_OPERATION
;
260 MOZ_RELEASE_ASSERT(mLossStatus
== webgl::LossStatus::Lost
||
261 mLossStatus
== webgl::LossStatus::LostManually
);
263 if (mAwaitingRestore
) return;
264 mAwaitingRestore
= true;
266 const auto weak
= WeakPtr
<const ClientWebGLContext
>(this);
267 const auto fnRun
= [weak
]() {
268 const auto strong
= RefPtr
<const ClientWebGLContext
>(weak
);
270 strong
->Event_webglcontextrestored();
272 already_AddRefed
<mozilla::CancelableRunnable
> runnable
=
273 NS_NewCancelableRunnableFunction("enqueue Event_webglcontextrestored",
275 NS_DispatchToCurrentThread(std::move(runnable
));
278 void ClientWebGLContext::Event_webglcontextrestored() const {
279 mAwaitingRestore
= false;
280 mLossStatus
= webgl::LossStatus::Ready
;
284 if (mCanvasElement
) {
285 requestSize
= {mCanvasElement
->Width(), mCanvasElement
->Height()};
286 } else if (mOffscreenCanvas
) {
287 requestSize
= {mOffscreenCanvas
->Width(), mOffscreenCanvas
->Height()};
289 MOZ_ASSERT_UNREACHABLE("no HTMLCanvasElement or OffscreenCanvas!");
293 if (!requestSize
.x
) {
296 if (!requestSize
.y
) {
300 const auto mutThis
= const_cast<ClientWebGLContext
*>(
301 this); // TODO: Make context loss non-mutable.
302 if (!mutThis
->CreateHostContext(requestSize
)) {
303 mLossStatus
= webgl::LossStatus::LostForever
;
309 (void)DispatchEvent(u
"webglcontextrestored"_ns
);
314 void ClientWebGLContext::ThrowEvent_WebGLContextCreationError(
315 const std::string
& text
) const {
317 msg
.AppendPrintf("Failed to create WebGL context: %s", text
.c_str());
318 JsWarning(msg
.BeginReading());
320 RefPtr
<dom::EventTarget
> target
= mCanvasElement
;
321 if (!target
&& mOffscreenCanvas
) {
322 target
= mOffscreenCanvas
;
323 } else if (!target
) {
327 const auto kEventName
= u
"webglcontextcreationerror"_ns
;
329 dom::WebGLContextEventInit eventInit
;
330 // eventInit.mCancelable = true; // The spec says this, but it's silly.
331 eventInit
.mStatusMessage
= NS_ConvertASCIItoUTF16(text
.c_str());
333 const RefPtr
<dom::WebGLContextEvent
> event
=
334 dom::WebGLContextEvent::Constructor(target
, kEventName
, eventInit
);
335 event
->SetTrusted(true);
337 target
->DispatchEvent(*event
);
342 // If we are running WebGL in this process then call the HostWebGLContext
343 // method directly. Otherwise, dispatch over IPC.
344 template <typename MethodType
, MethodType method
, typename
... Args
>
345 void ClientWebGLContext::Run(Args
&&... args
) const {
347 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
348 if (IsContextLost()) return;
350 const auto& inProcess
= notLost
->inProcess
;
352 return (inProcess
.get()->*method
)(std::forward
<Args
>(args
)...);
355 const auto& child
= notLost
->outOfProcess
;
357 const auto id
= IdByMethod
<MethodType
, method
>();
359 const auto info
= webgl::SerializationInfo(id
, args
...);
360 const auto maybeDest
= child
->AllocPendingCmdBytes(info
.requiredByteCount
,
361 info
.alignmentOverhead
);
363 JsWarning("Failed to allocate internal command buffer.");
364 OnContextLoss(webgl::ContextLossReason::None
);
367 const auto& destBytes
= *maybeDest
;
368 webgl::Serialize(destBytes
, id
, args
...);
371 // -------------------------------------------------------------------------
372 // Client-side helper methods. Dispatch to a Host method.
373 // -------------------------------------------------------------------------
375 #define RPROC(_METHOD) \
376 decltype(&HostWebGLContext::_METHOD), &HostWebGLContext::_METHOD
378 // ------------------------- Composition, etc -------------------------
380 void ClientWebGLContext::OnBeforePaintTransaction() { Present(nullptr); }
382 void ClientWebGLContext::EndComposition() {
383 // Mark ourselves as no longer invalidated.
389 layers::TextureType
ClientWebGLContext::GetTexTypeForSwapChain() const {
390 const RefPtr
<layers::ImageBridgeChild
> imageBridge
=
391 layers::ImageBridgeChild::GetSingleton();
392 return layers::TexTypeForWebgl(imageBridge
);
395 void ClientWebGLContext::Present(WebGLFramebufferJS
* const xrFb
,
397 const webgl::SwapChainOptions
& options
) {
398 const auto texType
= GetTexTypeForSwapChain();
399 Present(xrFb
, texType
, webvr
, options
);
402 // Fill in remote texture ids to SwapChainOptions if async present is enabled.
403 webgl::SwapChainOptions
ClientWebGLContext::PrepareAsyncSwapChainOptions(
404 WebGLFramebufferJS
* fb
, bool webvr
,
405 const webgl::SwapChainOptions
& options
) {
406 // Currently remote texture ids should only be set internally.
407 MOZ_ASSERT(!options
.remoteTextureOwnerId
.IsValid() &&
408 !options
.remoteTextureId
.IsValid());
409 auto& ownerId
= fb
? fb
->mRemoteTextureOwnerId
: mRemoteTextureOwnerId
;
410 auto& textureId
= fb
? fb
->mLastRemoteTextureId
: mLastRemoteTextureId
;
411 // Async present only works when out-of-process. It is not supported in WebVR.
412 // Allow it if it is either forced or if the pref is set.
413 if (!IsContextLost() && !mNotLost
->inProcess
&& !webvr
&&
414 (options
.forceAsyncPresent
||
415 StaticPrefs::webgl_out_of_process_async_present())) {
417 ownerId
= Some(layers::RemoteTextureOwnerId::GetNext());
419 textureId
= Some(layers::RemoteTextureId::GetNext());
420 webgl::SwapChainOptions asyncOptions
= options
;
421 asyncOptions
.remoteTextureOwnerId
= *ownerId
;
422 asyncOptions
.remoteTextureId
= *textureId
;
425 // Clear the current remote texture id so that we disable async.
426 textureId
= Nothing();
430 void ClientWebGLContext::Present(WebGLFramebufferJS
* const xrFb
,
431 const layers::TextureType type
,
433 const webgl::SwapChainOptions
& options
) {
434 if (!mIsCanvasDirty
&& !xrFb
) return;
436 mIsCanvasDirty
= false;
439 webgl::SwapChainOptions asyncOptions
=
440 PrepareAsyncSwapChainOptions(xrFb
, webvr
, options
);
441 Run
<RPROC(Present
)>(xrFb
? xrFb
->mId
: 0, type
, webvr
, asyncOptions
);
444 void ClientWebGLContext::CopyToSwapChain(
445 WebGLFramebufferJS
* const fb
, const webgl::SwapChainOptions
& options
) {
447 const auto texType
= GetTexTypeForSwapChain();
448 webgl::SwapChainOptions asyncOptions
=
449 PrepareAsyncSwapChainOptions(fb
, false, options
);
450 Run
<RPROC(CopyToSwapChain
)>(fb
? fb
->mId
: 0, texType
, asyncOptions
);
453 void ClientWebGLContext::EndOfFrame() {
455 Run
<RPROC(EndOfFrame
)>();
458 Maybe
<layers::SurfaceDescriptor
> ClientWebGLContext::GetFrontBuffer(
459 WebGLFramebufferJS
* const fb
, bool vr
) {
460 const FuncScope
funcScope(*this, "<GetFrontBuffer>");
461 if (IsContextLost()) return {};
463 const auto& inProcess
= mNotLost
->inProcess
;
465 return inProcess
->GetFrontBuffer(fb
? fb
->mId
: 0, vr
);
468 const auto& child
= mNotLost
->outOfProcess
;
469 child
->FlushPendingCmds();
471 Maybe
<layers::SurfaceDescriptor
> ret
;
473 // If valid remote texture data was set for async present, then use it.
474 const auto& ownerId
= fb
? fb
->mRemoteTextureOwnerId
: mRemoteTextureOwnerId
;
475 const auto& textureId
= fb
? fb
->mLastRemoteTextureId
: mLastRemoteTextureId
;
476 if (ownerId
&& textureId
) {
477 if (StaticPrefs::webgl_out_of_process_async_present_force_sync()) {
478 // Request the front buffer from IPDL to cause a sync, even though we
479 // will continue to use the remote texture descriptor after.
480 (void)child
->SendGetFrontBuffer(fb
? fb
->mId
: 0, vr
, &ret
);
482 return Some(layers::SurfaceDescriptorRemoteTexture(*textureId
, *ownerId
));
485 if (!child
->SendGetFrontBuffer(fb
? fb
->mId
: 0, vr
, &ret
)) return {};
490 Maybe
<layers::SurfaceDescriptor
> ClientWebGLContext::PresentFrontBuffer(
491 WebGLFramebufferJS
* const fb
, const layers::TextureType type
, bool webvr
) {
492 Present(fb
, type
, webvr
);
493 return GetFrontBuffer(fb
, webvr
);
496 void ClientWebGLContext::ClearVRSwapChain() { Run
<RPROC(ClearVRSwapChain
)>(); }
500 bool ClientWebGLContext::UpdateWebRenderCanvasData(
501 nsDisplayListBuilder
* aBuilder
, WebRenderCanvasData
* aCanvasData
) {
502 CanvasRenderer
* renderer
= aCanvasData
->GetCanvasRenderer();
504 if (!IsContextLost() && !mResetLayer
&& renderer
) {
508 const auto& size
= DrawingBufferSize();
510 if (!IsContextLost() && !renderer
&& mNotLost
->mCanvasRenderer
&&
511 mNotLost
->mCanvasRenderer
->GetSize() == gfx::IntSize(size
.x
, size
.y
) &&
512 aCanvasData
->SetCanvasRenderer(mNotLost
->mCanvasRenderer
)) {
513 mNotLost
->mCanvasRenderer
->SetDirty();
518 renderer
= aCanvasData
->CreateCanvasRenderer();
519 if (!InitializeCanvasRenderer(aBuilder
, renderer
)) {
520 // Clear CanvasRenderer of WebRenderCanvasData
521 aCanvasData
->ClearCanvasRenderer();
525 mNotLost
->mCanvasRenderer
= renderer
;
527 MOZ_ASSERT(renderer
);
533 bool ClientWebGLContext::InitializeCanvasRenderer(
534 nsDisplayListBuilder
* aBuilder
, CanvasRenderer
* aRenderer
) {
535 const FuncScope
funcScope(*this, "<InitializeCanvasRenderer>");
536 if (IsContextLost()) return false;
538 layers::CanvasRendererData data
;
539 data
.mContext
= this;
540 data
.mOriginPos
= gl::OriginPos::BottomLeft
;
542 const auto& options
= *mInitialOptions
;
543 const auto& size
= DrawingBufferSize();
545 if (IsContextLost()) return false;
547 data
.mIsOpaque
= !options
.alpha
;
548 data
.mIsAlphaPremult
= !options
.alpha
|| options
.premultipliedAlpha
;
549 data
.mSize
= {size
.x
, size
.y
};
551 if (aBuilder
->IsPaintingToWindow() && mCanvasElement
) {
552 data
.mDoPaintCallbacks
= true;
555 aRenderer
->Initialize(data
);
556 aRenderer
->SetDirty();
560 void ClientWebGLContext::UpdateCanvasParameters() {
561 if (!mOffscreenCanvas
) {
565 const auto& options
= *mInitialOptions
;
566 const auto& size
= DrawingBufferSize();
568 mozilla::dom::OffscreenCanvasDisplayData data
;
569 data
.mOriginPos
= gl::OriginPos::BottomLeft
;
570 data
.mIsOpaque
= !options
.alpha
;
571 data
.mIsAlphaPremult
= !options
.alpha
|| options
.premultipliedAlpha
;
572 data
.mSize
= {size
.x
, size
.y
};
573 data
.mDoPaintCallbacks
= false;
575 mOffscreenCanvas
->UpdateDisplayData(data
);
578 layers::LayersBackend
ClientWebGLContext::GetCompositorBackendType() const {
579 if (mCanvasElement
) {
580 return mCanvasElement
->GetCompositorBackendType();
581 } else if (mOffscreenCanvas
) {
582 return mOffscreenCanvas
->GetCompositorBackendType();
585 return layers::LayersBackend::LAYERS_NONE
;
588 mozilla::dom::Document
* ClientWebGLContext::GetOwnerDoc() const {
589 MOZ_ASSERT(mCanvasElement
);
590 if (!mCanvasElement
) {
593 return mCanvasElement
->OwnerDoc();
596 void ClientWebGLContext::Commit() {
597 if (mOffscreenCanvas
) {
598 mOffscreenCanvas
->CommitFrameToCompositor();
602 void ClientWebGLContext::GetCanvas(
603 dom::Nullable
<dom::OwningHTMLCanvasElementOrOffscreenCanvas
>& retval
) {
604 if (mCanvasElement
) {
605 MOZ_RELEASE_ASSERT(!mOffscreenCanvas
, "GFX: Canvas is offscreen.");
607 if (mCanvasElement
->IsInNativeAnonymousSubtree()) {
610 retval
.SetValue().SetAsHTMLCanvasElement() = mCanvasElement
;
612 } else if (mOffscreenCanvas
) {
613 retval
.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas
;
619 void ClientWebGLContext::GetContextAttributes(
620 dom::Nullable
<dom::WebGLContextAttributes
>& retval
) {
622 const FuncScope
funcScope(*this, "getContextAttributes");
623 if (IsContextLost()) return;
625 dom::WebGLContextAttributes
& result
= retval
.SetValue();
627 const auto& options
= mNotLost
->info
.options
;
629 result
.mAlpha
.Construct(options
.alpha
);
630 result
.mDepth
= options
.depth
;
631 result
.mStencil
= options
.stencil
;
632 result
.mAntialias
.Construct(options
.antialias
);
633 result
.mPremultipliedAlpha
= options
.premultipliedAlpha
;
634 result
.mPreserveDrawingBuffer
= options
.preserveDrawingBuffer
;
635 result
.mFailIfMajorPerformanceCaveat
= options
.failIfMajorPerformanceCaveat
;
636 result
.mPowerPreference
= options
.powerPreference
;
639 // -----------------------
642 ClientWebGLContext::SetDimensions(const int32_t signedWidth
,
643 const int32_t signedHeight
) {
644 const FuncScope
funcScope(*this, "<SetDimensions>");
645 MOZ_ASSERT(mInitialOptions
);
647 if (mLossStatus
!= webgl::LossStatus::Ready
) {
648 // Attempted resize of a lost context.
652 uvec2 size
= {static_cast<uint32_t>(signedWidth
),
653 static_cast<uint32_t>(signedHeight
)};
660 const auto prevRequestedSize
= mRequestedSize
;
661 mRequestedSize
= size
;
663 mResetLayer
= true; // Always treat this as resize.
666 auto& state
= State();
668 auto curSize
= prevRequestedSize
;
669 if (state
.mDrawingBufferSize
) {
670 curSize
= *state
.mDrawingBufferSize
;
672 if (size
== curSize
) return NS_OK
; // MUST skip no-op resize
674 state
.mDrawingBufferSize
= Nothing();
675 Run
<RPROC(Resize
)>(size
);
677 UpdateCanvasParameters();
683 // Context (re-)creation
685 if (!CreateHostContext(size
)) {
686 return NS_ERROR_FAILURE
;
691 void ClientWebGLContext::ResetBitmap() {
692 const auto size
= DrawingBufferSize();
693 Run
<RPROC(Resize
)>(size
); // No-change resize still clears/resets everything.
696 static bool IsWebglOutOfProcessEnabled() {
697 if (StaticPrefs::webgl_out_of_process_force()) {
700 if (!gfx::gfxVars::AllowWebglOop()) {
703 if (!NS_IsMainThread()) {
704 return StaticPrefs::webgl_out_of_process_worker();
706 return StaticPrefs::webgl_out_of_process();
709 static inline bool StartsWith(const std::string
& haystack
,
710 const std::string
& needle
) {
711 return haystack
.find(needle
) == 0;
714 bool ClientWebGLContext::CreateHostContext(const uvec2
& requestedSize
) {
715 const auto pNotLost
= std::make_shared
<webgl::NotLostData
>(*this);
716 auto& notLost
= *pNotLost
;
718 auto res
= [&]() -> Result
<Ok
, std::string
> {
719 auto options
= *mInitialOptions
;
720 if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) {
721 options
.failIfMajorPerformanceCaveat
= false;
724 if (options
.failIfMajorPerformanceCaveat
) {
725 const auto backend
= GetCompositorBackendType();
726 bool isCompositorSlow
= false;
727 isCompositorSlow
|= (backend
== layers::LayersBackend::LAYERS_WR
&&
728 gfx::gfxVars::UseSoftwareWebRender());
730 if (isCompositorSlow
) {
732 "failIfMajorPerformanceCaveat: Compositor is not"
733 " hardware-accelerated.");
737 const bool resistFingerprinting
= ShouldResistFingerprinting();
738 const auto principalKey
= GetPrincipalHashValue();
739 const auto initDesc
= webgl::InitContextDesc
{
740 mIsWebGL2
, resistFingerprinting
, requestedSize
, options
, principalKey
};
744 auto useOop
= IsWebglOutOfProcessEnabled();
745 if (XRE_IsParentProcess()) {
751 HostWebGLContext::Create({this, nullptr}, initDesc
, ¬Lost
.info
);
757 ScopedGfxFeatureReporter
reporter("IpcWebGL");
759 auto* const cm
= gfx::CanvasManagerChild::Get();
760 if (NS_WARN_IF(!cm
)) {
761 return Err("!CanvasManagerChild::Get()");
764 RefPtr
<dom::WebGLChild
> outOfProcess
= new dom::WebGLChild(*this);
766 static_cast<dom::WebGLChild
*>(cm
->SendPWebGLConstructor(outOfProcess
));
768 return Err("SendPWebGLConstructor failed");
771 if (!outOfProcess
->SendInitialize(initDesc
, ¬Lost
.info
)) {
772 return Err("WebGL actor Initialize failed");
775 notLost
.outOfProcess
= outOfProcess
;
776 reporter
.SetSuccessful();
780 auto str
= res
.unwrapErr();
781 if (StartsWith(str
, "failIfMajorPerformanceCaveat")) {
783 " (about:config override available:"
784 " webgl.disable-fail-if-major-performance-caveat)";
786 notLost
.info
.error
= str
;
788 if (!notLost
.info
.error
.empty()) {
789 ThrowEvent_WebGLContextCreationError(notLost
.info
.error
);
793 UpdateCanvasParameters();
797 const auto& limits
= Limits();
798 auto& state
= State();
799 state
.mDefaultTfo
= new WebGLTransformFeedbackJS(*this);
800 state
.mDefaultVao
= new WebGLVertexArrayJS(*this);
802 state
.mBoundTfo
= state
.mDefaultTfo
;
803 state
.mBoundVao
= state
.mDefaultVao
;
805 (void)state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
];
807 state
.mTexUnits
.resize(limits
.maxTexUnits
);
808 state
.mBoundUbos
.resize(limits
.maxUniformBufferBindings
);
811 webgl::TypedQuad initVal
;
812 const float fData
[4] = {0, 0, 0, 1};
813 memcpy(initVal
.data
.data(), fData
, initVal
.data
.size());
814 state
.mGenericVertexAttribs
.resize(limits
.maxVertexAttribs
, initVal
);
817 const auto& size
= DrawingBufferSize();
818 state
.mViewport
= {0, 0, static_cast<int32_t>(size
.x
),
819 static_cast<int32_t>(size
.y
)};
820 state
.mScissor
= state
.mViewport
;
823 // Insert keys to enable slots:
824 (void)state
.mBoundBufferByTarget
[LOCAL_GL_COPY_READ_BUFFER
];
825 (void)state
.mBoundBufferByTarget
[LOCAL_GL_COPY_WRITE_BUFFER
];
826 (void)state
.mBoundBufferByTarget
[LOCAL_GL_PIXEL_PACK_BUFFER
];
827 (void)state
.mBoundBufferByTarget
[LOCAL_GL_PIXEL_UNPACK_BUFFER
];
828 (void)state
.mBoundBufferByTarget
[LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
];
829 (void)state
.mBoundBufferByTarget
[LOCAL_GL_UNIFORM_BUFFER
];
831 (void)state
.mCurrentQueryByTarget
[LOCAL_GL_ANY_SAMPLES_PASSED
];
832 //(void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE];
833 //// Same slot as ANY_SAMPLES_PASSED.
835 .mCurrentQueryByTarget
[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
];
843 uvec2
ClientWebGLContext::DrawingBufferSize() {
844 if (IsContextLost()) return {};
846 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
847 auto& state
= State();
848 auto& size
= state
.mDrawingBufferSize
;
851 const auto& inProcess
= mNotLost
->inProcess
;
853 size
= Some(inProcess
->DrawingBufferSize());
855 const auto& child
= mNotLost
->outOfProcess
;
856 child
->FlushPendingCmds();
858 if (!child
->SendDrawingBufferSize(&actual
)) return {};
866 void ClientWebGLContext::OnMemoryPressure() {
867 if (IsContextLost()) return;
869 const auto& inProcess
= mNotLost
->inProcess
;
871 return inProcess
->OnMemoryPressure();
873 const auto& child
= mNotLost
->outOfProcess
;
874 (void)child
->SendOnMemoryPressure();
878 ClientWebGLContext::SetContextOptions(JSContext
* cx
,
879 JS::Handle
<JS::Value
> options
,
880 ErrorResult
& aRvForDictionaryInit
) {
881 if (mInitialOptions
&& options
.isNullOrUndefined()) return NS_OK
;
883 dom::WebGLContextAttributes attributes
;
884 if (!attributes
.Init(cx
, options
)) {
885 aRvForDictionaryInit
.Throw(NS_ERROR_UNEXPECTED
);
886 return NS_ERROR_UNEXPECTED
;
889 WebGLContextOptions newOpts
;
891 newOpts
.stencil
= attributes
.mStencil
;
892 newOpts
.depth
= attributes
.mDepth
;
893 newOpts
.premultipliedAlpha
= attributes
.mPremultipliedAlpha
;
894 newOpts
.preserveDrawingBuffer
= attributes
.mPreserveDrawingBuffer
;
895 newOpts
.failIfMajorPerformanceCaveat
=
896 attributes
.mFailIfMajorPerformanceCaveat
;
897 newOpts
.xrCompatible
= attributes
.mXrCompatible
;
898 newOpts
.powerPreference
= attributes
.mPowerPreference
;
899 newOpts
.enableDebugRendererInfo
=
900 StaticPrefs::webgl_enable_debug_renderer_info();
901 MOZ_ASSERT(mCanvasElement
|| mOffscreenCanvas
);
902 newOpts
.shouldResistFingerprinting
= ShouldResistFingerprinting();
904 if (attributes
.mAlpha
.WasPassed()) {
905 newOpts
.alpha
= attributes
.mAlpha
.Value();
907 if (attributes
.mAntialias
.WasPassed()) {
908 newOpts
.antialias
= attributes
.mAntialias
.Value();
910 newOpts
.ignoreColorSpace
= true;
911 if (attributes
.mColorSpace
.WasPassed()) {
912 newOpts
.ignoreColorSpace
= false;
913 newOpts
.colorSpace
= attributes
.mColorSpace
.Value();
916 // Don't do antialiasing if we've disabled MSAA.
917 if (!StaticPrefs::webgl_msaa_samples()) {
918 newOpts
.antialias
= false;
923 if (mInitialOptions
&& *mInitialOptions
!= newOpts
) {
924 // Err if the options asked for aren't the same as what they were
926 return NS_ERROR_FAILURE
;
929 mXRCompatible
= attributes
.mXrCompatible
;
931 mInitialOptions
.emplace(newOpts
);
935 void ClientWebGLContext::DidRefresh() { Run
<RPROC(DidRefresh
)>(); }
937 already_AddRefed
<gfx::SourceSurface
> ClientWebGLContext::GetSurfaceSnapshot(
938 gfxAlphaType
* const out_alphaType
) {
939 const FuncScope
funcScope(*this, "<GetSurfaceSnapshot>");
940 if (IsContextLost()) return nullptr;
942 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
944 auto ret
= BackBufferSnapshot();
945 if (!ret
) return nullptr;
949 const auto& options
= mNotLost
->info
.options
;
951 auto srcAlphaType
= gfxAlphaType::Opaque
;
953 if (options
.premultipliedAlpha
) {
954 srcAlphaType
= gfxAlphaType::Premult
;
956 srcAlphaType
= gfxAlphaType::NonPremult
;
961 *out_alphaType
= srcAlphaType
;
963 // Expects Opaque or Premult
964 if (srcAlphaType
== gfxAlphaType::NonPremult
) {
965 const gfx::DataSourceSurface::ScopedMap
map(
966 ret
, gfx::DataSourceSurface::READ_WRITE
);
967 MOZ_RELEASE_ASSERT(map
.IsMapped(), "Failed to map snapshot surface!");
969 const auto& size
= ret
->GetSize();
970 const auto format
= ret
->GetFormat();
972 gfx::PremultiplyData(map
.GetData(), map
.GetStride(), format
,
973 map
.GetData(), map
.GetStride(), format
, size
);
974 MOZ_RELEASE_ASSERT(rv
, "PremultiplyData failed!");
981 RefPtr
<gfx::SourceSurface
> ClientWebGLContext::GetFrontBufferSnapshot(
982 const bool requireAlphaPremult
) {
983 const FuncScope
funcScope(*this, "<GetSurfaceSnapshot>");
984 if (IsContextLost()) return nullptr;
986 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
988 const auto& options
= mNotLost
->info
.options
;
990 const auto surfFormat
= options
.alpha
? gfx::SurfaceFormat::B8G8R8A8
991 : gfx::SurfaceFormat::B8G8R8X8
;
993 const auto fnNewSurf
= [&](const uvec2 size
) {
994 const auto stride
= size
.x
* 4;
995 return RefPtr
<gfx::DataSourceSurface
>(
996 gfx::Factory::CreateDataSourceSurfaceWithStride({size
.x
, size
.y
},
1001 const auto& inProcess
= mNotLost
->inProcess
;
1003 const auto maybeSize
= inProcess
->FrontBufferSnapshotInto({});
1004 if (!maybeSize
) return nullptr;
1005 const auto& surfSize
= *maybeSize
;
1006 const auto stride
= surfSize
.x
* 4;
1007 const auto byteSize
= stride
* surfSize
.y
;
1008 const auto surf
= fnNewSurf(surfSize
);
1009 if (!surf
) return nullptr;
1011 const gfx::DataSourceSurface::ScopedMap
map(
1012 surf
, gfx::DataSourceSurface::READ_WRITE
);
1013 if (!map
.IsMapped()) {
1017 MOZ_RELEASE_ASSERT(map
.GetStride() == static_cast<int64_t>(stride
));
1018 auto range
= Range
<uint8_t>{map
.GetData(), byteSize
};
1019 if (!inProcess
->FrontBufferSnapshotInto(Some(range
))) {
1020 gfxCriticalNote
<< "ClientWebGLContext::GetFrontBufferSnapshot: "
1021 "FrontBufferSnapshotInto(some) failed after "
1022 "FrontBufferSnapshotInto(none)";
1025 if (requireAlphaPremult
&& options
.alpha
&& !options
.premultipliedAlpha
) {
1026 bool rv
= gfx::PremultiplyData(
1027 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::R8G8B8A8
,
1028 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1030 MOZ_RELEASE_ASSERT(rv
, "PremultiplyData failed!");
1032 bool rv
= gfx::SwizzleData(
1033 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::R8G8B8A8
,
1034 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1036 MOZ_RELEASE_ASSERT(rv
, "SwizzleData failed!");
1041 const auto& child
= mNotLost
->outOfProcess
;
1042 child
->FlushPendingCmds();
1043 webgl::FrontBufferSnapshotIpc res
;
1044 if (!child
->SendGetFrontBufferSnapshot(&res
)) {
1047 if (!res
.shmem
) return nullptr;
1049 const auto& surfSize
= res
.surfSize
;
1050 const webgl::RaiiShmem shmem
{child
, res
.shmem
.ref()};
1051 if (!shmem
) return nullptr;
1052 const auto& shmemBytes
= shmem
.ByteRange();
1053 if (!surfSize
.x
) return nullptr; // Zero means failure.
1055 const auto stride
= surfSize
.x
* 4;
1056 const auto byteSize
= stride
* surfSize
.y
;
1058 const auto surf
= fnNewSurf(surfSize
);
1059 if (!surf
) return nullptr;
1062 const gfx::DataSourceSurface::ScopedMap
map(
1063 surf
, gfx::DataSourceSurface::READ_WRITE
);
1064 if (!map
.IsMapped()) {
1068 MOZ_RELEASE_ASSERT(shmemBytes
.length() == byteSize
);
1069 if (requireAlphaPremult
&& options
.alpha
&& !options
.premultipliedAlpha
) {
1070 bool rv
= gfx::PremultiplyData(
1071 shmemBytes
.begin().get(), stride
, gfx::SurfaceFormat::R8G8B8A8
,
1072 map
.GetData(), map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1074 MOZ_RELEASE_ASSERT(rv
, "PremultiplyData failed!");
1076 bool rv
= gfx::SwizzleData(shmemBytes
.begin().get(), stride
,
1077 gfx::SurfaceFormat::R8G8B8A8
, map
.GetData(),
1078 map
.GetStride(), gfx::SurfaceFormat::B8G8R8A8
,
1080 MOZ_RELEASE_ASSERT(rv
, "SwizzleData failed!");
1086 RefPtr
<gfx::DataSourceSurface
> ClientWebGLContext::BackBufferSnapshot() {
1087 if (IsContextLost()) return nullptr;
1088 const auto notLost
=
1089 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1091 const auto& options
= mNotLost
->info
.options
;
1092 const auto& state
= State();
1094 const auto drawFbWas
= state
.mBoundDrawFb
;
1095 const auto readFbWas
= state
.mBoundReadFb
;
1097 Find(state
.mBoundBufferByTarget
, LOCAL_GL_PIXEL_PACK_BUFFER
);
1099 const auto size
= DrawingBufferSize();
1103 BindFramebuffer(LOCAL_GL_FRAMEBUFFER
, nullptr);
1105 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, nullptr);
1108 auto reset
= MakeScopeExit([&] {
1109 if (drawFbWas
== readFbWas
) {
1110 BindFramebuffer(LOCAL_GL_FRAMEBUFFER
, drawFbWas
);
1112 BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, drawFbWas
);
1113 BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, readFbWas
);
1116 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, pboWas
);
1120 const auto surfFormat
= options
.alpha
? gfx::SurfaceFormat::B8G8R8A8
1121 : gfx::SurfaceFormat::B8G8R8X8
;
1122 const auto stride
= size
.x
* 4;
1123 RefPtr
<gfx::DataSourceSurface
> surf
=
1124 gfx::Factory::CreateDataSourceSurfaceWithStride(
1125 {size
.x
, size
.y
}, surfFormat
, stride
, /*zero=*/true);
1126 if (NS_WARN_IF(!surf
)) {
1127 // Was this an OOM or alloc-limit? (500MB is our default resource size
1129 surf
= gfx::Factory::CreateDataSourceSurfaceWithStride({1, 1}, surfFormat
,
1132 // Still failed for a 1x1 size.
1133 gfxCriticalError() << "CreateDataSourceSurfaceWithStride(surfFormat="
1134 << surfFormat
<< ") failed.";
1140 const gfx::DataSourceSurface::ScopedMap
map(
1141 surf
, gfx::DataSourceSurface::READ_WRITE
);
1142 if (!map
.IsMapped()) {
1146 MOZ_ASSERT(static_cast<uint32_t>(map
.GetStride()) == stride
);
1148 const auto desc
= webgl::ReadPixelsDesc
{{0, 0}, size
};
1149 const auto range
= Range
<uint8_t>(map
.GetData(), stride
* size
.y
);
1150 if (!DoReadPixels(desc
, range
)) return nullptr;
1152 const auto begin
= range
.begin().get();
1154 std::vector
<uint8_t> temp
;
1155 temp
.resize(stride
);
1156 for (const auto i
: IntegerRange(size
.y
/ 2)) {
1157 const auto top
= begin
+ stride
* i
;
1158 const auto bottom
= begin
+ stride
* (size
.y
- 1 - i
);
1159 memcpy(temp
.data(), top
, stride
);
1160 memcpy(top
, bottom
, stride
);
1161 gfxUtils::ConvertBGRAtoRGBA(top
, stride
);
1163 memcpy(bottom
, temp
.data(), stride
);
1164 gfxUtils::ConvertBGRAtoRGBA(bottom
, stride
);
1168 const auto middle
= begin
+ stride
* (size
.y
/ 2);
1169 gfxUtils::ConvertBGRAtoRGBA(middle
, stride
);
1176 UniquePtr
<uint8_t[]> ClientWebGLContext::GetImageBuffer(int32_t* out_format
) {
1179 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1181 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
1182 if (!snapshot
) return nullptr;
1184 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1186 const auto& premultAlpha
= mNotLost
->info
.options
.premultipliedAlpha
;
1187 return gfxUtils::GetImageBuffer(dataSurface
, premultAlpha
, out_format
);
1191 ClientWebGLContext::GetInputStream(const char* mimeType
,
1192 const nsAString
& encoderOptions
,
1193 nsIInputStream
** out_stream
) {
1194 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1196 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
1197 if (!snapshot
) return NS_ERROR_FAILURE
;
1199 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1200 const auto& premultAlpha
= mNotLost
->info
.options
.premultipliedAlpha
;
1201 return gfxUtils::GetInputStream(dataSurface
, premultAlpha
, mimeType
,
1202 encoderOptions
, out_stream
);
1205 // ------------------------- Client WebGL Objects -------------------------
1206 // ------------------------- Create/Destroy/Is -------------------------
1208 template <typename T
>
1209 static already_AddRefed
<T
> AsAddRefed(T
* ptr
) {
1214 template <typename T
>
1215 static RefPtr
<T
> AsRefPtr(T
* ptr
) {
1219 already_AddRefed
<WebGLBufferJS
> ClientWebGLContext::CreateBuffer() const {
1220 const FuncScope
funcScope(*this, "createBuffer");
1221 if (IsContextLost()) return nullptr;
1223 auto ret
= AsRefPtr(new WebGLBufferJS(*this));
1224 Run
<RPROC(CreateBuffer
)>(ret
->mId
);
1225 return ret
.forget();
1228 already_AddRefed
<WebGLFramebufferJS
> ClientWebGLContext::CreateFramebuffer()
1230 const FuncScope
funcScope(*this, "createFramebuffer");
1231 if (IsContextLost()) return nullptr;
1233 auto ret
= AsRefPtr(new WebGLFramebufferJS(*this));
1234 Run
<RPROC(CreateFramebuffer
)>(ret
->mId
);
1235 return ret
.forget();
1238 already_AddRefed
<WebGLFramebufferJS
>
1239 ClientWebGLContext::CreateOpaqueFramebuffer(
1240 const webgl::OpaqueFramebufferOptions
& options
) const {
1241 const FuncScope
funcScope(*this, "createOpaqueFramebuffer");
1242 if (IsContextLost()) return nullptr;
1244 auto ret
= AsRefPtr(new WebGLFramebufferJS(*this, true));
1246 const auto& inProcess
= mNotLost
->inProcess
;
1248 if (!inProcess
->CreateOpaqueFramebuffer(ret
->mId
, options
)) {
1251 return ret
.forget();
1253 const auto& child
= mNotLost
->outOfProcess
;
1254 child
->FlushPendingCmds();
1256 if (!child
->SendCreateOpaqueFramebuffer(ret
->mId
, options
, &ok
))
1258 if (!ok
) return nullptr;
1259 return ret
.forget();
1262 already_AddRefed
<WebGLProgramJS
> ClientWebGLContext::CreateProgram() const {
1263 const FuncScope
funcScope(*this, "createProgram");
1264 if (IsContextLost()) return nullptr;
1266 auto ret
= AsRefPtr(new WebGLProgramJS(*this));
1267 Run
<RPROC(CreateProgram
)>(ret
->mId
);
1268 return ret
.forget();
1271 already_AddRefed
<WebGLQueryJS
> ClientWebGLContext::CreateQuery() const {
1272 const FuncScope
funcScope(*this, "createQuery");
1273 if (IsContextLost()) return nullptr;
1275 auto ret
= AsRefPtr(new WebGLQueryJS(*this));
1276 Run
<RPROC(CreateQuery
)>(ret
->mId
);
1277 return ret
.forget();
1280 already_AddRefed
<WebGLRenderbufferJS
> ClientWebGLContext::CreateRenderbuffer()
1282 const FuncScope
funcScope(*this, "createRenderbuffer");
1283 if (IsContextLost()) return nullptr;
1285 auto ret
= AsRefPtr(new WebGLRenderbufferJS(*this));
1286 Run
<RPROC(CreateRenderbuffer
)>(ret
->mId
);
1287 return ret
.forget();
1290 already_AddRefed
<WebGLSamplerJS
> ClientWebGLContext::CreateSampler() const {
1291 const FuncScope
funcScope(*this, "createSampler");
1292 if (IsContextLost()) return nullptr;
1294 auto ret
= AsRefPtr(new WebGLSamplerJS(*this));
1295 Run
<RPROC(CreateSampler
)>(ret
->mId
);
1296 return ret
.forget();
1299 already_AddRefed
<WebGLShaderJS
> ClientWebGLContext::CreateShader(
1300 const GLenum type
) const {
1301 const FuncScope
funcScope(*this, "createShader");
1302 if (IsContextLost()) return nullptr;
1305 case LOCAL_GL_VERTEX_SHADER
:
1306 case LOCAL_GL_FRAGMENT_SHADER
:
1309 EnqueueError_ArgEnum("type", type
);
1313 auto ret
= AsRefPtr(new WebGLShaderJS(*this, type
));
1314 Run
<RPROC(CreateShader
)>(ret
->mId
, ret
->mType
);
1315 return ret
.forget();
1318 already_AddRefed
<WebGLSyncJS
> ClientWebGLContext::FenceSync(
1319 const GLenum condition
, const GLbitfield flags
) const {
1320 const FuncScope
funcScope(*this, "fenceSync");
1321 if (IsContextLost()) return nullptr;
1323 if (condition
!= LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE
) {
1324 EnqueueError_ArgEnum("condition", condition
);
1329 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`flags` must be 0.");
1333 auto ret
= AsRefPtr(new WebGLSyncJS(*this));
1334 Run
<RPROC(CreateSync
)>(ret
->mId
);
1336 auto& availRunnable
= EnsureAvailabilityRunnable();
1337 availRunnable
.mSyncs
.push_back(ret
.get());
1338 ret
->mCanBeAvailable
= false;
1340 return ret
.forget();
1343 already_AddRefed
<WebGLTextureJS
> ClientWebGLContext::CreateTexture() const {
1344 const FuncScope
funcScope(*this, "createTexture");
1345 if (IsContextLost()) return nullptr;
1347 auto ret
= AsRefPtr(new WebGLTextureJS(*this));
1348 Run
<RPROC(CreateTexture
)>(ret
->mId
);
1349 return ret
.forget();
1352 already_AddRefed
<WebGLTransformFeedbackJS
>
1353 ClientWebGLContext::CreateTransformFeedback() const {
1354 const FuncScope
funcScope(*this, "createTransformFeedback");
1355 if (IsContextLost()) return nullptr;
1357 auto ret
= AsRefPtr(new WebGLTransformFeedbackJS(*this));
1358 Run
<RPROC(CreateTransformFeedback
)>(ret
->mId
);
1359 return ret
.forget();
1362 already_AddRefed
<WebGLVertexArrayJS
> ClientWebGLContext::CreateVertexArray()
1364 const FuncScope
funcScope(*this, "createVertexArray");
1365 if (IsContextLost()) return nullptr;
1367 auto ret
= AsRefPtr(new WebGLVertexArrayJS(*this));
1368 Run
<RPROC(CreateVertexArray
)>(ret
->mId
);
1369 return ret
.forget();
1374 static bool ValidateOrSkipForDelete(const ClientWebGLContext
& context
,
1375 const webgl::ObjectJS
* const obj
) {
1376 if (!obj
) return false;
1377 if (!obj
->ValidateForContext(context
, "obj")) return false;
1378 if (obj
->IsDeleted()) return false;
1382 void ClientWebGLContext::DeleteBuffer(WebGLBufferJS
* const obj
) {
1383 const FuncScope
funcScope(*this, "deleteBuffer");
1384 if (IsContextLost()) return;
1385 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1386 auto& state
= State();
1388 // Unbind from all bind points and bound containers
1391 for (const auto i
: IntegerRange(state
.mBoundUbos
.size())) {
1392 if (state
.mBoundUbos
[i
] == obj
) {
1393 BindBufferBase(LOCAL_GL_UNIFORM_BUFFER
, i
, nullptr);
1397 // TFO only if not active
1398 if (!state
.mBoundTfo
->mActiveOrPaused
) {
1399 const auto& buffers
= state
.mBoundTfo
->mAttribBuffers
;
1400 for (const auto i
: IntegerRange(buffers
.size())) {
1401 if (buffers
[i
] == obj
) {
1402 BindBufferBase(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
, i
, nullptr);
1407 // Generic/global bind points
1408 for (const auto& pair
: state
.mBoundBufferByTarget
) {
1409 if (pair
.second
== obj
) {
1410 BindBuffer(pair
.first
, nullptr);
1415 if (state
.mBoundVao
->mIndexBuffer
== obj
) {
1416 BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER
, nullptr);
1419 const auto& vaoBuffers
= state
.mBoundVao
->mAttribBuffers
;
1420 Maybe
<WebGLBufferJS
*> toRestore
;
1421 for (const auto i
: IntegerRange(vaoBuffers
.size())) {
1422 if (vaoBuffers
[i
] == obj
) {
1425 Some(state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
].get());
1427 BindBuffer(LOCAL_GL_ARRAY_BUFFER
, nullptr);
1430 VertexAttribPointer(i
, 4, LOCAL_GL_FLOAT
, false, 0, 0);
1433 if (toRestore
&& *toRestore
) {
1434 BindBuffer(LOCAL_GL_ARRAY_BUFFER
, *toRestore
);
1439 obj
->mDeleteRequested
= true;
1440 Run
<RPROC(DeleteBuffer
)>(obj
->mId
);
1443 void ClientWebGLContext::DeleteFramebuffer(WebGLFramebufferJS
* const obj
,
1444 bool canDeleteOpaque
) {
1445 const FuncScope
funcScope(*this, "deleteFramebuffer");
1446 if (IsContextLost()) return;
1447 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1448 if (!canDeleteOpaque
&& obj
->mOpaque
) {
1450 LOCAL_GL_INVALID_OPERATION
,
1451 "An opaque framebuffer's attachments cannot be inspected or changed.");
1454 const auto& state
= State();
1457 const auto fnDetach
= [&](const GLenum target
,
1458 const WebGLFramebufferJS
* const fb
) {
1460 BindFramebuffer(target
, nullptr);
1463 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1464 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1466 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1467 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1470 obj
->mDeleteRequested
= true;
1471 Run
<RPROC(DeleteFramebuffer
)>(obj
->mId
);
1474 void ClientWebGLContext::DeleteProgram(WebGLProgramJS
* const obj
) const {
1475 const FuncScope
funcScope(*this, "deleteProgram");
1476 if (IsContextLost()) return;
1477 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1481 obj
->mKeepAlive
= nullptr;
1484 webgl::ProgramKeepAlive::~ProgramKeepAlive() {
1485 if (!mParent
) return;
1486 const auto& context
= mParent
->Context();
1487 if (!context
) return;
1488 context
->DoDeleteProgram(*mParent
);
1491 void ClientWebGLContext::DoDeleteProgram(WebGLProgramJS
& obj
) const {
1492 obj
.mNextLink_Shaders
= {};
1493 Run
<RPROC(DeleteProgram
)>(obj
.mId
);
1496 static GLenum
QuerySlotTarget(const GLenum specificTarget
);
1498 void ClientWebGLContext::DeleteQuery(WebGLQueryJS
* const obj
) {
1499 const FuncScope
funcScope(*this, "deleteQuery");
1500 if (IsContextLost()) return;
1501 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1502 const auto& state
= State();
1504 // Unbind if current
1507 // Despite mTarget being set, we may not have called BeginQuery on this
1508 // object. QueryCounter may also set mTarget.
1509 const auto slotTarget
= QuerySlotTarget(obj
->mTarget
);
1510 const auto curForTarget
=
1511 MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
1513 if (curForTarget
&& *curForTarget
== obj
) {
1514 EndQuery(obj
->mTarget
);
1518 obj
->mDeleteRequested
= true;
1519 Run
<RPROC(DeleteQuery
)>(obj
->mId
);
1522 void ClientWebGLContext::DeleteRenderbuffer(WebGLRenderbufferJS
* const obj
) {
1523 const FuncScope
funcScope(*this, "deleteRenderbuffer");
1524 if (IsContextLost()) return;
1525 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1526 const auto& state
= State();
1529 if (state
.mBoundRb
== obj
) {
1530 BindRenderbuffer(LOCAL_GL_RENDERBUFFER
, nullptr);
1533 // Unbind from bound FBs
1534 const auto fnDetach
= [&](const GLenum target
,
1535 const WebGLFramebufferJS
* const fb
) {
1537 for (const auto& pair
: fb
->mAttachments
) {
1538 if (pair
.second
.rb
== obj
) {
1539 FramebufferRenderbuffer(target
, pair
.first
, LOCAL_GL_RENDERBUFFER
,
1544 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1545 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1547 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1548 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1551 obj
->mDeleteRequested
= true;
1552 Run
<RPROC(DeleteRenderbuffer
)>(obj
->mId
);
1555 void ClientWebGLContext::DeleteSampler(WebGLSamplerJS
* const obj
) {
1556 const FuncScope
funcScope(*this, "deleteSampler");
1557 if (IsContextLost()) return;
1558 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1559 const auto& state
= State();
1562 for (const auto i
: IntegerRange(state
.mTexUnits
.size())) {
1563 if (state
.mTexUnits
[i
].sampler
== obj
) {
1564 BindSampler(i
, nullptr);
1568 obj
->mDeleteRequested
= true;
1569 Run
<RPROC(DeleteSampler
)>(obj
->mId
);
1572 void ClientWebGLContext::DeleteShader(WebGLShaderJS
* const obj
) const {
1573 const FuncScope
funcScope(*this, "deleteShader");
1574 if (IsContextLost()) return;
1575 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1579 obj
->mKeepAlive
= nullptr;
1582 webgl::ShaderKeepAlive::~ShaderKeepAlive() {
1583 if (!mParent
) return;
1584 const auto& context
= mParent
->Context();
1585 if (!context
) return;
1586 context
->DoDeleteShader(*mParent
);
1589 void ClientWebGLContext::DoDeleteShader(const WebGLShaderJS
& obj
) const {
1590 Run
<RPROC(DeleteShader
)>(obj
.mId
);
1593 void ClientWebGLContext::DeleteSync(WebGLSyncJS
* const obj
) const {
1594 const FuncScope
funcScope(*this, "deleteSync");
1595 if (IsContextLost()) return;
1596 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1598 // Nothing to unbind
1600 obj
->mDeleteRequested
= true;
1601 Run
<RPROC(DeleteSync
)>(obj
->mId
);
1604 void ClientWebGLContext::DeleteTexture(WebGLTextureJS
* const obj
) {
1605 const FuncScope
funcScope(*this, "deleteTexture");
1606 if (IsContextLost()) return;
1607 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1608 auto& state
= State();
1611 const auto& target
= obj
->mTarget
;
1613 // Unbind from tex units
1614 Maybe
<uint32_t> restoreTexUnit
;
1615 for (const auto i
: IntegerRange(state
.mTexUnits
.size())) {
1616 if (state
.mTexUnits
[i
].texByTarget
[target
] == obj
) {
1617 if (!restoreTexUnit
) {
1618 restoreTexUnit
= Some(state
.mActiveTexUnit
);
1620 ActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
1621 BindTexture(target
, nullptr);
1624 if (restoreTexUnit
) {
1625 ActiveTexture(LOCAL_GL_TEXTURE0
+ *restoreTexUnit
);
1628 // Unbind from bound FBs
1629 const auto fnDetach
= [&](const GLenum target
,
1630 const WebGLFramebufferJS
* const fb
) {
1632 for (const auto& pair
: fb
->mAttachments
) {
1633 if (pair
.second
.tex
== obj
) {
1634 FramebufferRenderbuffer(target
, pair
.first
, LOCAL_GL_RENDERBUFFER
,
1639 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1640 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1642 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1643 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1647 obj
->mDeleteRequested
= true;
1648 Run
<RPROC(DeleteTexture
)>(obj
->mId
);
1651 void ClientWebGLContext::DeleteTransformFeedback(
1652 WebGLTransformFeedbackJS
* const obj
) {
1653 const FuncScope
funcScope(*this, "deleteTransformFeedback");
1654 if (IsContextLost()) return;
1655 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1656 const auto& state
= State();
1658 if (obj
->mActiveOrPaused
) {
1659 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
1660 "Transform Feedback object still active or paused.");
1665 if (state
.mBoundTfo
== obj
) {
1666 BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK
, nullptr);
1669 obj
->mDeleteRequested
= true;
1670 Run
<RPROC(DeleteTransformFeedback
)>(obj
->mId
);
1673 void ClientWebGLContext::DeleteVertexArray(WebGLVertexArrayJS
* const obj
) {
1674 const FuncScope
funcScope(*this, "deleteVertexArray");
1675 if (IsContextLost()) return;
1676 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1677 const auto& state
= State();
1680 if (state
.mBoundVao
== obj
) {
1681 BindVertexArray(nullptr);
1684 obj
->mDeleteRequested
= true;
1685 Run
<RPROC(DeleteVertexArray
)>(obj
->mId
);
1690 bool ClientWebGLContext::IsBuffer(const WebGLBufferJS
* const obj
) const {
1691 const FuncScope
funcScope(*this, "isBuffer");
1692 if (IsContextLost()) return false;
1694 return obj
&& obj
->IsUsable(*this) &&
1695 obj
->mKind
!= webgl::BufferKind::Undefined
;
1698 bool ClientWebGLContext::IsFramebuffer(
1699 const WebGLFramebufferJS
* const obj
) const {
1700 const FuncScope
funcScope(*this, "isFramebuffer");
1701 if (IsContextLost()) return false;
1703 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1706 bool ClientWebGLContext::IsProgram(const WebGLProgramJS
* const obj
) const {
1707 const FuncScope
funcScope(*this, "isProgram");
1708 if (IsContextLost()) return false;
1710 return obj
&& obj
->IsUsable(*this);
1713 bool ClientWebGLContext::IsQuery(const WebGLQueryJS
* const obj
) const {
1714 const FuncScope
funcScope(*this, "isQuery");
1715 if (IsContextLost()) return false;
1717 return obj
&& obj
->IsUsable(*this) && obj
->mTarget
;
1720 bool ClientWebGLContext::IsRenderbuffer(
1721 const WebGLRenderbufferJS
* const obj
) const {
1722 const FuncScope
funcScope(*this, "isRenderbuffer");
1723 if (IsContextLost()) return false;
1725 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1728 bool ClientWebGLContext::IsSampler(const WebGLSamplerJS
* const obj
) const {
1729 const FuncScope
funcScope(*this, "isSampler");
1730 if (IsContextLost()) return false;
1732 return obj
&& obj
->IsUsable(*this);
1735 bool ClientWebGLContext::IsShader(const WebGLShaderJS
* const obj
) const {
1736 const FuncScope
funcScope(*this, "isShader");
1737 if (IsContextLost()) return false;
1739 return obj
&& obj
->IsUsable(*this);
1742 bool ClientWebGLContext::IsSync(const WebGLSyncJS
* const obj
) const {
1743 const FuncScope
funcScope(*this, "isSync");
1744 if (IsContextLost()) return false;
1746 return obj
&& obj
->IsUsable(*this);
1749 bool ClientWebGLContext::IsTexture(const WebGLTextureJS
* const obj
) const {
1750 const FuncScope
funcScope(*this, "isTexture");
1751 if (IsContextLost()) return false;
1753 return obj
&& obj
->IsUsable(*this) && obj
->mTarget
;
1756 bool ClientWebGLContext::IsTransformFeedback(
1757 const WebGLTransformFeedbackJS
* const obj
) const {
1758 const FuncScope
funcScope(*this, "isTransformFeedback");
1759 if (IsContextLost()) return false;
1761 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1764 bool ClientWebGLContext::IsVertexArray(
1765 const WebGLVertexArrayJS
* const obj
) const {
1766 const FuncScope
funcScope(*this, "isVertexArray");
1767 if (IsContextLost()) return false;
1769 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1772 // ------------------------- GL State -------------------------
1774 void ClientWebGLContext::SetEnabledI(GLenum cap
, Maybe
<GLuint
> i
,
1776 Run
<RPROC(SetEnabled
)>(cap
, i
, val
);
1779 bool ClientWebGLContext::IsEnabled(GLenum cap
) const {
1780 const FuncScope
funcScope(*this, "isEnabled");
1781 if (IsContextLost()) return false;
1783 const auto& inProcess
= mNotLost
->inProcess
;
1785 return inProcess
->IsEnabled(cap
);
1787 const auto& child
= mNotLost
->outOfProcess
;
1788 child
->FlushPendingCmds();
1790 if (!child
->SendIsEnabled(cap
, &ret
)) return false;
1794 void ClientWebGLContext::GetInternalformatParameter(
1795 JSContext
* cx
, GLenum target
, GLenum internalformat
, GLenum pname
,
1796 JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) {
1797 const FuncScope
funcScope(*this, "getInternalformatParameter");
1798 retval
.set(JS::NullValue());
1799 const auto notLost
=
1800 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1801 if (IsContextLost()) return;
1803 const auto& inProcessContext
= notLost
->inProcess
;
1804 Maybe
<std::vector
<int32_t>> maybe
;
1805 if (inProcessContext
) {
1806 maybe
= inProcessContext
->GetInternalformatParameter(target
, internalformat
,
1809 const auto& child
= notLost
->outOfProcess
;
1810 child
->FlushPendingCmds();
1811 if (!child
->SendGetInternalformatParameter(target
, internalformat
, pname
,
1820 // zero-length array indicates out-of-memory
1822 dom::Int32Array::Create(cx
, this, maybe
->size(), maybe
->data());
1824 rv
= NS_ERROR_OUT_OF_MEMORY
;
1826 retval
.setObjectOrNull(obj
);
1829 static JS::Value
StringValue(JSContext
* cx
, const std::string
& str
,
1831 JSString
* jsStr
= JS_NewStringCopyN(cx
, str
.data(), str
.size());
1833 er
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1834 return JS::NullValue();
1837 return JS::StringValue(jsStr
);
1840 template <typename T
>
1841 bool ToJSValueOrNull(JSContext
* const cx
, const RefPtr
<T
>& ptr
,
1842 JS::MutableHandle
<JS::Value
> retval
) {
1844 retval
.set(JS::NullValue());
1847 return dom::ToJSValue(cx
, ptr
, retval
);
1850 template <typename T
, typename U
, typename S
>
1851 static JS::Value
CreateAs(JSContext
* cx
, nsWrapperCache
* creator
, const S
& src
,
1854 T::Create(cx
, creator
, src
.size(), reinterpret_cast<U
>(src
.data()));
1856 rv
= NS_ERROR_OUT_OF_MEMORY
;
1858 return JS::ObjectOrNullValue(obj
);
1861 template <typename T
, typename S
>
1862 static JS::Value
Create(JSContext
* cx
, nsWrapperCache
* creator
, const S
& src
,
1864 return CreateAs
<T
, decltype(&src
[0]), S
>(cx
, creator
, src
, rv
);
1867 Maybe
<double> ClientWebGLContext::GetNumber(const GLenum pname
) {
1868 MOZ_ASSERT(!IsContextLost());
1870 const auto& inProcess
= mNotLost
->inProcess
;
1872 return inProcess
->GetNumber(pname
);
1875 const auto& child
= mNotLost
->outOfProcess
;
1876 child
->FlushPendingCmds();
1879 if (!child
->SendGetNumber(pname
, &ret
)) {
1885 Maybe
<std::string
> ClientWebGLContext::GetString(const GLenum pname
) {
1886 MOZ_ASSERT(!IsContextLost());
1888 const auto& inProcess
= mNotLost
->inProcess
;
1890 return inProcess
->GetString(pname
);
1893 const auto& child
= mNotLost
->outOfProcess
;
1894 child
->FlushPendingCmds();
1896 Maybe
<std::string
> ret
;
1897 if (!child
->SendGetString(pname
, &ret
)) {
1903 void ClientWebGLContext::GetParameter(JSContext
* cx
, GLenum pname
,
1904 JS::MutableHandle
<JS::Value
> retval
,
1905 ErrorResult
& rv
, const bool debug
) {
1906 retval
.set(JS::NullValue());
1907 const FuncScope
funcScope(*this, "getParameter");
1908 if (IsContextLost()) return;
1909 const auto& limits
= Limits();
1910 const auto& state
= State();
1914 const auto fnSetRetval_Buffer
= [&](const GLenum target
) {
1915 const auto buffer
= *MaybeFind(state
.mBoundBufferByTarget
, target
);
1916 (void)ToJSValueOrNull(cx
, buffer
, retval
);
1918 const auto fnSetRetval_Tex
= [&](const GLenum texTarget
) {
1919 const auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
1920 const auto tex
= Find(texUnit
.texByTarget
, texTarget
, nullptr);
1921 (void)ToJSValueOrNull(cx
, tex
, retval
);
1925 case LOCAL_GL_ARRAY_BUFFER_BINDING
:
1926 fnSetRetval_Buffer(LOCAL_GL_ARRAY_BUFFER
);
1929 case LOCAL_GL_CURRENT_PROGRAM
:
1930 (void)ToJSValueOrNull(cx
, state
.mCurrentProgram
, retval
);
1933 case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING
:
1934 (void)ToJSValueOrNull(cx
, state
.mBoundVao
->mIndexBuffer
, retval
);
1937 case LOCAL_GL_FRAMEBUFFER_BINDING
:
1938 (void)ToJSValueOrNull(cx
, state
.mBoundDrawFb
, retval
);
1941 case LOCAL_GL_RENDERBUFFER_BINDING
:
1942 (void)ToJSValueOrNull(cx
, state
.mBoundRb
, retval
);
1945 case LOCAL_GL_TEXTURE_BINDING_2D
:
1946 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D
);
1949 case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP
:
1950 fnSetRetval_Tex(LOCAL_GL_TEXTURE_CUBE_MAP
);
1953 case LOCAL_GL_VERTEX_ARRAY_BINDING
: {
1955 !IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object
))
1958 auto ret
= state
.mBoundVao
;
1959 if (ret
== state
.mDefaultVao
) {
1962 (void)ToJSValueOrNull(cx
, ret
, retval
);
1966 case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
:
1967 retval
.set(JS::NumberValue(limits
.maxTexUnits
));
1969 case LOCAL_GL_MAX_TEXTURE_SIZE
:
1970 retval
.set(JS::NumberValue(limits
.maxTex2dSize
));
1972 case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE
:
1973 retval
.set(JS::NumberValue(limits
.maxTexCubeSize
));
1975 case LOCAL_GL_MAX_VERTEX_ATTRIBS
:
1976 retval
.set(JS::NumberValue(limits
.maxVertexAttribs
));
1979 case LOCAL_GL_MAX_VIEWS_OVR
:
1980 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
1981 retval
.set(JS::NumberValue(limits
.maxMultiviewLayers
));
1986 case LOCAL_GL_PACK_ALIGNMENT
:
1987 retval
.set(JS::NumberValue(state
.mPixelPackState
.alignmentInTypeElems
));
1989 case LOCAL_GL_UNPACK_ALIGNMENT
:
1990 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.alignmentInTypeElems
));
1993 case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL
:
1994 retval
.set(JS::BooleanValue(state
.mPixelUnpackState
.flipY
));
1996 case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL
:
1997 retval
.set(JS::BooleanValue(state
.mPixelUnpackState
.premultiplyAlpha
));
1999 case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL
:
2000 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.colorspaceConversion
));
2007 case LOCAL_GL_DEPTH_RANGE
:
2008 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mDepthRange
, rv
));
2011 case LOCAL_GL_ALIASED_POINT_SIZE_RANGE
:
2013 Create
<dom::Float32Array
>(cx
, this, limits
.pointSizeRange
, rv
));
2016 case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE
:
2018 Create
<dom::Float32Array
>(cx
, this, limits
.lineWidthRange
, rv
));
2022 case LOCAL_GL_COLOR_CLEAR_VALUE
:
2023 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mClearColor
, rv
));
2026 case LOCAL_GL_BLEND_COLOR
:
2027 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mBlendColor
, rv
));
2031 case LOCAL_GL_MAX_VIEWPORT_DIMS
: {
2033 std::array
<uint32_t, 2>{limits
.maxViewportDim
, limits
.maxViewportDim
};
2034 retval
.set(CreateAs
<dom::Int32Array
, const int32_t*>(cx
, this, dims
, rv
));
2039 case LOCAL_GL_SCISSOR_BOX
:
2040 retval
.set(Create
<dom::Int32Array
>(cx
, this, state
.mScissor
, rv
));
2043 case LOCAL_GL_VIEWPORT
:
2044 retval
.set(Create
<dom::Int32Array
>(cx
, this, state
.mViewport
, rv
));
2048 case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS
:
2049 retval
.set(Create
<dom::Uint32Array
>(cx
, this,
2050 state
.mCompressedTextureFormats
, rv
));
2056 case LOCAL_GL_COPY_READ_BUFFER_BINDING
:
2057 fnSetRetval_Buffer(LOCAL_GL_COPY_READ_BUFFER
);
2060 case LOCAL_GL_COPY_WRITE_BUFFER_BINDING
:
2061 fnSetRetval_Buffer(LOCAL_GL_COPY_WRITE_BUFFER
);
2064 case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
:
2065 (void)ToJSValueOrNull(cx
, state
.mBoundDrawFb
, retval
);
2068 case LOCAL_GL_PIXEL_PACK_BUFFER_BINDING
:
2069 fnSetRetval_Buffer(LOCAL_GL_PIXEL_PACK_BUFFER
);
2072 case LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING
:
2073 fnSetRetval_Buffer(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
2076 case LOCAL_GL_READ_FRAMEBUFFER_BINDING
:
2077 (void)ToJSValueOrNull(cx
, state
.mBoundReadFb
, retval
);
2080 case LOCAL_GL_SAMPLER_BINDING
: {
2081 const auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
2082 (void)ToJSValueOrNull(cx
, texUnit
.sampler
, retval
);
2086 case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY
:
2087 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D_ARRAY
);
2090 case LOCAL_GL_TEXTURE_BINDING_3D
:
2091 fnSetRetval_Tex(LOCAL_GL_TEXTURE_3D
);
2094 case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING
: {
2095 auto ret
= state
.mBoundTfo
;
2096 if (ret
== state
.mDefaultTfo
) {
2099 (void)ToJSValueOrNull(cx
, ret
, retval
);
2103 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING
:
2104 fnSetRetval_Buffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
);
2107 case LOCAL_GL_UNIFORM_BUFFER_BINDING
:
2108 fnSetRetval_Buffer(LOCAL_GL_UNIFORM_BUFFER
);
2111 case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
:
2113 JS::NumberValue(webgl::kMaxTransformFeedbackSeparateAttribs
));
2115 case LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS
:
2116 retval
.set(JS::NumberValue(limits
.maxUniformBufferBindings
));
2118 case LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
:
2119 retval
.set(JS::NumberValue(limits
.uniformBufferOffsetAlignment
));
2121 case LOCAL_GL_MAX_3D_TEXTURE_SIZE
:
2122 retval
.set(JS::NumberValue(limits
.maxTex3dSize
));
2124 case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS
:
2125 retval
.set(JS::NumberValue(limits
.maxTexArrayLayers
));
2128 case LOCAL_GL_PACK_ROW_LENGTH
:
2129 retval
.set(JS::NumberValue(state
.mPixelPackState
.rowLength
));
2131 case LOCAL_GL_PACK_SKIP_PIXELS
:
2132 retval
.set(JS::NumberValue(state
.mPixelPackState
.skipPixels
));
2134 case LOCAL_GL_PACK_SKIP_ROWS
:
2135 retval
.set(JS::NumberValue(state
.mPixelPackState
.skipRows
));
2138 case LOCAL_GL_UNPACK_IMAGE_HEIGHT
:
2139 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.imageHeight
));
2141 case LOCAL_GL_UNPACK_ROW_LENGTH
:
2142 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.rowLength
));
2144 case LOCAL_GL_UNPACK_SKIP_IMAGES
:
2145 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.skipImages
));
2147 case LOCAL_GL_UNPACK_SKIP_PIXELS
:
2148 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.skipPixels
));
2150 case LOCAL_GL_UNPACK_SKIP_ROWS
:
2151 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.skipRows
));
2159 const auto GetUnmaskedRenderer
= [&]() {
2160 const auto prefLock
= StaticPrefs::webgl_override_unmasked_renderer();
2161 if (!prefLock
->IsEmpty()) {
2162 return Some(ToString(*prefLock
));
2164 return GetString(LOCAL_GL_RENDERER
);
2167 const auto GetUnmaskedVendor
= [&]() {
2168 const auto prefLock
= StaticPrefs::webgl_override_unmasked_vendor();
2169 if (!prefLock
->IsEmpty()) {
2170 return Some(ToString(*prefLock
));
2172 return GetString(LOCAL_GL_VENDOR
);
2177 Maybe
<std::string
> ret
;
2180 case LOCAL_GL_VENDOR
:
2181 ret
= Some(std::string
{"Mozilla"});
2184 case LOCAL_GL_RENDERER
: {
2185 bool allowRenderer
= StaticPrefs::webgl_enable_renderer_query();
2186 if (nsContentUtils::ShouldResistFingerprinting()) {
2187 allowRenderer
= false;
2189 if (allowRenderer
) {
2190 ret
= GetUnmaskedRenderer();
2192 ret
= Some(webgl::SanitizeRenderer(*ret
));
2196 ret
= Some(std::string
{"Mozilla"});
2201 case LOCAL_GL_VERSION
:
2203 ret
= Some(std::string
{"WebGL 2.0"});
2205 ret
= Some(std::string
{"WebGL 1.0"});
2209 case LOCAL_GL_SHADING_LANGUAGE_VERSION
:
2211 ret
= Some(std::string
{"WebGL GLSL ES 3.00"});
2213 ret
= Some(std::string
{"WebGL GLSL ES 1.0"});
2217 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL
:
2218 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL
: {
2219 if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info
)) {
2220 EnqueueError_ArgEnum("pname", pname
);
2225 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL
:
2226 ret
= GetUnmaskedRenderer();
2227 if (ret
&& StaticPrefs::webgl_sanitize_unmasked_renderer()) {
2228 *ret
= webgl::SanitizeRenderer(*ret
);
2232 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL
:
2233 ret
= GetUnmaskedVendor();
2247 retval
.set(StringValue(cx
, *ret
, rv
));
2254 bool debugOnly
= false;
2255 bool asString
= false;
2258 case LOCAL_GL_EXTENSIONS
:
2259 case LOCAL_GL_RENDERER
:
2260 case LOCAL_GL_VENDOR
:
2261 case LOCAL_GL_VERSION
:
2262 case dom::MOZ_debug_Binding::WSI_INFO
:
2267 case dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION
:
2275 if (debugOnly
&& !debug
) {
2276 EnqueueError_ArgEnum("pname", pname
);
2283 const auto maybe
= GetString(pname
);
2286 if (pname
== dom::MOZ_debug_Binding::WSI_INFO
) {
2287 nsPrintfCString
more("\nIsWebglOutOfProcessEnabled: %i",
2288 int(IsWebglOutOfProcessEnabled()));
2289 str
+= more
.BeginReading();
2291 retval
.set(StringValue(cx
, str
.c_str(), rv
));
2294 const auto maybe
= GetNumber(pname
);
2298 case LOCAL_GL_BLEND
:
2299 case LOCAL_GL_CULL_FACE
:
2300 case LOCAL_GL_DEPTH_TEST
:
2301 case LOCAL_GL_DEPTH_WRITEMASK
:
2302 case LOCAL_GL_DITHER
:
2303 case LOCAL_GL_POLYGON_OFFSET_FILL
:
2304 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
:
2305 case LOCAL_GL_SAMPLE_COVERAGE
:
2306 case LOCAL_GL_SAMPLE_COVERAGE_INVERT
:
2307 case LOCAL_GL_SCISSOR_TEST
:
2308 case LOCAL_GL_STENCIL_TEST
:
2310 case LOCAL_GL_RASTERIZER_DISCARD
:
2311 case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE
:
2312 case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED
:
2313 retval
.set(JS::BooleanValue(*maybe
));
2317 case LOCAL_GL_COLOR_WRITEMASK
: {
2318 const auto mask
= uint8_t(*maybe
);
2319 const auto bs
= std::bitset
<4>(mask
);
2320 const auto src
= std::array
<bool, 4>{bs
[0], bs
[1], bs
[2], bs
[3]};
2321 JS::Rooted
<JS::Value
> arr(cx
);
2322 if (!dom::ToJSValue(cx
, src
.data(), src
.size(), &arr
)) {
2323 rv
= NS_ERROR_OUT_OF_MEMORY
;
2330 retval
.set(JS::NumberValue(*maybe
));
2337 void ClientWebGLContext::GetBufferParameter(
2338 JSContext
* cx
, GLenum target
, GLenum pname
,
2339 JS::MutableHandle
<JS::Value
> retval
) const {
2340 retval
.set(JS::NullValue());
2341 if (IsContextLost()) return;
2343 const auto maybe
= [&]() {
2344 const auto& inProcess
= mNotLost
->inProcess
;
2346 return inProcess
->GetBufferParameter(target
, pname
);
2348 const auto& child
= mNotLost
->outOfProcess
;
2349 child
->FlushPendingCmds();
2351 if (!child
->SendGetBufferParameter(target
, pname
, &ret
)) {
2357 retval
.set(JS::NumberValue(*maybe
));
2361 bool IsFramebufferTarget(const bool isWebgl2
, const GLenum target
) {
2363 case LOCAL_GL_FRAMEBUFFER
:
2366 case LOCAL_GL_DRAW_FRAMEBUFFER
:
2367 case LOCAL_GL_READ_FRAMEBUFFER
:
2375 void ClientWebGLContext::GetFramebufferAttachmentParameter(
2376 JSContext
* const cx
, const GLenum target
, const GLenum attachment
,
2377 const GLenum pname
, JS::MutableHandle
<JS::Value
> retval
,
2378 ErrorResult
& rv
) const {
2379 retval
.set(JS::NullValue());
2380 const FuncScope
funcScope(*this, "getFramebufferAttachmentParameter");
2381 if (IsContextLost()) return;
2383 const auto& state
= State();
2385 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
2386 EnqueueError_ArgEnum("target", target
);
2389 auto fb
= state
.mBoundDrawFb
;
2390 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
2391 fb
= state
.mBoundReadFb
;
2394 const auto fnGet
= [&](const GLenum pname
) {
2395 const auto fbId
= fb
? fb
->mId
: 0;
2397 const auto& inProcess
= mNotLost
->inProcess
;
2399 return inProcess
->GetFramebufferAttachmentParameter(fbId
, attachment
,
2402 const auto& child
= mNotLost
->outOfProcess
;
2403 child
->FlushPendingCmds();
2405 if (!child
->SendGetFramebufferAttachmentParameter(fbId
, attachment
, pname
,
2414 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
2415 "An opaque framebuffer's attachments cannot be inspected or "
2419 auto attachmentSlotEnum
= attachment
;
2420 if (mIsWebGL2
&& attachment
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
2421 // In webgl2, DEPTH_STENCIL is valid iff the DEPTH and STENCIL images
2422 // match, so check if the server errors.
2423 const auto maybe
= fnGet(LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
);
2425 attachmentSlotEnum
= LOCAL_GL_DEPTH_ATTACHMENT
;
2428 const auto maybeSlot
= fb
->GetAttachment(attachmentSlotEnum
);
2430 EnqueueError_ArgEnum("attachment", attachment
);
2433 const auto& attached
= *maybeSlot
;
2437 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
) {
2439 (void)ToJSValueOrNull(cx
, attached
.rb
, retval
);
2441 if (!mIsWebGL2
&& !attached
.tex
) {
2442 EnqueueError_ArgEnum("pname", pname
);
2445 (void)ToJSValueOrNull(cx
, attached
.tex
, retval
);
2451 const auto maybe
= fnGet(pname
);
2453 retval
.set(JS::NumberValue(*maybe
));
2457 void ClientWebGLContext::GetRenderbufferParameter(
2458 JSContext
* cx
, GLenum target
, GLenum pname
,
2459 JS::MutableHandle
<JS::Value
> retval
) const {
2460 retval
.set(JS::NullValue());
2461 const FuncScope
funcScope(*this, "getRenderbufferParameter");
2462 if (IsContextLost()) return;
2464 if (target
!= LOCAL_GL_RENDERBUFFER
) {
2465 EnqueueError_ArgEnum("target", target
);
2469 const auto& state
= State();
2470 const auto& rb
= state
.mBoundRb
;
2471 const auto rbId
= rb
? rb
->mId
: 0;
2472 const auto maybe
= [&]() {
2473 const auto& inProcess
= mNotLost
->inProcess
;
2475 return inProcess
->GetRenderbufferParameter(rbId
, pname
);
2477 const auto& child
= mNotLost
->outOfProcess
;
2478 child
->FlushPendingCmds();
2480 if (!child
->SendGetRenderbufferParameter(rbId
, pname
, &ret
)) {
2486 retval
.set(JS::NumberValue(*maybe
));
2490 void ClientWebGLContext::GetIndexedParameter(
2491 JSContext
* cx
, GLenum target
, GLuint index
,
2492 JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) const {
2493 retval
.set(JS::NullValue());
2494 const FuncScope
funcScope(*this, "getIndexedParameter");
2495 if (IsContextLost()) return;
2497 const auto& state
= State();
2500 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING
: {
2501 const auto& list
= state
.mBoundTfo
->mAttribBuffers
;
2502 if (index
>= list
.size()) {
2503 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2504 "`index` (%u) >= MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS",
2508 (void)ToJSValueOrNull(cx
, list
[index
], retval
);
2512 case LOCAL_GL_UNIFORM_BUFFER_BINDING
: {
2513 const auto& list
= state
.mBoundUbos
;
2514 if (index
>= list
.size()) {
2515 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2516 "`index` (%u) >= MAX_UNIFORM_BUFFER_BINDINGS", index
);
2519 (void)ToJSValueOrNull(cx
, list
[index
], retval
);
2524 const auto maybe
= [&]() {
2525 const auto& inProcess
= mNotLost
->inProcess
;
2527 return inProcess
->GetIndexedParameter(target
, index
);
2529 const auto& child
= mNotLost
->outOfProcess
;
2530 child
->FlushPendingCmds();
2532 if (!child
->SendGetIndexedParameter(target
, index
, &ret
)) {
2539 case LOCAL_GL_COLOR_WRITEMASK
: {
2540 const auto bs
= std::bitset
<4>(*maybe
);
2541 const auto src
= std::array
<bool, 4>{bs
[0], bs
[1], bs
[2], bs
[3]};
2542 JS::Rooted
<JS::Value
> arr(cx
);
2543 if (!dom::ToJSValue(cx
, src
.data(), src
.size(), &arr
)) {
2544 rv
= NS_ERROR_OUT_OF_MEMORY
;
2551 retval
.set(JS::NumberValue(*maybe
));
2557 void ClientWebGLContext::GetUniform(JSContext
* const cx
,
2558 const WebGLProgramJS
& prog
,
2559 const WebGLUniformLocationJS
& loc
,
2560 JS::MutableHandle
<JS::Value
> retval
) {
2561 retval
.set(JS::NullValue());
2562 const FuncScope
funcScope(*this, "getUniform");
2563 if (IsContextLost()) return;
2564 if (!prog
.ValidateUsable(*this, "prog")) return;
2565 if (!loc
.ValidateUsable(*this, "loc")) return;
2567 const auto& activeLinkResult
= GetActiveLinkResult();
2568 if (!activeLinkResult
) {
2569 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No active linked Program.");
2572 const auto& reqLinkInfo
= loc
.mParent
.lock();
2573 if (reqLinkInfo
.get() != activeLinkResult
) {
2574 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
2575 "UniformLocation is not from the current active Program.");
2579 const auto res
= [&]() {
2580 const auto& inProcess
= mNotLost
->inProcess
;
2582 return inProcess
->GetUniform(prog
.mId
, loc
.mLocation
);
2584 const auto& child
= mNotLost
->outOfProcess
;
2585 child
->FlushPendingCmds();
2586 webgl::GetUniformData ret
;
2587 if (!child
->SendGetUniform(prog
.mId
, loc
.mLocation
, &ret
)) {
2592 if (!res
.type
) return;
2594 const auto elemCount
= ElemTypeComponents(res
.type
);
2595 MOZ_ASSERT(elemCount
);
2599 retval
.set(JS::BooleanValue(res
.data
[0]));
2602 case LOCAL_GL_FLOAT
: {
2603 const auto ptr
= reinterpret_cast<const float*>(res
.data
);
2604 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2607 case LOCAL_GL_INT
: {
2608 const auto ptr
= reinterpret_cast<const int32_t*>(res
.data
);
2609 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2612 case LOCAL_GL_UNSIGNED_INT
:
2613 case LOCAL_GL_SAMPLER_2D
:
2614 case LOCAL_GL_SAMPLER_3D
:
2615 case LOCAL_GL_SAMPLER_CUBE
:
2616 case LOCAL_GL_SAMPLER_2D_SHADOW
:
2617 case LOCAL_GL_SAMPLER_2D_ARRAY
:
2618 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
2619 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
2620 case LOCAL_GL_INT_SAMPLER_2D
:
2621 case LOCAL_GL_INT_SAMPLER_3D
:
2622 case LOCAL_GL_INT_SAMPLER_CUBE
:
2623 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
2624 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
2625 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
2626 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
2627 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
: {
2628 const auto ptr
= reinterpret_cast<const uint32_t*>(res
.data
);
2629 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2635 case LOCAL_GL_BOOL_VEC2
:
2636 case LOCAL_GL_BOOL_VEC3
:
2637 case LOCAL_GL_BOOL_VEC4
: {
2638 const auto intArr
= reinterpret_cast<const int32_t*>(res
.data
);
2639 bool boolArr
[4] = {};
2640 for (const auto i
: IntegerRange(elemCount
)) {
2641 boolArr
[i
] = bool(intArr
[i
]);
2643 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, boolArr
, elemCount
, retval
));
2647 case LOCAL_GL_FLOAT_VEC2
:
2648 case LOCAL_GL_FLOAT_VEC3
:
2649 case LOCAL_GL_FLOAT_VEC4
:
2650 case LOCAL_GL_FLOAT_MAT2
:
2651 case LOCAL_GL_FLOAT_MAT3
:
2652 case LOCAL_GL_FLOAT_MAT4
:
2653 case LOCAL_GL_FLOAT_MAT2x3
:
2654 case LOCAL_GL_FLOAT_MAT2x4
:
2655 case LOCAL_GL_FLOAT_MAT3x2
:
2656 case LOCAL_GL_FLOAT_MAT3x4
:
2657 case LOCAL_GL_FLOAT_MAT4x2
:
2658 case LOCAL_GL_FLOAT_MAT4x3
: {
2659 const auto ptr
= reinterpret_cast<const float*>(res
.data
);
2660 JSObject
* obj
= dom::Float32Array::Create(cx
, this, elemCount
, ptr
);
2662 retval
.set(JS::ObjectOrNullValue(obj
));
2666 case LOCAL_GL_INT_VEC2
:
2667 case LOCAL_GL_INT_VEC3
:
2668 case LOCAL_GL_INT_VEC4
: {
2669 const auto ptr
= reinterpret_cast<const int32_t*>(res
.data
);
2670 JSObject
* obj
= dom::Int32Array::Create(cx
, this, elemCount
, ptr
);
2672 retval
.set(JS::ObjectOrNullValue(obj
));
2676 case LOCAL_GL_UNSIGNED_INT_VEC2
:
2677 case LOCAL_GL_UNSIGNED_INT_VEC3
:
2678 case LOCAL_GL_UNSIGNED_INT_VEC4
: {
2679 const auto ptr
= reinterpret_cast<const uint32_t*>(res
.data
);
2680 JSObject
* obj
= dom::Uint32Array::Create(cx
, this, elemCount
, ptr
);
2682 retval
.set(JS::ObjectOrNullValue(obj
));
2687 MOZ_CRASH("GFX: Invalid elemType.");
2691 already_AddRefed
<WebGLShaderPrecisionFormatJS
>
2692 ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype
,
2693 const GLenum precisiontype
) {
2694 if (IsContextLost()) return nullptr;
2695 const auto info
= [&]() {
2696 const auto& inProcess
= mNotLost
->inProcess
;
2698 return inProcess
->GetShaderPrecisionFormat(shadertype
, precisiontype
);
2700 const auto& child
= mNotLost
->outOfProcess
;
2701 child
->FlushPendingCmds();
2702 Maybe
<webgl::ShaderPrecisionFormat
> ret
;
2703 if (!child
->SendGetShaderPrecisionFormat(shadertype
, precisiontype
, &ret
)) {
2709 if (!info
) return nullptr;
2710 return AsAddRefed(new WebGLShaderPrecisionFormatJS(*info
));
2713 void ClientWebGLContext::BlendColor(GLclampf r
, GLclampf g
, GLclampf b
,
2715 const FuncScope
funcScope(*this, "blendColor");
2716 if (IsContextLost()) return;
2717 auto& state
= State();
2719 auto& cache
= state
.mBlendColor
;
2725 Run
<RPROC(BlendColor
)>(r
, g
, b
, a
);
2728 void ClientWebGLContext::BlendEquationSeparateI(Maybe
<GLuint
> i
, GLenum modeRGB
,
2730 Run
<RPROC(BlendEquationSeparate
)>(i
, modeRGB
, modeAlpha
);
2733 void ClientWebGLContext::BlendFuncSeparateI(Maybe
<GLuint
> i
, GLenum srcRGB
,
2734 GLenum dstRGB
, GLenum srcAlpha
,
2736 Run
<RPROC(BlendFuncSeparate
)>(i
, srcRGB
, dstRGB
, srcAlpha
, dstAlpha
);
2739 GLenum
ClientWebGLContext::CheckFramebufferStatus(GLenum target
) {
2740 if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
2742 const auto& inProcess
= mNotLost
->inProcess
;
2744 return inProcess
->CheckFramebufferStatus(target
);
2746 const auto& child
= mNotLost
->outOfProcess
;
2747 child
->FlushPendingCmds();
2749 if (!child
->SendCheckFramebufferStatus(target
, &ret
)) {
2755 void ClientWebGLContext::Clear(GLbitfield mask
) {
2756 Run
<RPROC(Clear
)>(mask
);
2763 void ClientWebGLContext::ClearBufferTv(const GLenum buffer
,
2764 const GLint drawBuffer
,
2765 const webgl::AttribBaseType type
,
2766 const Range
<const uint8_t>& view
,
2767 const GLuint srcElemOffset
) {
2768 const FuncScope
funcScope(*this, "clearBufferu?[fi]v");
2769 if (IsContextLost()) return;
2771 const auto byteOffset
= CheckedInt
<size_t>(srcElemOffset
) * sizeof(float);
2772 if (!byteOffset
.isValid() || byteOffset
.value() > view
.length()) {
2773 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`srcOffset` too large for `values`.");
2776 webgl::TypedQuad data
;
2779 auto dataSize
= data
.data
.size();
2781 case LOCAL_GL_COLOR
:
2784 case LOCAL_GL_DEPTH
:
2785 dataSize
= sizeof(float);
2788 case LOCAL_GL_STENCIL
:
2789 dataSize
= sizeof(int32_t);
2793 EnqueueError_ArgEnum("buffer", buffer
);
2797 const auto requiredBytes
= byteOffset
+ dataSize
;
2798 if (!requiredBytes
.isValid() || requiredBytes
.value() > view
.length()) {
2799 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`values` too small.");
2803 memcpy(data
.data
.data(), view
.begin().get() + byteOffset
.value(), dataSize
);
2804 Run
<RPROC(ClearBufferTv
)>(buffer
, drawBuffer
, data
);
2809 void ClientWebGLContext::ClearBufferfi(GLenum buffer
, GLint drawBuffer
,
2810 GLfloat depth
, GLint stencil
) {
2811 Run
<RPROC(ClearBufferfi
)>(buffer
, drawBuffer
, depth
, stencil
);
2818 void ClientWebGLContext::ClearColor(GLclampf r
, GLclampf g
, GLclampf b
,
2820 const FuncScope
funcScope(*this, "clearColor");
2821 if (IsContextLost()) return;
2822 auto& state
= State();
2824 auto& cache
= state
.mClearColor
;
2830 Run
<RPROC(ClearColor
)>(r
, g
, b
, a
);
2833 void ClientWebGLContext::ClearDepth(GLclampf v
) { Run
<RPROC(ClearDepth
)>(v
); }
2835 void ClientWebGLContext::ClearStencil(GLint v
) { Run
<RPROC(ClearStencil
)>(v
); }
2837 void ClientWebGLContext::ColorMaskI(Maybe
<GLuint
> i
, bool r
, bool g
, bool b
,
2839 const FuncScope
funcScope(*this, "colorMask");
2840 if (IsContextLost()) return;
2842 const uint8_t mask
=
2843 uint8_t(r
<< 0) | uint8_t(g
<< 1) | uint8_t(b
<< 2) | uint8_t(a
<< 3);
2844 Run
<RPROC(ColorMask
)>(i
, mask
);
2847 void ClientWebGLContext::CullFace(GLenum face
) { Run
<RPROC(CullFace
)>(face
); }
2849 void ClientWebGLContext::DepthFunc(GLenum func
) { Run
<RPROC(DepthFunc
)>(func
); }
2851 void ClientWebGLContext::DepthMask(WebGLboolean b
) { Run
<RPROC(DepthMask
)>(b
); }
2853 void ClientWebGLContext::DepthRange(GLclampf zNear
, GLclampf zFar
) {
2854 const FuncScope
funcScope(*this, "depthRange");
2855 if (IsContextLost()) return;
2856 auto& state
= State();
2858 state
.mDepthRange
= {zNear
, zFar
};
2860 Run
<RPROC(DepthRange
)>(zNear
, zFar
);
2863 void ClientWebGLContext::Flush(const bool flushGl
) {
2864 const FuncScope
funcScope(*this, "flush");
2865 if (IsContextLost()) return;
2868 Run
<RPROC(Flush
)>();
2871 if (mNotLost
->inProcess
) return;
2872 const auto& child
= mNotLost
->outOfProcess
;
2873 child
->FlushPendingCmds();
2876 void ClientWebGLContext::Finish() {
2877 if (IsContextLost()) return;
2879 const auto& inProcess
= mNotLost
->inProcess
;
2881 inProcess
->Finish();
2884 const auto& child
= mNotLost
->outOfProcess
;
2885 child
->FlushPendingCmds();
2886 (void)child
->SendFinish();
2889 void ClientWebGLContext::FrontFace(GLenum mode
) { Run
<RPROC(FrontFace
)>(mode
); }
2891 GLenum
ClientWebGLContext::GetError() {
2892 const FuncScope
funcScope(*this, "getError");
2894 const auto ret
= mNextError
;
2898 if (IsContextLost()) return 0;
2900 const auto& inProcess
= mNotLost
->inProcess
;
2902 return inProcess
->GetError();
2904 const auto& child
= mNotLost
->outOfProcess
;
2905 child
->FlushPendingCmds();
2907 if (!child
->SendGetError(&ret
)) {
2913 void ClientWebGLContext::Hint(GLenum target
, GLenum mode
) {
2914 Run
<RPROC(Hint
)>(target
, mode
);
2917 void ClientWebGLContext::LineWidth(GLfloat width
) {
2918 Run
<RPROC(LineWidth
)>(width
);
2921 Maybe
<webgl::ErrorInfo
> SetPixelUnpack(
2922 const bool isWebgl2
, webgl::PixelUnpackStateWebgl
* const unpacking
,
2923 const GLenum pname
, const GLint param
);
2925 void ClientWebGLContext::PixelStorei(const GLenum pname
, const GLint iparam
) {
2926 const FuncScope
funcScope(*this, "pixelStorei");
2927 if (IsContextLost()) return;
2928 if (!ValidateNonNegative("param", iparam
)) return;
2929 const auto param
= static_cast<uint32_t>(iparam
);
2931 auto& state
= State();
2932 auto& packState
= state
.mPixelPackState
;
2934 case LOCAL_GL_PACK_ALIGNMENT
:
2942 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2943 "PACK_ALIGNMENT must be one of [1,2,4,8], was %i.",
2947 packState
.alignmentInTypeElems
= param
;
2950 case LOCAL_GL_PACK_ROW_LENGTH
:
2951 if (!mIsWebGL2
) break;
2952 packState
.rowLength
= param
;
2955 case LOCAL_GL_PACK_SKIP_PIXELS
:
2956 if (!mIsWebGL2
) break;
2957 packState
.skipPixels
= param
;
2960 case LOCAL_GL_PACK_SKIP_ROWS
:
2961 if (!mIsWebGL2
) break;
2962 packState
.skipRows
= param
;
2965 case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH
:
2966 if (!IsSupported(WebGLExtensionID::MOZ_debug
)) {
2967 EnqueueError_ArgEnum("pname", pname
);
2977 SetPixelUnpack(mIsWebGL2
, &state
.mPixelUnpackState
, pname
, iparam
);
2984 void ClientWebGLContext::PolygonOffset(GLfloat factor
, GLfloat units
) {
2985 Run
<RPROC(PolygonOffset
)>(factor
, units
);
2988 void ClientWebGLContext::SampleCoverage(GLclampf value
, WebGLboolean invert
) {
2989 Run
<RPROC(SampleCoverage
)>(value
, invert
);
2992 void ClientWebGLContext::Scissor(GLint x
, GLint y
, GLsizei width
,
2994 const FuncScope
funcScope(*this, "scissor");
2995 if (IsContextLost()) return;
2996 auto& state
= State();
2998 if (!ValidateNonNegative("width", width
) ||
2999 !ValidateNonNegative("height", height
)) {
3003 state
.mScissor
= {x
, y
, width
, height
};
3005 Run
<RPROC(Scissor
)>(x
, y
, width
, height
);
3008 void ClientWebGLContext::StencilFuncSeparate(GLenum face
, GLenum func
,
3009 GLint ref
, GLuint mask
) {
3010 Run
<RPROC(StencilFuncSeparate
)>(face
, func
, ref
, mask
);
3013 void ClientWebGLContext::StencilMaskSeparate(GLenum face
, GLuint mask
) {
3014 Run
<RPROC(StencilMaskSeparate
)>(face
, mask
);
3017 void ClientWebGLContext::StencilOpSeparate(GLenum face
, GLenum sfail
,
3018 GLenum dpfail
, GLenum dppass
) {
3019 Run
<RPROC(StencilOpSeparate
)>(face
, sfail
, dpfail
, dppass
);
3022 void ClientWebGLContext::Viewport(GLint x
, GLint y
, GLsizei width
,
3024 const FuncScope
funcScope(*this, "viewport");
3025 if (IsContextLost()) return;
3026 auto& state
= State();
3028 if (!ValidateNonNegative("width", width
) ||
3029 !ValidateNonNegative("height", height
)) {
3033 state
.mViewport
= {x
, y
, width
, height
};
3035 Run
<RPROC(Viewport
)>(x
, y
, width
, height
);
3038 // ------------------------- Buffer Objects -------------------------
3040 Maybe
<const webgl::ErrorInfo
> ValidateBindBuffer(
3041 const GLenum target
, const webgl::BufferKind curKind
) {
3042 if (curKind
== webgl::BufferKind::Undefined
) return {};
3044 auto requiredKind
= webgl::BufferKind::NonIndex
;
3046 case LOCAL_GL_COPY_READ_BUFFER
:
3047 case LOCAL_GL_COPY_WRITE_BUFFER
:
3048 return {}; // Always ok
3050 case LOCAL_GL_ELEMENT_ARRAY_BUFFER
:
3051 requiredKind
= webgl::BufferKind::Index
;
3058 if (curKind
!= requiredKind
) {
3059 const auto fnKindStr
= [&](const webgl::BufferKind kind
) {
3060 if (kind
== webgl::BufferKind::Index
) return "ELEMENT_ARRAY_BUFFER";
3061 return "non-ELEMENT_ARRAY_BUFFER";
3063 const auto info
= nsPrintfCString(
3064 "Buffer previously bound to %s cannot be now bound to %s.",
3065 fnKindStr(curKind
), fnKindStr(requiredKind
));
3067 webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
, info
.BeginReading()});
3073 Maybe
<webgl::ErrorInfo
> CheckBindBufferRange(
3074 const GLenum target
, const GLuint index
, const bool isBuffer
,
3075 const uint64_t offset
, const uint64_t size
, const webgl::Limits
& limits
) {
3076 const auto fnSome
= [&](const GLenum type
, const nsACString
& info
) {
3077 return Some(webgl::ErrorInfo
{type
, info
.BeginReading()});
3081 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
:
3082 if (index
>= webgl::kMaxTransformFeedbackSeparateAttribs
) {
3083 const auto info
= nsPrintfCString(
3084 "`index` (%u) must be less than "
3085 "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS (%u).",
3086 index
, webgl::kMaxTransformFeedbackSeparateAttribs
);
3087 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3090 if (offset
% 4 != 0 || size
% 4 != 0) {
3092 nsPrintfCString("`offset` (%" PRIu64
") and `size` (%" PRIu64
3093 ") must both be aligned to 4 for"
3094 " TRANSFORM_FEEDBACK_BUFFER.",
3096 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3100 case LOCAL_GL_UNIFORM_BUFFER
:
3101 if (index
>= limits
.maxUniformBufferBindings
) {
3102 const auto info
= nsPrintfCString(
3103 "`index` (%u) must be less than MAX_UNIFORM_BUFFER_BINDINGS (%u).",
3104 index
, limits
.maxUniformBufferBindings
);
3105 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3108 if (offset
% limits
.uniformBufferOffsetAlignment
!= 0) {
3110 nsPrintfCString("`offset` (%" PRIu64
3111 ") must be aligned to "
3112 "UNIFORM_BUFFER_OFFSET_ALIGNMENT (%u).",
3113 offset
, limits
.uniformBufferOffsetAlignment
);
3114 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
3120 nsPrintfCString("Unrecognized `target`: 0x%04x", target
);
3121 return fnSome(LOCAL_GL_INVALID_ENUM
, info
);
3130 void ClientWebGLContext::BindBuffer(const GLenum target
,
3131 WebGLBufferJS
* const buffer
) {
3132 const FuncScope
funcScope(*this, "bindBuffer");
3133 if (IsContextLost()) return;
3134 if (buffer
&& !buffer
->ValidateUsable(*this, "buffer")) return;
3137 // Check for INVALID_ENUM
3139 auto& state
= State();
3140 auto* slot
= &(state
.mBoundVao
->mIndexBuffer
);
3141 if (target
!= LOCAL_GL_ELEMENT_ARRAY_BUFFER
) {
3142 const auto itr
= state
.mBoundBufferByTarget
.find(target
);
3143 if (itr
== state
.mBoundBufferByTarget
.end()) {
3144 EnqueueError_ArgEnum("target", target
);
3147 slot
= &(itr
->second
);
3152 auto kind
= webgl::BufferKind::Undefined
;
3154 kind
= buffer
->mKind
;
3156 const auto err
= ValidateBindBuffer(target
, kind
);
3158 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3163 // Validation complete
3165 if (buffer
&& buffer
->mKind
== webgl::BufferKind::Undefined
) {
3166 if (target
== LOCAL_GL_ELEMENT_ARRAY_BUFFER
) {
3167 buffer
->mKind
= webgl::BufferKind::Index
;
3169 buffer
->mKind
= webgl::BufferKind::NonIndex
;
3176 Run
<RPROC(BindBuffer
)>(target
, buffer
? buffer
->mId
: 0);
3181 void ClientWebGLContext::BindBufferRangeImpl(const GLenum target
,
3183 WebGLBufferJS
* const buffer
,
3184 const uint64_t offset
,
3185 const uint64_t size
) {
3186 if (buffer
&& !buffer
->ValidateUsable(*this, "buffer")) return;
3187 auto& state
= State();
3191 const auto& limits
= Limits();
3193 CheckBindBufferRange(target
, index
, bool(buffer
), offset
, size
, limits
);
3195 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3201 auto kind
= webgl::BufferKind::Undefined
;
3203 kind
= buffer
->mKind
;
3205 err
= ValidateBindBuffer(target
, kind
);
3207 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3211 if (target
== LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
) {
3212 if (state
.mTfActiveAndNotPaused
) {
3213 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3214 "Cannot change TRANSFORM_FEEDBACK_BUFFER while "
3215 "TransformFeedback is active and not paused.");
3221 // Validation complete
3223 if (buffer
&& buffer
->mKind
== webgl::BufferKind::Undefined
) {
3224 buffer
->mKind
= webgl::BufferKind::NonIndex
;
3230 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
:
3231 state
.mBoundTfo
->mAttribBuffers
[index
] = buffer
;
3234 case LOCAL_GL_UNIFORM_BUFFER
:
3235 state
.mBoundUbos
[index
] = buffer
;
3239 MOZ_CRASH("Bad `target`");
3241 state
.mBoundBufferByTarget
[target
] = buffer
;
3245 Run
<RPROC(BindBufferRange
)>(target
, index
, buffer
? buffer
->mId
: 0, offset
,
3249 void ClientWebGLContext::GetBufferSubData(GLenum target
, GLintptr srcByteOffset
,
3250 const dom::ArrayBufferView
& dstData
,
3251 GLuint dstElemOffset
,
3252 GLuint dstElemCountOverride
) {
3253 const FuncScope
funcScope(*this, "getBufferSubData");
3254 if (IsContextLost()) return;
3255 const auto notLost
=
3256 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
3257 if (!ValidateNonNegative("srcByteOffset", srcByteOffset
)) return;
3261 if (!ValidateArrayBufferView(dstData
, dstElemOffset
, dstElemCountOverride
,
3262 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
3265 const auto destView
= Range
<uint8_t>{bytes
, byteLen
};
3267 const auto& inProcessContext
= notLost
->inProcess
;
3268 if (inProcessContext
) {
3269 inProcessContext
->GetBufferSubData(target
, srcByteOffset
, destView
);
3273 const auto& child
= notLost
->outOfProcess
;
3274 child
->FlushPendingCmds();
3275 mozilla::ipc::Shmem rawShmem
;
3276 if (!child
->SendGetBufferSubData(target
, srcByteOffset
, destView
.length(),
3280 const webgl::RaiiShmem shmem
{child
, rawShmem
};
3282 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "Failed to map in sub data buffer.");
3286 const auto shmemView
= shmem
.ByteRange();
3287 MOZ_RELEASE_ASSERT(shmemView
.length() == 1 + destView
.length());
3289 const auto ok
= bool(*(shmemView
.begin().get()));
3290 const auto srcView
=
3291 Range
<const uint8_t>{shmemView
.begin() + 1, shmemView
.end()};
3293 Memcpy(destView
.begin(), srcView
.begin(), srcView
.length());
3299 void ClientWebGLContext::BufferData(GLenum target
, WebGLsizeiptr rawSize
,
3301 const FuncScope
funcScope(*this, "bufferData");
3302 if (!ValidateNonNegative("size", rawSize
)) return;
3304 const auto size
= MaybeAs
<size_t>(rawSize
);
3306 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "`size` too large for platform.");
3310 const auto data
= RawBuffer
<>{*size
};
3311 Run
<RPROC(BufferData
)>(target
, data
, usage
);
3314 void ClientWebGLContext::BufferData(
3315 GLenum target
, const dom::Nullable
<dom::ArrayBuffer
>& maybeSrc
,
3317 const FuncScope
funcScope(*this, "bufferData");
3318 if (!ValidateNonNull("src", maybeSrc
)) return;
3319 const auto& src
= maybeSrc
.Value();
3322 const auto range
= Range
<const uint8_t>{src
.Data(), src
.Length()};
3323 Run
<RPROC(BufferData
)>(target
, RawBuffer
<>(range
), usage
);
3326 void ClientWebGLContext::BufferData(GLenum target
,
3327 const dom::ArrayBufferView
& src
,
3328 GLenum usage
, GLuint srcElemOffset
,
3329 GLuint srcElemCountOverride
) {
3330 const FuncScope
funcScope(*this, "bufferData");
3333 if (!ValidateArrayBufferView(src
, srcElemOffset
, srcElemCountOverride
,
3334 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
3337 const auto range
= Range
<const uint8_t>{bytes
, byteLen
};
3338 Run
<RPROC(BufferData
)>(target
, RawBuffer
<>(range
), usage
);
3341 void ClientWebGLContext::RawBufferData(GLenum target
, const uint8_t* srcBytes
,
3342 size_t srcLen
, GLenum usage
) {
3343 const FuncScope
funcScope(*this, "bufferData");
3345 const auto srcBuffer
=
3346 srcBytes
? RawBuffer
<>({srcBytes
, srcLen
}) : RawBuffer
<>(srcLen
);
3347 Run
<RPROC(BufferData
)>(target
, srcBuffer
, usage
);
3352 void ClientWebGLContext::BufferSubData(GLenum target
,
3353 WebGLsizeiptr dstByteOffset
,
3354 const dom::ArrayBuffer
& src
) {
3355 const FuncScope
funcScope(*this, "bufferSubData");
3357 const auto range
= Range
<const uint8_t>{src
.Data(), src
.Length()};
3358 Run
<RPROC(BufferSubData
)>(target
, dstByteOffset
, RawBuffer
<>(range
));
3361 void ClientWebGLContext::BufferSubData(GLenum target
,
3362 WebGLsizeiptr dstByteOffset
,
3363 const dom::ArrayBufferView
& src
,
3364 GLuint srcElemOffset
,
3365 GLuint srcElemCountOverride
) {
3366 const FuncScope
funcScope(*this, "bufferSubData");
3369 if (!ValidateArrayBufferView(src
, srcElemOffset
, srcElemCountOverride
,
3370 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
3373 const auto range
= Range
<const uint8_t>{bytes
, byteLen
};
3374 Run
<RPROC(BufferSubData
)>(target
, dstByteOffset
, RawBuffer
<>(range
));
3377 void ClientWebGLContext::CopyBufferSubData(GLenum readTarget
,
3379 GLintptr readOffset
,
3380 GLintptr writeOffset
,
3382 const FuncScope
funcScope(*this, "copyBufferSubData");
3383 if (!ValidateNonNegative("readOffset", readOffset
) ||
3384 !ValidateNonNegative("writeOffset", writeOffset
) ||
3385 !ValidateNonNegative("size", size
)) {
3388 Run
<RPROC(CopyBufferSubData
)>(
3389 readTarget
, writeTarget
, static_cast<uint64_t>(readOffset
),
3390 static_cast<uint64_t>(writeOffset
), static_cast<uint64_t>(size
));
3393 // -------------------------- Framebuffer Objects --------------------------
3395 void ClientWebGLContext::BindFramebuffer(const GLenum target
,
3396 WebGLFramebufferJS
* const fb
) {
3397 const FuncScope
funcScope(*this, "bindFramebuffer");
3398 if (IsContextLost()) return;
3399 if (fb
&& !fb
->ValidateUsable(*this, "fb")) return;
3401 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
3402 EnqueueError_ArgEnum("target", target
);
3408 auto& state
= State();
3411 case LOCAL_GL_FRAMEBUFFER
:
3412 state
.mBoundDrawFb
= fb
;
3413 state
.mBoundReadFb
= fb
;
3416 case LOCAL_GL_DRAW_FRAMEBUFFER
:
3417 state
.mBoundDrawFb
= fb
;
3419 case LOCAL_GL_READ_FRAMEBUFFER
:
3420 state
.mBoundReadFb
= fb
;
3430 fb
->mHasBeenBound
= true;
3433 Run
<RPROC(BindFramebuffer
)>(target
, fb
? fb
->mId
: 0);
3438 void ClientWebGLContext::FramebufferTexture2D(GLenum target
, GLenum attachSlot
,
3439 GLenum bindImageTarget
,
3440 WebGLTextureJS
* const tex
,
3441 GLint mipLevel
) const {
3442 const FuncScope
funcScope(*this, "framebufferTexture2D");
3443 if (IsContextLost()) return;
3445 const auto bindTexTarget
= ImageToTexTarget(bindImageTarget
);
3446 uint32_t zLayer
= 0;
3447 switch (bindTexTarget
) {
3448 case LOCAL_GL_TEXTURE_2D
:
3450 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3451 zLayer
= bindImageTarget
- LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
;
3454 EnqueueError_ArgEnum("imageTarget", bindImageTarget
);
3459 !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap
)) {
3460 if (mipLevel
!= 0) {
3461 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3462 "mipLevel != 0 requires OES_fbo_render_mipmap.");
3467 FramebufferAttach(target
, attachSlot
, bindImageTarget
, nullptr, tex
,
3468 static_cast<uint32_t>(mipLevel
), zLayer
, 0);
3471 Maybe
<webgl::ErrorInfo
> CheckFramebufferAttach(const GLenum bindImageTarget
,
3472 const GLenum curTexTarget
,
3473 const uint32_t mipLevel
,
3474 const uint32_t zLayerBase
,
3475 const uint32_t zLayerCount
,
3476 const webgl::Limits
& limits
) {
3477 if (!curTexTarget
) {
3479 webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3480 "`tex` not yet bound. Call bindTexture first."});
3483 auto texTarget
= curTexTarget
;
3484 if (bindImageTarget
) {
3485 // FramebufferTexture2D
3486 const auto bindTexTarget
= ImageToTexTarget(bindImageTarget
);
3487 if (curTexTarget
!= bindTexTarget
) {
3488 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3489 "`tex` cannot be rebound to a new target."});
3492 switch (bindTexTarget
) {
3493 case LOCAL_GL_TEXTURE_2D
:
3494 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3497 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_ENUM
,
3498 "`tex` must have been bound to target "
3499 "TEXTURE_2D or TEXTURE_CUBE_MAP."});
3501 texTarget
= bindTexTarget
;
3503 // FramebufferTextureLayer/Multiview
3504 switch (curTexTarget
) {
3505 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3506 case LOCAL_GL_TEXTURE_3D
:
3509 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3510 "`tex` must have been bound to target "
3511 "TEXTURE_2D_ARRAY or TEXTURE_3D."});
3514 MOZ_ASSERT(texTarget
);
3517 switch (texTarget
) {
3518 case LOCAL_GL_TEXTURE_2D
:
3519 maxSize
= limits
.maxTex2dSize
;
3522 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3523 maxSize
= limits
.maxTexCubeSize
;
3526 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3527 maxSize
= limits
.maxTex2dSize
;
3528 maxZ
= limits
.maxTexArrayLayers
;
3530 case LOCAL_GL_TEXTURE_3D
:
3531 maxSize
= limits
.maxTex3dSize
;
3532 maxZ
= limits
.maxTex3dSize
;
3537 const auto maxMipLevel
= FloorLog2(maxSize
);
3538 if (mipLevel
> maxMipLevel
) {
3539 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
,
3540 "`mipLevel` too large for texture target."});
3542 const auto requiredZLayers
= CheckedInt
<uint32_t>(zLayerBase
) + zLayerCount
;
3543 if (!requiredZLayers
.isValid() || requiredZLayers
.value() > maxZ
) {
3544 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
,
3545 "`zLayer` too large for texture target."});
3551 void ClientWebGLContext::FramebufferAttach(
3552 const GLenum target
, const GLenum attachSlot
, const GLenum bindImageTarget
,
3553 WebGLRenderbufferJS
* const rb
, WebGLTextureJS
* const tex
,
3554 const uint32_t mipLevel
, const uint32_t zLayerBase
,
3555 const uint32_t numViewLayers
) const {
3556 if (rb
&& !rb
->ValidateUsable(*this, "rb")) return;
3557 if (tex
&& !tex
->ValidateUsable(*this, "tex")) return;
3558 const auto& state
= State();
3559 const auto& limits
= Limits();
3561 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
3562 EnqueueError_ArgEnum("target", target
);
3565 auto fb
= state
.mBoundDrawFb
;
3566 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
3567 fb
= state
.mBoundReadFb
;
3570 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No framebuffer bound.");
3576 LOCAL_GL_INVALID_OPERATION
,
3577 "An opaque framebuffer's attachments cannot be inspected or changed.");
3582 // Multiview-specific validation skipped by Host.
3584 if (tex
&& numViewLayers
) {
3585 if (tex
->mTarget
!= LOCAL_GL_TEXTURE_2D_ARRAY
) {
3586 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3587 "`tex` must have been bound to target TEXTURE_2D_ARRAY.");
3590 if (numViewLayers
> limits
.maxMultiviewLayers
) {
3591 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3592 "`numViews` (%u) must be <= MAX_VIEWS (%u).", numViewLayers
,
3593 limits
.maxMultiviewLayers
);
3600 webgl::ObjectId id
= 0;
3602 auto zLayerCount
= numViewLayers
;
3607 CheckFramebufferAttach(bindImageTarget
, tex
->mTarget
, mipLevel
,
3608 zLayerBase
, zLayerCount
, limits
);
3610 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3615 if (!rb
->mHasBeenBound
) {
3616 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3617 "`rb` has not yet been bound with BindRenderbuffer.");
3624 // But DEPTH_STENCIL in webgl2 is actually two slots!
3626 const auto fnAttachTo
= [&](const GLenum actualAttachSlot
) {
3627 const auto slot
= fb
->GetAttachment(actualAttachSlot
);
3629 EnqueueError_ArgEnum("attachment", actualAttachSlot
);
3636 Run
<RPROC(FramebufferAttach
)>(target
, actualAttachSlot
, bindImageTarget
, id
,
3637 mipLevel
, zLayerBase
, numViewLayers
);
3640 if (mIsWebGL2
&& attachSlot
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
3641 fnAttachTo(LOCAL_GL_DEPTH_ATTACHMENT
);
3642 fnAttachTo(LOCAL_GL_STENCIL_ATTACHMENT
);
3644 fnAttachTo(attachSlot
);
3647 if (bindImageTarget
) {
3649 rb
->mHasBeenBound
= true;
3652 tex
->mTarget
= ImageToTexTarget(bindImageTarget
);
3659 void ClientWebGLContext::BlitFramebuffer(GLint srcX0
, GLint srcY0
, GLint srcX1
,
3660 GLint srcY1
, GLint dstX0
, GLint dstY0
,
3661 GLint dstX1
, GLint dstY1
,
3662 GLbitfield mask
, GLenum filter
) {
3663 Run
<RPROC(BlitFramebuffer
)>(srcX0
, srcY0
, srcX1
, srcY1
, dstX0
, dstY0
, dstX1
,
3664 dstY1
, mask
, filter
);
3669 void ClientWebGLContext::InvalidateFramebuffer(
3670 GLenum target
, const dom::Sequence
<GLenum
>& attachments
,
3671 ErrorResult
& unused
) {
3672 const auto range
= MakeRange(attachments
);
3673 const auto& buffer
= RawBufferView(range
);
3674 Run
<RPROC(InvalidateFramebuffer
)>(target
, buffer
);
3676 // Never invalidate the backbuffer, so never needs AfterDrawCall.
3679 void ClientWebGLContext::InvalidateSubFramebuffer(
3680 GLenum target
, const dom::Sequence
<GLenum
>& attachments
, GLint x
, GLint y
,
3681 GLsizei width
, GLsizei height
, ErrorResult
& unused
) {
3682 const auto range
= MakeRange(attachments
);
3683 const auto& buffer
= RawBufferView(range
);
3684 Run
<RPROC(InvalidateSubFramebuffer
)>(target
, buffer
, x
, y
, width
, height
);
3686 // Never invalidate the backbuffer, so never needs AfterDrawCall.
3689 void ClientWebGLContext::ReadBuffer(GLenum mode
) {
3690 Run
<RPROC(ReadBuffer
)>(mode
);
3693 // ----------------------- Renderbuffer objects -----------------------
3695 void ClientWebGLContext::BindRenderbuffer(const GLenum target
,
3696 WebGLRenderbufferJS
* const rb
) {
3697 const FuncScope
funcScope(*this, "bindRenderbuffer");
3698 if (IsContextLost()) return;
3699 if (rb
&& !rb
->ValidateUsable(*this, "rb")) return;
3700 auto& state
= State();
3702 if (target
!= LOCAL_GL_RENDERBUFFER
) {
3703 EnqueueError_ArgEnum("target", target
);
3707 state
.mBoundRb
= rb
;
3709 rb
->mHasBeenBound
= true;
3713 void ClientWebGLContext::RenderbufferStorageMultisample(GLenum target
,
3715 GLenum internalFormat
,
3717 GLsizei height
) const {
3718 const FuncScope
funcScope(*this, "renderbufferStorageMultisample");
3719 if (IsContextLost()) return;
3721 if (target
!= LOCAL_GL_RENDERBUFFER
) {
3722 EnqueueError_ArgEnum("target", target
);
3726 const auto& state
= State();
3728 const auto& rb
= state
.mBoundRb
;
3730 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No renderbuffer bound");
3734 if (!ValidateNonNegative("width", width
) ||
3735 !ValidateNonNegative("height", height
) ||
3736 !ValidateNonNegative("samples", samples
)) {
3740 if (internalFormat
== LOCAL_GL_DEPTH_STENCIL
&& samples
> 0) {
3741 // While our backend supports it trivially, the spec forbids it.
3742 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3743 "WebGL 1's DEPTH_STENCIL format may not be multisampled. Use "
3744 "DEPTH24_STENCIL8 when `samples > 0`.");
3748 Run
<RPROC(RenderbufferStorageMultisample
)>(
3749 rb
->mId
, static_cast<uint32_t>(samples
), internalFormat
,
3750 static_cast<uint32_t>(width
), static_cast<uint32_t>(height
));
3753 // --------------------------- Texture objects ---------------------------
3755 void ClientWebGLContext::ActiveTexture(const GLenum texUnitEnum
) {
3756 const FuncScope
funcScope(*this, "activeTexture");
3757 if (IsContextLost()) return;
3759 if (texUnitEnum
< LOCAL_GL_TEXTURE0
) {
3760 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3761 "`texture` (0x%04x) must be >= TEXTURE0 (0x%04x).",
3762 texUnitEnum
, LOCAL_GL_TEXTURE0
);
3766 const auto texUnit
= texUnitEnum
- LOCAL_GL_TEXTURE0
;
3768 auto& state
= State();
3769 if (texUnit
>= state
.mTexUnits
.size()) {
3770 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3771 "TEXTURE%u must be < MAX_COMBINED_TEXTURE_IMAGE_UNITS (%zu).",
3772 texUnit
, state
.mTexUnits
.size());
3778 state
.mActiveTexUnit
= texUnit
;
3779 Run
<RPROC(ActiveTexture
)>(texUnit
);
3782 static bool IsTexTarget(const GLenum texTarget
, const bool webgl2
) {
3783 switch (texTarget
) {
3784 case LOCAL_GL_TEXTURE_2D
:
3785 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3788 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3789 case LOCAL_GL_TEXTURE_3D
:
3797 void ClientWebGLContext::BindTexture(const GLenum texTarget
,
3798 WebGLTextureJS
* const tex
) {
3799 const FuncScope
funcScope(*this, "bindTexture");
3800 if (IsContextLost()) return;
3801 if (tex
&& !tex
->ValidateUsable(*this, "tex")) return;
3803 if (!IsTexTarget(texTarget
, mIsWebGL2
)) {
3804 EnqueueError_ArgEnum("texTarget", texTarget
);
3808 if (tex
&& tex
->mTarget
) {
3809 if (texTarget
!= tex
->mTarget
) {
3810 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3811 "Texture previously bound to %s cannot be bound now to %s.",
3812 EnumString(tex
->mTarget
).c_str(),
3813 EnumString(texTarget
).c_str());
3818 auto& state
= State();
3819 auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
3820 texUnit
.texByTarget
[texTarget
] = tex
;
3822 tex
->mTarget
= texTarget
;
3825 Run
<RPROC(BindTexture
)>(texTarget
, tex
? tex
->mId
: 0);
3828 void ClientWebGLContext::GenerateMipmap(GLenum texTarget
) const {
3829 Run
<RPROC(GenerateMipmap
)>(texTarget
);
3832 void ClientWebGLContext::GetTexParameter(
3833 JSContext
* cx
, GLenum texTarget
, GLenum pname
,
3834 JS::MutableHandle
<JS::Value
> retval
) const {
3835 retval
.set(JS::NullValue());
3836 const FuncScope
funcScope(*this, "getTexParameter");
3837 if (IsContextLost()) return;
3838 auto& state
= State();
3840 auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
3842 const auto& tex
= Find(texUnit
.texByTarget
, texTarget
, nullptr);
3844 if (!IsTexTarget(texTarget
, mIsWebGL2
)) {
3845 EnqueueError_ArgEnum("texTarget", texTarget
);
3847 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No texture bound to %s[%u].",
3848 EnumString(texTarget
).c_str(), state
.mActiveTexUnit
);
3853 const auto maybe
= [&]() {
3854 const auto& inProcess
= mNotLost
->inProcess
;
3856 return inProcess
->GetTexParameter(tex
->mId
, pname
);
3858 const auto& child
= mNotLost
->outOfProcess
;
3859 child
->FlushPendingCmds();
3861 if (!child
->SendGetTexParameter(tex
->mId
, pname
, &ret
)) {
3869 case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT
:
3870 retval
.set(JS::BooleanValue(*maybe
));
3874 retval
.set(JS::NumberValue(*maybe
));
3880 void ClientWebGLContext::TexParameterf(GLenum texTarget
, GLenum pname
,
3882 Run
<RPROC(TexParameter_base
)>(texTarget
, pname
, FloatOrInt(param
));
3885 void ClientWebGLContext::TexParameteri(GLenum texTarget
, GLenum pname
,
3887 Run
<RPROC(TexParameter_base
)>(texTarget
, pname
, FloatOrInt(param
));
3890 ////////////////////////////////////
3892 static GLenum
JSTypeMatchUnpackTypeError(GLenum unpackType
,
3893 js::Scalar::Type jsType
) {
3894 bool matches
= false;
3895 switch (unpackType
) {
3897 matches
= (jsType
== js::Scalar::Type::Int8
);
3900 case LOCAL_GL_UNSIGNED_BYTE
:
3901 matches
= (jsType
== js::Scalar::Type::Uint8
||
3902 jsType
== js::Scalar::Type::Uint8Clamped
);
3905 case LOCAL_GL_SHORT
:
3906 matches
= (jsType
== js::Scalar::Type::Int16
);
3909 case LOCAL_GL_UNSIGNED_SHORT
:
3910 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
3911 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
3912 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
3913 case LOCAL_GL_HALF_FLOAT
:
3914 case LOCAL_GL_HALF_FLOAT_OES
:
3915 matches
= (jsType
== js::Scalar::Type::Uint16
);
3919 matches
= (jsType
== js::Scalar::Type::Int32
);
3922 case LOCAL_GL_UNSIGNED_INT
:
3923 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
:
3924 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
3925 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV
:
3926 case LOCAL_GL_UNSIGNED_INT_24_8
:
3927 matches
= (jsType
== js::Scalar::Type::Uint32
);
3930 case LOCAL_GL_FLOAT
:
3931 matches
= (jsType
== js::Scalar::Type::Float32
);
3934 case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV
:
3935 matches
= false; // No valid jsType, but we allow uploads with null.
3939 return LOCAL_GL_INVALID_ENUM
;
3941 if (!matches
) return LOCAL_GL_INVALID_OPERATION
;
3945 /////////////////////////////////////////////////
3947 static inline uvec2
CastUvec2(const ivec2
& val
) {
3948 return {static_cast<uint32_t>(val
.x
), static_cast<uint32_t>(val
.y
)};
3951 static inline uvec3
CastUvec3(const ivec3
& val
) {
3952 return {static_cast<uint32_t>(val
.x
), static_cast<uint32_t>(val
.y
),
3953 static_cast<uint32_t>(val
.z
)};
3956 template <typename T
>
3957 Range
<T
> SubRange(const Range
<T
>& full
, const size_t offset
,
3958 const size_t length
) {
3959 const auto newBegin
= full
.begin() + offset
;
3960 return Range
<T
>{newBegin
, newBegin
+ length
};
3963 static inline size_t SizeOfViewElem(const dom::ArrayBufferView
& view
) {
3964 const auto& elemType
= view
.Type();
3965 if (elemType
== js::Scalar::MaxTypedArrayViewType
) // DataViews.
3968 return js::Scalar::byteSize(elemType
);
3971 Maybe
<Range
<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView
& view
,
3973 GLuint elemCountOverride
) {
3974 const auto byteRange
= MakeRangeAbv(view
); // In bytes.
3975 const auto bytesPerElem
= SizeOfViewElem(view
);
3977 auto elemCount
= byteRange
.length() / bytesPerElem
;
3978 if (elemOffset
> elemCount
) return {};
3979 elemCount
-= elemOffset
;
3981 if (elemCountOverride
) {
3982 if (elemCountOverride
> elemCount
) return {};
3983 elemCount
= elemCountOverride
;
3985 const auto subrange
=
3986 SubRange(byteRange
, elemOffset
* bytesPerElem
, elemCount
* bytesPerElem
);
3987 return Some(subrange
);
3992 static bool IsTexTargetForDims(const GLenum texTarget
, const bool webgl2
,
3993 const uint8_t funcDims
) {
3994 if (!IsTexTarget(texTarget
, webgl2
)) return false;
3995 switch (texTarget
) {
3996 case LOCAL_GL_TEXTURE_2D
:
3997 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3998 return funcDims
== 2;
4001 return funcDims
== 3;
4005 void ClientWebGLContext::TexStorage(uint8_t funcDims
, GLenum texTarget
,
4006 GLsizei levels
, GLenum internalFormat
,
4007 const ivec3
& size
) const {
4008 const FuncScope
funcScope(*this, "texStorage[23]D");
4009 if (IsContextLost()) return;
4010 if (!IsTexTargetForDims(texTarget
, mIsWebGL2
, funcDims
)) {
4011 EnqueueError_ArgEnum("texTarget", texTarget
);
4014 Run
<RPROC(TexStorage
)>(texTarget
, static_cast<uint32_t>(levels
),
4015 internalFormat
, CastUvec3(size
));
4019 // TODO: Move these definitions into statics here.
4020 Maybe
<webgl::TexUnpackBlobDesc
> FromImageBitmap(
4021 GLenum target
, Maybe
<uvec3
> size
, const dom::ImageBitmap
& imageBitmap
,
4022 ErrorResult
* const out_rv
);
4024 Maybe
<webgl::TexUnpackBlobDesc
> FromOffscreenCanvas(
4025 const ClientWebGLContext
&, GLenum target
, Maybe
<uvec3
> size
,
4026 const dom::OffscreenCanvas
& src
, ErrorResult
* const out_error
);
4028 Maybe
<webgl::TexUnpackBlobDesc
> FromDomElem(const ClientWebGLContext
&,
4029 GLenum target
, Maybe
<uvec3
> size
,
4030 const dom::Element
& src
,
4031 ErrorResult
* const out_error
);
4032 } // namespace webgl
4036 void webgl::TexUnpackBlobDesc::Shrink(const webgl::PackingInfo
& pi
) {
4038 if (!size
.x
|| !size
.y
|| !size
.z
) return;
4040 const auto unpackRes
= ExplicitUnpacking(pi
, {});
4041 if (!unpackRes
.isOk()) {
4044 const auto& unpack
= unpackRes
.inspect();
4046 const auto bytesUpperBound
=
4047 CheckedInt
<size_t>(unpack
.metrics
.bytesPerRowStride
) *
4048 unpack
.metrics
.totalRows
;
4049 if (bytesUpperBound
.isValid()) {
4050 cpuData
->Shrink(bytesUpperBound
.value());
4057 void ClientWebGLContext::TexImage(uint8_t funcDims
, GLenum imageTarget
,
4058 GLint level
, GLenum respecFormat
,
4059 const ivec3
& offset
,
4060 const Maybe
<ivec3
>& isize
, GLint border
,
4061 const webgl::PackingInfo
& pi
,
4062 const TexImageSource
& src
) const {
4063 const FuncScope
funcScope(*this, "tex(Sub)Image[23]D");
4064 if (IsContextLost()) return;
4065 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4066 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4070 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4076 size
= Some(CastUvec3(isize
.value()));
4081 // Demarcate the region within which GC is disallowed. Typed arrays can move
4082 // their data during a GC, so this will allow the rooting hazard analysis to
4083 // report if a GC is possible while any data pointers extracted from the
4084 // typed array are still live.
4085 dom::Uint8ClampedArray scopedArr
;
4086 const auto reset
= MakeScopeExit([&] {
4087 scopedArr
.Reset(); // (For the hazard analysis) Done with the data.
4091 bool isDataUpload
= false;
4092 auto desc
= [&]() -> Maybe
<webgl::TexUnpackBlobDesc
> {
4093 if (src
.mPboOffset
) {
4094 isDataUpload
= true;
4095 const auto offset
= static_cast<uint64_t>(*src
.mPboOffset
);
4096 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
4098 gfxAlphaType::NonPremult
,
4104 isDataUpload
= true;
4105 const auto& view
= *src
.mView
;
4106 const auto& jsType
= view
.Type();
4107 const auto err
= JSTypeMatchUnpackTypeError(pi
.type
, jsType
);
4109 case LOCAL_GL_INVALID_ENUM
:
4110 EnqueueError_ArgEnum("unpackType", pi
.type
);
4112 case LOCAL_GL_INVALID_OPERATION
:
4113 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4114 "ArrayBufferView type %s not compatible with `type` %s.",
4115 name(jsType
), EnumString(pi
.type
).c_str());
4121 const auto range
= GetRangeFromView(view
, src
.mViewElemOffset
,
4122 src
.mViewElemLengthOverride
);
4124 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`source` too small.");
4127 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
4129 gfxAlphaType::NonPremult
,
4130 Some(RawBuffer
<>{*range
}),
4134 if (src
.mImageBitmap
) {
4135 return webgl::FromImageBitmap(imageTarget
, size
, *(src
.mImageBitmap
),
4139 if (src
.mImageData
) {
4140 const auto& imageData
= *src
.mImageData
;
4141 MOZ_RELEASE_ASSERT(scopedArr
.Init(imageData
.GetDataObject()));
4142 scopedArr
.ComputeState();
4143 const auto dataSize
= scopedArr
.Length();
4144 const auto data
= reinterpret_cast<uint8_t*>(scopedArr
.Data());
4146 // Neutered, e.g. via Transfer
4147 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4148 "ImageData.data.buffer is Detached. (Maybe you Transfered "
4155 const gfx::IntSize
imageSize(imageData
.Width(), imageData
.Height());
4156 const auto sizeFromDims
=
4157 CheckedInt
<size_t>(imageSize
.width
) * imageSize
.height
* 4;
4158 MOZ_RELEASE_ASSERT(sizeFromDims
.isValid() &&
4159 sizeFromDims
.value() == dataSize
);
4161 const RefPtr
<gfx::DataSourceSurface
> surf
=
4162 gfx::Factory::CreateWrappingDataSourceSurface(
4163 data
, imageSize
.width
* 4, imageSize
,
4164 gfx::SurfaceFormat::R8G8B8A8
);
4169 const auto imageUSize
= *uvec2::FromSize(imageSize
);
4170 const auto concreteSize
=
4171 size
.valueOr(uvec3
{imageUSize
.x
, imageUSize
.y
, 1});
4173 // WhatWG "HTML Living Standard" (30 October 2015):
4174 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned
4175 // as non-premultiplied alpha values."
4176 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
4178 gfxAlphaType::NonPremult
,
4187 if (src
.mOffscreenCanvas
) {
4188 return webgl::FromOffscreenCanvas(
4189 *this, imageTarget
, size
, *(src
.mOffscreenCanvas
), src
.mOut_error
);
4193 return webgl::FromDomElem(*this, imageTarget
, size
, *(src
.mDomElem
),
4197 return Some(webgl::TexUnpackBlobDesc
{
4198 imageTarget
, size
.value(), gfxAlphaType::NonPremult
, {}, {}});
4206 const auto& rawUnpacking
= State().mPixelUnpackState
;
4208 auto defaultSubrectState
= webgl::PixelPackingState
{};
4209 defaultSubrectState
.alignmentInTypeElems
=
4210 rawUnpacking
.alignmentInTypeElems
;
4211 const bool isSubrect
= (rawUnpacking
!= defaultSubrectState
);
4212 if (isDataUpload
&& isSubrect
) {
4213 if (rawUnpacking
.flipY
|| rawUnpacking
.premultiplyAlpha
) {
4214 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4215 "Non-DOM-Element uploads with alpha-premult"
4216 " or y-flip do not support subrect selection.");
4221 desc
->unpacking
= rawUnpacking
;
4223 if (desc
->structuredSrcSize
) {
4225 // ### 5.35 Pixel store parameters for uploads from TexImageSource
4226 // UNPACK_ALIGNMENT and UNPACK_ROW_LENGTH are ignored.
4227 const auto& elemSize
= *desc
->structuredSrcSize
;
4228 desc
->unpacking
.alignmentInTypeElems
= 1;
4229 desc
->unpacking
.rowLength
= elemSize
.x
;
4231 if (!desc
->unpacking
.rowLength
) {
4232 desc
->unpacking
.rowLength
= desc
->size
.x
;
4234 if (!desc
->unpacking
.imageHeight
) {
4235 desc
->unpacking
.imageHeight
= desc
->size
.y
;
4240 mozilla::ipc::Shmem
* pShmem
= nullptr;
4243 const auto& sd
= *(desc
->sd
);
4244 const auto sdType
= sd
.type();
4245 const auto& contextInfo
= mNotLost
->info
;
4247 const auto fallbackReason
= [&]() -> Maybe
<std::string
> {
4248 auto fallbackReason
= BlitPreventReason(level
, offset
, pi
, *desc
);
4249 if (fallbackReason
) return fallbackReason
;
4251 const bool canUploadViaSd
= contextInfo
.uploadableSdTypes
[sdType
];
4252 if (!canUploadViaSd
) {
4253 const nsPrintfCString
msg(
4254 "Fast uploads for resource type %i not implemented.", int(sdType
));
4255 return Some(ToString(msg
));
4258 if (sdType
== layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
) {
4259 const auto& sdb
= sd
.get_SurfaceDescriptorBuffer();
4260 const auto& data
= sdb
.data();
4261 if (data
.type() == layers::MemoryOrShmem::TShmem
) {
4262 pShmem
= &data
.get_Shmem();
4265 std::string
{"SurfaceDescriptorBuffer data is not Shmem."});
4269 if (sdType
== layers::SurfaceDescriptor::TSurfaceDescriptorD3D10
) {
4270 const auto& sdD3D
= sd
.get_SurfaceDescriptorD3D10();
4271 const auto& inProcess
= mNotLost
->inProcess
;
4272 if (sdD3D
.gpuProcessTextureId().isSome() && inProcess
) {
4274 std::string
{"gpuProcessTextureId works only in GPU process."});
4278 switch (respecFormat
) {
4280 case LOCAL_GL_SRGB8
:
4281 case LOCAL_GL_SRGB_ALPHA
:
4282 case LOCAL_GL_SRGB8_ALPHA8
: {
4283 const nsPrintfCString
msg(
4284 "srgb-encoded formats (like %s) are not supported.",
4285 EnumString(respecFormat
).c_str());
4286 return Some(ToString(msg
));
4290 if (StaticPrefs::webgl_disable_DOM_blit_uploads()) {
4291 return Some(std::string
{"DOM blit uploads are disabled."});
4296 if (fallbackReason
) {
4297 EnqueuePerfWarning("Missed GPU-copy fast-path: %s",
4298 fallbackReason
->c_str());
4300 const auto& image
= desc
->image
;
4301 const RefPtr
<gfx::SourceSurface
> surf
= image
->GetAsSourceSurface();
4303 // WARNING: OSX can lose our MakeCurrent here.
4304 desc
->dataSurf
= surf
->GetDataSurface();
4306 if (!desc
->dataSurf
) {
4307 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
,
4308 "Failed to retrieve source bytes for CPU upload.");
4311 desc
->sd
= Nothing();
4314 desc
->image
= nullptr;
4320 std::shared_ptr
<webgl::RaiiShmem
> tempShmem
;
4322 const bool doInlineUpload
= !desc
->sd
;
4323 // Why always de-inline SDs here?
4324 // 1. This way we always send SDs down the same handling path, which
4325 // should keep things from breaking if things flip between paths because of
4326 // what we get handed by SurfaceFromElement etc.
4327 // 2. We don't actually always grab strong-refs to the resources in the SDs,
4328 // so we should try to use them sooner rather than later. Yes we should fix
4329 // this, but for now let's give the SDs the best chance of lucking out, eh?
4331 // 3. It means we don't need to write QueueParamTraits<SurfaceDescriptor>.
4332 if (doInlineUpload
) {
4333 // We definitely want e.g. TexImage(PBO) here.
4334 Run
<RPROC(TexImage
)>(static_cast<uint32_t>(level
), respecFormat
,
4335 CastUvec3(offset
), pi
, std::move(*desc
));
4337 // We can't handle shmems like SurfaceDescriptorBuffer inline, so use ipdl.
4338 const auto& inProcess
= mNotLost
->inProcess
;
4340 return inProcess
->TexImage(static_cast<uint32_t>(level
), respecFormat
,
4341 CastUvec3(offset
), pi
, *desc
);
4343 const auto& child
= mNotLost
->outOfProcess
;
4344 child
->FlushPendingCmds();
4346 // The shmem we're handling was only shared from RDD to Content, and
4347 // immediately on Content receiving it, it was closed! RIP
4348 // Eventually we'll be able to make shmems that can traverse multiple
4349 // endpoints, but for now we need to make a new Content->WebGLParent shmem
4350 // and memcpy into it. We don't use `desc` elsewhere, so just replace the
4351 // Shmem buried within it with one that's valid for WebGLChild->Parent
4354 MOZ_ASSERT(desc
->sd
);
4355 const auto srcBytes
= ShmemRange
<uint8_t>(*pShmem
);
4356 tempShmem
= std::make_shared
<webgl::RaiiShmem
>();
4358 // We need Unsafe because we want to dictate when to destroy it from the
4360 *tempShmem
= webgl::RaiiShmem::AllocUnsafe(child
, srcBytes
.length());
4362 NS_WARNING("AllocShmem failed in TexImage");
4365 const auto dstBytes
= ShmemRange
<uint8_t>(tempShmem
->Shmem());
4366 Memcpy(&dstBytes
, srcBytes
.begin());
4368 *pShmem
= tempShmem
->Shmem();
4369 // Not Extract, because we free tempShmem manually below, after the remote
4370 // side has finished executing SendTexImage.
4373 (void)child
->SendTexImage(static_cast<uint32_t>(level
), respecFormat
,
4374 CastUvec3(offset
), pi
, std::move(*desc
));
4377 const auto eventTarget
= GetCurrentSerialEventTarget();
4378 MOZ_ASSERT(eventTarget
);
4379 child
->SendPing()->Then(eventTarget
, __func__
, [tempShmem
]() {
4380 // Cleans up when (our copy of) sendableShmem goes out of scope.
4386 void ClientWebGLContext::RawTexImage(uint32_t level
, GLenum respecFormat
,
4387 uvec3 offset
, const webgl::PackingInfo
& pi
,
4388 webgl::TexUnpackBlobDesc
&& desc
) const {
4389 const FuncScope
funcScope(*this, "tex(Sub)Image[23]D");
4390 if (IsContextLost()) return;
4392 // Shmems are stored in Buffer surface descriptors. We need to ensure first
4393 // that all queued commands are flushed and then send the Shmem over IPDL.
4394 const auto& sd
= *(desc
.sd
);
4395 if (sd
.type() == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
&&
4396 sd
.get_SurfaceDescriptorBuffer().data().type() ==
4397 layers::MemoryOrShmem::TShmem
) {
4398 const auto& inProcess
= mNotLost
->inProcess
;
4400 inProcess
->TexImage(level
, respecFormat
, offset
, pi
, desc
);
4402 const auto& child
= mNotLost
->outOfProcess
;
4403 child
->FlushPendingCmds();
4404 (void)child
->SendTexImage(level
, respecFormat
, offset
, pi
,
4409 "RawTexImage with SurfaceDescriptor only supports "
4410 "SurfaceDescriptorBuffer with Shmem");
4415 Run
<RPROC(TexImage
)>(level
, respecFormat
, offset
, pi
, desc
);
4420 void ClientWebGLContext::CompressedTexImage(bool sub
, uint8_t funcDims
,
4421 GLenum imageTarget
, GLint level
,
4422 GLenum format
, const ivec3
& offset
,
4423 const ivec3
& isize
, GLint border
,
4424 const TexImageSource
& src
,
4425 GLsizei pboImageSize
) const {
4426 const FuncScope
funcScope(*this, "compressedTex(Sub)Image[23]D");
4427 if (IsContextLost()) return;
4428 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4429 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4433 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4438 Maybe
<uint64_t> pboOffset
;
4440 const auto maybe
= GetRangeFromView(*src
.mView
, src
.mViewElemOffset
,
4441 src
.mViewElemLengthOverride
);
4443 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`source` too small.");
4446 range
= RawBuffer
<>{*maybe
};
4447 } else if (src
.mPboOffset
) {
4448 if (!ValidateNonNegative("offset", *src
.mPboOffset
)) return;
4449 pboOffset
= Some(*src
.mPboOffset
);
4451 MOZ_CRASH("impossible");
4454 // We don't need to shrink `range` because valid calls require `range` to
4455 // match requirements exactly.
4457 Run
<RPROC(CompressedTexImage
)>(
4458 sub
, imageTarget
, static_cast<uint32_t>(level
), format
, CastUvec3(offset
),
4459 CastUvec3(isize
), range
, static_cast<uint32_t>(pboImageSize
), pboOffset
);
4462 void ClientWebGLContext::CopyTexImage(uint8_t funcDims
, GLenum imageTarget
,
4463 GLint level
, GLenum respecFormat
,
4464 const ivec3
& dstOffset
,
4465 const ivec2
& srcOffset
, const ivec2
& size
,
4466 GLint border
) const {
4467 const FuncScope
funcScope(*this, "copy(Sub)Image[23]D");
4468 if (IsContextLost()) return;
4469 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4470 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4474 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4477 Run
<RPROC(CopyTexImage
)>(imageTarget
, static_cast<uint32_t>(level
),
4478 respecFormat
, CastUvec3(dstOffset
), srcOffset
,
4482 // ------------------- Programs and shaders --------------------------------
4484 void ClientWebGLContext::UseProgram(WebGLProgramJS
* const prog
) {
4485 const FuncScope
funcScope(*this, "useProgram");
4486 if (IsContextLost()) return;
4487 if (prog
&& !prog
->ValidateUsable(*this, "prog")) return;
4489 auto& state
= State();
4491 if (state
.mTfActiveAndNotPaused
) {
4492 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4493 "Transform feedback is active and not paused.");
4498 const auto& res
= GetLinkResult(*prog
);
4500 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4501 "Program must be linked successfully.");
4508 state
.mCurrentProgram
= prog
;
4509 state
.mProgramKeepAlive
= prog
? prog
->mKeepAliveWeak
.lock() : nullptr;
4510 state
.mActiveLinkResult
= prog
? prog
->mResult
: nullptr;
4512 Run
<RPROC(UseProgram
)>(prog
? prog
->mId
: 0);
4515 void ClientWebGLContext::ValidateProgram(WebGLProgramJS
& prog
) const {
4516 const FuncScope
funcScope(*this, "validateProgram");
4517 if (IsContextLost()) return;
4518 if (!prog
.ValidateUsable(*this, "prog")) return;
4520 prog
.mLastValidate
= [&]() {
4521 const auto& inProcess
= mNotLost
->inProcess
;
4523 return inProcess
->ValidateProgram(prog
.mId
);
4525 const auto& child
= mNotLost
->outOfProcess
;
4526 child
->FlushPendingCmds();
4528 if (!child
->SendValidateProgram(prog
.mId
, &ret
)) {
4535 // ------------------------ Uniforms and attributes ------------------------
4537 Maybe
<double> ClientWebGLContext::GetVertexAttribPriv(const GLuint index
,
4538 const GLenum pname
) {
4539 const auto& inProcess
= mNotLost
->inProcess
;
4541 return inProcess
->GetVertexAttrib(index
, pname
);
4543 const auto& child
= mNotLost
->outOfProcess
;
4544 child
->FlushPendingCmds();
4546 if (!child
->SendGetVertexAttrib(index
, pname
, &ret
)) {
4552 void ClientWebGLContext::GetVertexAttrib(JSContext
* cx
, GLuint index
,
4554 JS::MutableHandle
<JS::Value
> retval
,
4556 retval
.set(JS::NullValue());
4557 const FuncScope
funcScope(*this, "getVertexAttrib");
4558 if (IsContextLost()) return;
4559 const auto& state
= State();
4561 const auto& genericAttribs
= state
.mGenericVertexAttribs
;
4562 if (index
>= genericAttribs
.size()) {
4563 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` (%u) >= MAX_VERTEX_ATTRIBS",
4569 case LOCAL_GL_CURRENT_VERTEX_ATTRIB
: {
4570 JS::Rooted
<JSObject
*> obj(cx
);
4572 const auto& attrib
= genericAttribs
[index
];
4573 switch (attrib
.type
) {
4574 case webgl::AttribBaseType::Float
:
4575 obj
= dom::Float32Array::Create(
4576 cx
, this, 4, reinterpret_cast<const float*>(attrib
.data
.data()));
4578 case webgl::AttribBaseType::Int
:
4579 obj
= dom::Int32Array::Create(
4581 reinterpret_cast<const int32_t*>(attrib
.data
.data()));
4583 case webgl::AttribBaseType::Uint
:
4584 obj
= dom::Uint32Array::Create(
4586 reinterpret_cast<const uint32_t*>(attrib
.data
.data()));
4588 case webgl::AttribBaseType::Boolean
:
4589 MOZ_CRASH("impossible");
4593 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
4596 retval
.set(JS::ObjectValue(*obj
));
4600 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
: {
4601 const auto& buffers
= state
.mBoundVao
->mAttribBuffers
;
4602 const auto& buffer
= buffers
[index
];
4603 (void)ToJSValueOrNull(cx
, buffer
, retval
);
4607 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
:
4608 // Disallowed from JS, but allowed in Host.
4609 EnqueueError_ArgEnum("pname", pname
);
4616 const auto maybe
= GetVertexAttribPriv(index
, pname
);
4619 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED
:
4620 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
:
4621 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER
:
4622 retval
.set(JS::BooleanValue(*maybe
));
4626 retval
.set(JS::NumberValue(*maybe
));
4632 void ClientWebGLContext::UniformData(const GLenum funcElemType
,
4633 const WebGLUniformLocationJS
* const loc
,
4635 const Range
<const uint8_t>& bytes
,
4637 GLuint elemCountOverride
) const {
4638 const FuncScope
funcScope(*this, "uniform setter");
4639 if (IsContextLost()) return;
4641 const auto& activeLinkResult
= GetActiveLinkResult();
4642 if (!activeLinkResult
) {
4643 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No active linked Program.");
4649 auto availCount
= bytes
.length() / sizeof(float);
4650 if (elemOffset
> availCount
) {
4651 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`elemOffset` too large for `data`.");
4654 availCount
-= elemOffset
;
4655 if (elemCountOverride
) {
4656 if (elemCountOverride
> availCount
) {
4657 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4658 "`elemCountOverride` too large for `data`.");
4661 availCount
= elemCountOverride
;
4666 const auto channels
= ElemTypeComponents(funcElemType
);
4667 if (!availCount
|| availCount
% channels
!= 0) {
4668 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4669 "`values` length (%u) must be a positive "
4670 "integer multiple of size of %s.",
4671 availCount
, EnumString(funcElemType
).c_str());
4677 uint32_t locId
= -1;
4678 if (MOZ_LIKELY(loc
)) {
4679 locId
= loc
->mLocation
;
4680 if (!loc
->ValidateUsable(*this, "location")) return;
4684 const auto& reqLinkInfo
= loc
->mParent
.lock();
4685 if (reqLinkInfo
.get() != activeLinkResult
) {
4686 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4687 "UniformLocation is not from the current active Program.");
4693 bool funcMatchesLocation
= false;
4694 for (const auto allowed
: loc
->mValidUploadElemTypes
) {
4695 funcMatchesLocation
|= (funcElemType
== allowed
);
4697 if (MOZ_UNLIKELY(!funcMatchesLocation
)) {
4698 std::string validSetters
;
4699 for (const auto allowed
: loc
->mValidUploadElemTypes
) {
4700 validSetters
+= EnumString(allowed
);
4701 validSetters
+= '/';
4703 validSetters
.pop_back(); // Cheekily discard the extra trailing '/'.
4705 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4706 "Uniform's `type` requires uniform setter of type %s.",
4707 validSetters
.c_str());
4715 reinterpret_cast<const webgl::UniformDataVal
*>(bytes
.begin().get()) +
4717 const auto range
= Range
{begin
, availCount
};
4718 Run
<RPROC(UniformData
)>(locId
, transpose
, RawBuffer
{range
});
4723 void ClientWebGLContext::BindVertexArray(WebGLVertexArrayJS
* const vao
) {
4724 const FuncScope
funcScope(*this, "bindVertexArray");
4725 if (IsContextLost()) return;
4726 if (vao
&& !vao
->ValidateUsable(*this, "vao")) return;
4727 auto& state
= State();
4730 vao
->mHasBeenBound
= true;
4731 state
.mBoundVao
= vao
;
4733 state
.mBoundVao
= state
.mDefaultVao
;
4736 Run
<RPROC(BindVertexArray
)>(vao
? vao
->mId
: 0);
4739 void ClientWebGLContext::EnableVertexAttribArray(GLuint index
) {
4740 Run
<RPROC(EnableVertexAttribArray
)>(index
);
4743 void ClientWebGLContext::DisableVertexAttribArray(GLuint index
) {
4744 Run
<RPROC(DisableVertexAttribArray
)>(index
);
4747 WebGLsizeiptr
ClientWebGLContext::GetVertexAttribOffset(GLuint index
,
4749 const FuncScope
funcScope(*this, "getVertexAttribOffset");
4750 if (IsContextLost()) return 0;
4752 if (pname
!= LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
) {
4753 EnqueueError_ArgEnum("pname", pname
);
4757 const auto maybe
= GetVertexAttribPriv(index
, pname
);
4758 if (!maybe
) return 0;
4762 void ClientWebGLContext::VertexAttrib4Tv(GLuint index
, webgl::AttribBaseType t
,
4763 const Range
<const uint8_t>& src
) {
4764 const FuncScope
funcScope(*this, "vertexAttrib[1234]u?[fi]{v}");
4765 if (IsContextLost()) return;
4766 auto& state
= State();
4768 if (src
.length() / sizeof(float) < 4) {
4769 EnqueueError(LOCAL_GL_INVALID_VALUE
, "Array must have >=4 elements.");
4773 auto& list
= state
.mGenericVertexAttribs
;
4774 if (index
>= list
.size()) {
4775 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4776 "`index` must be < MAX_VERTEX_ATTRIBS.");
4780 auto& attrib
= list
[index
];
4782 memcpy(attrib
.data
.data(), src
.begin().get(), attrib
.data
.size());
4784 Run
<RPROC(VertexAttrib4T
)>(index
, attrib
);
4789 void ClientWebGLContext::VertexAttribDivisor(GLuint index
, GLuint divisor
) {
4790 Run
<RPROC(VertexAttribDivisor
)>(index
, divisor
);
4795 void ClientWebGLContext::VertexAttribPointerImpl(bool isFuncInt
, GLuint index
,
4796 GLint rawChannels
, GLenum type
,
4798 GLsizei rawByteStrideOrZero
,
4799 WebGLintptr rawByteOffset
) {
4800 const FuncScope
funcScope(*this, "vertexAttribI?Pointer");
4801 if (IsContextLost()) return;
4802 auto& state
= State();
4804 const auto channels
= MaybeAs
<uint8_t>(rawChannels
);
4806 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4807 "Channel count `size` must be within [1,4].");
4811 const auto byteStrideOrZero
= MaybeAs
<uint8_t>(rawByteStrideOrZero
);
4812 if (!byteStrideOrZero
) {
4813 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`stride` must be within [0,255].");
4817 if (!ValidateNonNegative("byteOffset", rawByteOffset
)) return;
4818 const auto byteOffset
= static_cast<uint64_t>(rawByteOffset
);
4822 const webgl::VertAttribPointerDesc desc
{
4823 isFuncInt
, *channels
, normalized
, *byteStrideOrZero
, type
, byteOffset
};
4825 const auto res
= CheckVertexAttribPointer(mIsWebGL2
, desc
);
4827 const auto& err
= res
.inspectErr();
4828 EnqueueError(err
.type
, "%s", err
.info
.c_str());
4832 auto& list
= state
.mBoundVao
->mAttribBuffers
;
4833 if (index
>= list
.size()) {
4834 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4835 "`index` (%u) must be < MAX_VERTEX_ATTRIBS.", index
);
4839 const auto buffer
= state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
];
4840 if (!buffer
&& byteOffset
) {
4841 return EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4842 "If ARRAY_BUFFER is null, byteOffset must be zero.");
4845 Run
<RPROC(VertexAttribPointer
)>(index
, desc
);
4847 list
[index
] = buffer
;
4850 // -------------------------------- Drawing -------------------------------
4852 void ClientWebGLContext::DrawArraysInstanced(GLenum mode
, GLint first
,
4853 GLsizei count
, GLsizei primcount
) {
4854 Run
<RPROC(DrawArraysInstanced
)>(mode
, first
, count
, primcount
);
4858 void ClientWebGLContext::DrawElementsInstanced(GLenum mode
, GLsizei count
,
4859 GLenum type
, WebGLintptr offset
,
4860 GLsizei primcount
) {
4861 Run
<RPROC(DrawElementsInstanced
)>(mode
, count
, type
, offset
, primcount
);
4865 // ------------------------------ Readback -------------------------------
4866 void ClientWebGLContext::ReadPixels(GLint x
, GLint y
, GLsizei width
,
4867 GLsizei height
, GLenum format
, GLenum type
,
4868 WebGLsizeiptr offset
,
4869 dom::CallerType aCallerType
,
4870 ErrorResult
& out_error
) const {
4871 const FuncScope
funcScope(*this, "readPixels");
4872 if (!ReadPixels_SharedPrecheck(aCallerType
, out_error
)) return;
4873 const auto& state
= State();
4874 if (!ValidateNonNegative("width", width
)) return;
4875 if (!ValidateNonNegative("height", height
)) return;
4876 if (!ValidateNonNegative("offset", offset
)) return;
4878 const auto desc
= webgl::ReadPixelsDesc
{{x
, y
},
4879 *uvec2::From(width
, height
),
4881 state
.mPixelPackState
};
4882 Run
<RPROC(ReadPixelsPbo
)>(desc
, static_cast<uint64_t>(offset
));
4885 void ClientWebGLContext::ReadPixels(GLint x
, GLint y
, GLsizei width
,
4886 GLsizei height
, GLenum format
, GLenum type
,
4887 const dom::ArrayBufferView
& dstData
,
4888 GLuint dstElemOffset
,
4889 dom::CallerType aCallerType
,
4890 ErrorResult
& out_error
) const {
4891 const FuncScope
funcScope(*this, "readPixels");
4892 if (!ReadPixels_SharedPrecheck(aCallerType
, out_error
)) return;
4893 const auto& state
= State();
4894 if (!ValidateNonNegative("width", width
)) return;
4895 if (!ValidateNonNegative("height", height
)) return;
4899 js::Scalar::Type reqScalarType
;
4900 if (!GetJSScalarFromGLType(type
, &reqScalarType
)) {
4902 WebGLContext::EnumName(type
, &name
);
4903 EnqueueError(LOCAL_GL_INVALID_ENUM
, "type: invalid enum value %s",
4904 name
.BeginReading());
4908 auto viewElemType
= dstData
.Type();
4909 if (viewElemType
== js::Scalar::Uint8Clamped
) {
4910 viewElemType
= js::Scalar::Uint8
;
4912 if (viewElemType
!= reqScalarType
) {
4913 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4914 "`pixels` type does not match `type`.");
4920 if (!ValidateArrayBufferView(dstData
, dstElemOffset
, 0,
4921 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
4925 const auto desc
= webgl::ReadPixelsDesc
{{x
, y
},
4926 *uvec2::From(width
, height
),
4928 state
.mPixelPackState
};
4929 const auto range
= Range
<uint8_t>(bytes
, byteLen
);
4930 if (!DoReadPixels(desc
, range
)) {
4935 bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc
& desc
,
4936 const Range
<uint8_t> dest
) const {
4937 const auto notLost
=
4938 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
4939 if (!notLost
) return false;
4940 const auto& inProcess
= notLost
->inProcess
;
4942 inProcess
->ReadPixelsInto(desc
, dest
);
4945 const auto& child
= notLost
->outOfProcess
;
4946 child
->FlushPendingCmds();
4947 webgl::ReadPixelsResultIpc res
= {};
4948 if (!child
->SendReadPixels(desc
, dest
.length(), &res
)) {
4951 if (!res
.byteStride
|| !res
.shmem
) return false;
4952 const auto& byteStride
= res
.byteStride
;
4953 const auto& subrect
= res
.subrect
;
4954 const webgl::RaiiShmem shmem
{child
, res
.shmem
.ref()};
4956 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "Failed to map in back buffer.");
4960 const auto& shmemBytes
= shmem
.ByteRange();
4962 const auto pii
= webgl::PackingInfoInfo::For(desc
.pi
);
4964 gfxCriticalError() << "ReadPixels: Bad " << desc
.pi
;
4967 const auto bpp
= pii
->BytesPerPixel();
4969 const auto& packing
= desc
.packState
;
4970 auto packRect
= *uvec2::From(subrect
.x
, subrect
.y
);
4971 packRect
.x
+= packing
.skipPixels
;
4972 packRect
.y
+= packing
.skipRows
;
4974 const auto xByteSize
= bpp
* static_cast<uint32_t>(subrect
.width
);
4975 const ptrdiff_t byteOffset
= packRect
.y
* byteStride
+ packRect
.x
* bpp
;
4977 auto srcItr
= shmemBytes
.begin() + byteOffset
;
4978 auto destItr
= dest
.begin() + byteOffset
;
4980 for (const auto i
: IntegerRange(subrect
.height
)) {
4982 // Don't trigger an assert on the last loop by pushing a RangedPtr past
4984 srcItr
+= byteStride
;
4985 destItr
+= byteStride
;
4986 MOZ_RELEASE_ASSERT(srcItr
+ xByteSize
<= shmemBytes
.end());
4987 MOZ_RELEASE_ASSERT(destItr
+ xByteSize
<= dest
.end());
4989 Memcpy(destItr
, srcItr
, xByteSize
);
4995 bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc
& desc
,
4996 const mozilla::ipc::Shmem
& shmem
) const {
4997 const auto notLost
=
4998 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
4999 if (!notLost
) return false;
5000 const auto& inProcess
= notLost
->inProcess
;
5002 const auto& shmemBytes
= shmem
.Range
<uint8_t>();
5003 inProcess
->ReadPixelsInto(desc
, shmemBytes
);
5006 const auto& child
= notLost
->outOfProcess
;
5007 child
->FlushPendingCmds();
5008 webgl::ReadPixelsResultIpc res
= {};
5009 // We assume the input is an unsafe shmem which won't be consumed by this
5010 // request. Since SendReadPixels expects a Shmem rvalue, we must create a copy
5011 // to provide it that can be consumed instead of the original descriptor.
5012 mozilla::ipc::Shmem dest
= shmem
;
5013 if (!child
->SendReadPixels(desc
, dest
, &res
)) {
5016 return res
.byteStride
> 0;
5019 bool ClientWebGLContext::ReadPixels_SharedPrecheck(
5020 dom::CallerType aCallerType
, ErrorResult
& out_error
) const {
5021 if (IsContextLost()) return false;
5023 if (mCanvasElement
&& mCanvasElement
->IsWriteOnly() &&
5024 aCallerType
!= dom::CallerType::System
) {
5025 JsWarning("readPixels: Not allowed");
5026 out_error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
5033 // --------------------------------- GL Query ---------------------------------
5035 static inline GLenum
QuerySlotTarget(const GLenum specificTarget
) {
5036 if (specificTarget
== LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE
) {
5037 return LOCAL_GL_ANY_SAMPLES_PASSED
;
5039 return specificTarget
;
5042 void ClientWebGLContext::GetQuery(JSContext
* cx
, GLenum specificTarget
,
5044 JS::MutableHandle
<JS::Value
> retval
) const {
5045 retval
.set(JS::NullValue());
5046 const FuncScope
funcScope(*this, "getQuery");
5047 if (IsContextLost()) return;
5048 const auto& limits
= Limits();
5049 auto& state
= State();
5051 if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query
)) {
5052 if (pname
== LOCAL_GL_QUERY_COUNTER_BITS
) {
5053 switch (specificTarget
) {
5054 case LOCAL_GL_TIME_ELAPSED_EXT
:
5055 retval
.set(JS::NumberValue(limits
.queryCounterBitsTimeElapsed
));
5058 case LOCAL_GL_TIMESTAMP_EXT
:
5059 retval
.set(JS::NumberValue(limits
.queryCounterBitsTimestamp
));
5063 EnqueueError_ArgEnum("target", specificTarget
);
5069 if (pname
!= LOCAL_GL_CURRENT_QUERY
) {
5070 EnqueueError_ArgEnum("pname", pname
);
5074 const auto slotTarget
= QuerySlotTarget(specificTarget
);
5075 const auto& slot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
5077 EnqueueError_ArgEnum("target", specificTarget
);
5082 if (query
&& query
->mTarget
!= specificTarget
) {
5086 (void)ToJSValueOrNull(cx
, query
, retval
);
5089 void ClientWebGLContext::GetQueryParameter(
5090 JSContext
*, WebGLQueryJS
& query
, const GLenum pname
,
5091 JS::MutableHandle
<JS::Value
> retval
) const {
5092 retval
.set(JS::NullValue());
5093 const FuncScope
funcScope(*this, "getQueryParameter");
5094 if (IsContextLost()) return;
5095 if (!query
.ValidateUsable(*this, "query")) return;
5097 auto maybe
= [&]() {
5098 const auto& inProcess
= mNotLost
->inProcess
;
5100 return inProcess
->GetQueryParameter(query
.mId
, pname
);
5102 const auto& child
= mNotLost
->outOfProcess
;
5103 child
->FlushPendingCmds();
5105 if (!child
->SendGetQueryParameter(query
.mId
, pname
, &ret
)) {
5112 // We must usually wait for an event loop before the query can be available.
5113 const bool canBeAvailable
=
5114 (query
.mCanBeAvailable
|| StaticPrefs::webgl_allow_immediate_queries());
5115 if (!canBeAvailable
) {
5116 if (pname
!= LOCAL_GL_QUERY_RESULT_AVAILABLE
) {
5123 case LOCAL_GL_QUERY_RESULT_AVAILABLE
:
5124 retval
.set(JS::BooleanValue(*maybe
));
5128 retval
.set(JS::NumberValue(*maybe
));
5133 void ClientWebGLContext::BeginQuery(const GLenum specificTarget
,
5134 WebGLQueryJS
& query
) {
5135 const FuncScope
funcScope(*this, "beginQuery");
5136 if (IsContextLost()) return;
5137 if (!query
.ValidateUsable(*this, "query")) return;
5138 auto& state
= State();
5140 const auto slotTarget
= QuerySlotTarget(specificTarget
);
5141 const auto& slot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
5143 EnqueueError_ArgEnum("target", specificTarget
);
5148 auto enumStr
= EnumString(slotTarget
);
5149 if (slotTarget
== LOCAL_GL_ANY_SAMPLES_PASSED
) {
5150 enumStr
+= "/ANY_SAMPLES_PASSED_CONSERVATIVE";
5152 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5153 "A Query is already active for %s.", enumStr
.c_str());
5157 if (query
.mTarget
&& query
.mTarget
!= specificTarget
) {
5158 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5159 "`query` cannot be changed to a different target.");
5164 query
.mTarget
= specificTarget
;
5166 Run
<RPROC(BeginQuery
)>(specificTarget
, query
.mId
);
5169 void ClientWebGLContext::EndQuery(const GLenum specificTarget
) {
5170 const FuncScope
funcScope(*this, "endQuery");
5171 if (IsContextLost()) return;
5172 auto& state
= State();
5174 const auto slotTarget
= QuerySlotTarget(specificTarget
);
5175 const auto& maybeSlot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
5177 EnqueueError_ArgEnum("target", specificTarget
);
5180 auto& slot
= *maybeSlot
;
5181 if (!slot
|| slot
->mTarget
!= specificTarget
) {
5182 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No Query is active for %s.",
5183 EnumString(specificTarget
).c_str());
5186 const auto query
= slot
;
5189 Run
<RPROC(EndQuery
)>(specificTarget
);
5191 auto& availRunnable
= EnsureAvailabilityRunnable();
5192 availRunnable
.mQueries
.push_back(query
.get());
5193 query
->mCanBeAvailable
= false;
5196 void ClientWebGLContext::QueryCounter(WebGLQueryJS
& query
,
5197 const GLenum target
) const {
5198 const FuncScope
funcScope(*this, "queryCounter");
5199 if (IsContextLost()) return;
5200 if (!query
.ValidateUsable(*this, "query")) return;
5202 if (target
!= LOCAL_GL_TIMESTAMP
) {
5203 EnqueueError(LOCAL_GL_INVALID_ENUM
, "`target` must be TIMESTAMP.");
5207 if (query
.mTarget
&& query
.mTarget
!= target
) {
5208 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5209 "`query` cannot be changed to a different target.");
5212 query
.mTarget
= target
;
5214 Run
<RPROC(QueryCounter
)>(query
.mId
);
5216 auto& availRunnable
= EnsureAvailabilityRunnable();
5217 availRunnable
.mQueries
.push_back(&query
);
5218 query
.mCanBeAvailable
= false;
5221 // -------------------------------- Sampler -------------------------------
5222 void ClientWebGLContext::GetSamplerParameter(
5223 JSContext
* cx
, const WebGLSamplerJS
& sampler
, const GLenum pname
,
5224 JS::MutableHandle
<JS::Value
> retval
) const {
5225 retval
.set(JS::NullValue());
5226 const FuncScope
funcScope(*this, "getSamplerParameter");
5227 if (IsContextLost()) return;
5228 if (!sampler
.ValidateUsable(*this, "sampler")) return;
5230 const auto maybe
= [&]() {
5231 const auto& inProcess
= mNotLost
->inProcess
;
5233 return inProcess
->GetSamplerParameter(sampler
.mId
, pname
);
5235 const auto& child
= mNotLost
->outOfProcess
;
5236 child
->FlushPendingCmds();
5238 if (!child
->SendGetSamplerParameter(sampler
.mId
, pname
, &ret
)) {
5244 retval
.set(JS::NumberValue(*maybe
));
5248 void ClientWebGLContext::BindSampler(const GLuint unit
,
5249 WebGLSamplerJS
* const sampler
) {
5250 const FuncScope
funcScope(*this, "bindSampler");
5251 if (IsContextLost()) return;
5252 if (sampler
&& !sampler
->ValidateUsable(*this, "sampler")) return;
5253 auto& state
= State();
5255 auto& texUnits
= state
.mTexUnits
;
5256 if (unit
>= texUnits
.size()) {
5257 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`unit` (%u) larger than %zu.", unit
,
5264 texUnits
[unit
].sampler
= sampler
;
5266 Run
<RPROC(BindSampler
)>(unit
, sampler
? sampler
->mId
: 0);
5269 void ClientWebGLContext::SamplerParameteri(WebGLSamplerJS
& sampler
,
5271 const GLint param
) const {
5272 const FuncScope
funcScope(*this, "samplerParameteri");
5273 if (IsContextLost()) return;
5274 if (!sampler
.ValidateUsable(*this, "sampler")) return;
5276 Run
<RPROC(SamplerParameteri
)>(sampler
.mId
, pname
, param
);
5279 void ClientWebGLContext::SamplerParameterf(WebGLSamplerJS
& sampler
,
5281 const GLfloat param
) const {
5282 const FuncScope
funcScope(*this, "samplerParameterf");
5283 if (IsContextLost()) return;
5284 if (!sampler
.ValidateUsable(*this, "sampler")) return;
5286 Run
<RPROC(SamplerParameterf
)>(sampler
.mId
, pname
, param
);
5289 // ------------------------------- GL Sync ---------------------------------
5291 void ClientWebGLContext::GetSyncParameter(
5292 JSContext
* const cx
, WebGLSyncJS
& sync
, const GLenum pname
,
5293 JS::MutableHandle
<JS::Value
> retval
) const {
5294 retval
.set(JS::NullValue());
5295 const FuncScope
funcScope(*this, "getSyncParameter");
5296 if (IsContextLost()) return;
5297 if (!sync
.ValidateUsable(*this, "sync")) return;
5299 retval
.set([&]() -> JS::Value
{
5301 case LOCAL_GL_OBJECT_TYPE
:
5302 return JS::NumberValue(LOCAL_GL_SYNC_FENCE
);
5303 case LOCAL_GL_SYNC_CONDITION
:
5304 return JS::NumberValue(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE
);
5305 case LOCAL_GL_SYNC_FLAGS
:
5306 return JS::NumberValue(0);
5307 case LOCAL_GL_SYNC_STATUS
: {
5308 if (!sync
.mSignaled
) {
5309 const auto res
= ClientWaitSync(sync
, 0, 0);
5310 sync
.mSignaled
= (res
== LOCAL_GL_ALREADY_SIGNALED
||
5311 res
== LOCAL_GL_CONDITION_SATISFIED
);
5313 return JS::NumberValue(sync
.mSignaled
? LOCAL_GL_SIGNALED
5314 : LOCAL_GL_UNSIGNALED
);
5317 EnqueueError_ArgEnum("pname", pname
);
5318 return JS::NullValue();
5323 GLenum
ClientWebGLContext::ClientWaitSync(WebGLSyncJS
& sync
,
5324 const GLbitfield flags
,
5325 const GLuint64 timeout
) const {
5326 const FuncScope
funcScope(*this, "clientWaitSync");
5327 if (IsContextLost()) return LOCAL_GL_WAIT_FAILED
;
5328 if (!sync
.ValidateUsable(*this, "sync")) return LOCAL_GL_WAIT_FAILED
;
5330 if (flags
!= 0 && flags
!= LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT
) {
5331 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5332 "`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.");
5333 return LOCAL_GL_WAIT_FAILED
;
5336 const auto ret
= [&]() {
5337 const auto& inProcess
= mNotLost
->inProcess
;
5339 return inProcess
->ClientWaitSync(sync
.mId
, flags
, timeout
);
5341 const auto& child
= mNotLost
->outOfProcess
;
5342 child
->FlushPendingCmds();
5344 if (!child
->SendClientWaitSync(sync
.mId
, flags
, timeout
, &ret
)) {
5351 case LOCAL_GL_CONDITION_SATISFIED
:
5352 case LOCAL_GL_ALREADY_SIGNALED
:
5353 sync
.mSignaled
= true;
5359 const bool canBeAvailable
=
5360 (sync
.mCanBeAvailable
|| StaticPrefs::webgl_allow_immediate_queries());
5361 if (!canBeAvailable
) {
5362 if (!sync
.mHasWarnedNotAvailable
) {
5364 "ClientWaitSync must return TIMEOUT_EXPIRED until control has"
5365 " returned to the user agent's main loop. (only warns once)");
5366 sync
.mHasWarnedNotAvailable
= true;
5368 return LOCAL_GL_TIMEOUT_EXPIRED
;
5374 void ClientWebGLContext::WaitSync(const WebGLSyncJS
& sync
,
5375 const GLbitfield flags
,
5376 const GLint64 timeout
) const {
5377 const FuncScope
funcScope(*this, "waitSync");
5378 if (IsContextLost()) return;
5379 if (!sync
.ValidateUsable(*this, "sync")) return;
5382 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`flags` must be 0.");
5385 if (timeout
!= -1) {
5386 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`timeout` must be TIMEOUT_IGNORED.");
5390 JsWarning("waitSync is a no-op.");
5393 // -------------------------- Transform Feedback ---------------------------
5395 void ClientWebGLContext::BindTransformFeedback(
5396 const GLenum target
, WebGLTransformFeedbackJS
* const tf
) {
5397 const FuncScope
funcScope(*this, "bindTransformFeedback");
5398 if (IsContextLost()) return;
5399 if (tf
&& !tf
->ValidateUsable(*this, "tf")) return;
5400 auto& state
= State();
5402 if (target
!= LOCAL_GL_TRANSFORM_FEEDBACK
) {
5403 EnqueueError(LOCAL_GL_INVALID_ENUM
, "`target` must be TRANSFORM_FEEDBACK.");
5406 if (state
.mTfActiveAndNotPaused
) {
5407 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5408 "Current Transform Feedback object is active and not paused.");
5413 tf
->mHasBeenBound
= true;
5414 state
.mBoundTfo
= tf
;
5416 state
.mBoundTfo
= state
.mDefaultTfo
;
5419 Run
<RPROC(BindTransformFeedback
)>(tf
? tf
->mId
: 0);
5422 void ClientWebGLContext::BeginTransformFeedback(const GLenum primMode
) {
5423 const FuncScope
funcScope(*this, "beginTransformFeedback");
5424 if (IsContextLost()) return;
5425 auto& state
= State();
5426 auto& tfo
= *(state
.mBoundTfo
);
5428 if (tfo
.mActiveOrPaused
) {
5429 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5430 "Transform Feedback is already active or paused.");
5433 MOZ_ASSERT(!state
.mTfActiveAndNotPaused
);
5435 auto& prog
= state
.mCurrentProgram
;
5437 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No program in use.");
5440 const auto& linkResult
= GetLinkResult(*prog
);
5441 if (!linkResult
.success
) {
5442 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5443 "Program is not successfully linked.");
5447 auto tfBufferCount
= linkResult
.active
.activeTfVaryings
.size();
5448 if (tfBufferCount
&&
5449 linkResult
.tfBufferMode
== LOCAL_GL_INTERLEAVED_ATTRIBS
) {
5452 if (!tfBufferCount
) {
5453 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5454 "Program does not use Transform Feedback.");
5458 const auto& buffers
= tfo
.mAttribBuffers
;
5459 for (const auto i
: IntegerRange(tfBufferCount
)) {
5461 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5462 "Transform Feedback buffer %u is null.", i
);
5468 case LOCAL_GL_POINTS
:
5469 case LOCAL_GL_LINES
:
5470 case LOCAL_GL_TRIANGLES
:
5473 EnqueueError(LOCAL_GL_INVALID_ENUM
,
5474 "`primitiveMode` must be POINTS, LINES< or TRIANGLES.");
5480 tfo
.mActiveOrPaused
= true;
5481 tfo
.mActiveProgram
= prog
;
5482 tfo
.mActiveProgramKeepAlive
= prog
->mKeepAliveWeak
.lock();
5483 prog
->mActiveTfos
.insert(&tfo
);
5484 state
.mTfActiveAndNotPaused
= true;
5486 Run
<RPROC(BeginTransformFeedback
)>(primMode
);
5489 void ClientWebGLContext::EndTransformFeedback() {
5490 const FuncScope
funcScope(*this, "endTransformFeedback");
5491 if (IsContextLost()) return;
5492 auto& state
= State();
5493 auto& tfo
= *(state
.mBoundTfo
);
5495 if (!tfo
.mActiveOrPaused
) {
5496 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5497 "Transform Feedback is not active or paused.");
5501 tfo
.mActiveOrPaused
= false;
5502 tfo
.mActiveProgram
->mActiveTfos
.erase(&tfo
);
5503 tfo
.mActiveProgram
= nullptr;
5504 tfo
.mActiveProgramKeepAlive
= nullptr;
5505 state
.mTfActiveAndNotPaused
= false;
5506 Run
<RPROC(EndTransformFeedback
)>();
5509 void ClientWebGLContext::PauseTransformFeedback() {
5510 const FuncScope
funcScope(*this, "pauseTransformFeedback");
5511 if (IsContextLost()) return;
5512 auto& state
= State();
5513 auto& tfo
= *(state
.mBoundTfo
);
5515 if (!tfo
.mActiveOrPaused
) {
5516 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5517 "Transform Feedback is not active.");
5520 if (!state
.mTfActiveAndNotPaused
) {
5521 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5522 "Transform Feedback is already paused.");
5526 state
.mTfActiveAndNotPaused
= false;
5527 Run
<RPROC(PauseTransformFeedback
)>();
5530 void ClientWebGLContext::ResumeTransformFeedback() {
5531 const FuncScope
funcScope(*this, "resumeTransformFeedback");
5532 if (IsContextLost()) return;
5533 auto& state
= State();
5534 auto& tfo
= *(state
.mBoundTfo
);
5536 if (!tfo
.mActiveOrPaused
) {
5537 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5538 "Transform Feedback is not active and paused.");
5541 if (state
.mTfActiveAndNotPaused
) {
5542 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5543 "Transform Feedback is not paused.");
5546 if (state
.mCurrentProgram
!= tfo
.mActiveProgram
) {
5548 LOCAL_GL_INVALID_OPERATION
,
5549 "Cannot Resume Transform Feedback with a program link result different"
5550 " from when Begin was called.");
5554 state
.mTfActiveAndNotPaused
= true;
5555 Run
<RPROC(ResumeTransformFeedback
)>();
5558 void ClientWebGLContext::SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS
* fb
,
5560 fb
->mInOpaqueRAF
= value
;
5561 Run
<RPROC(SetFramebufferIsInOpaqueRAF
)>(fb
->mId
, value
);
5564 // ---------------------------- Misc Extensions ----------------------------
5565 void ClientWebGLContext::DrawBuffers(const dom::Sequence
<GLenum
>& buffers
) {
5566 const auto range
= MakeRange(buffers
);
5567 const auto vec
= std::vector
<GLenum
>(range
.begin().get(), range
.end().get());
5568 Run
<RPROC(DrawBuffers
)>(vec
);
5571 void ClientWebGLContext::EnqueueErrorImpl(const GLenum error
,
5572 const nsACString
& text
) const {
5573 if (!mNotLost
) return; // Ignored if context is lost.
5574 Run
<RPROC(GenerateError
)>(error
, ToString(text
));
5577 void ClientWebGLContext::RequestExtension(const WebGLExtensionID ext
) const {
5578 Run
<RPROC(RequestExtension
)>(ext
);
5583 static bool IsExtensionForbiddenForCaller(const WebGLExtensionID ext
,
5584 const dom::CallerType callerType
) {
5585 if (callerType
== dom::CallerType::System
) return false;
5587 if (StaticPrefs::webgl_enable_privileged_extensions()) return false;
5589 const bool resistFingerprinting
=
5590 nsContentUtils::ShouldResistFingerprinting();
5592 case WebGLExtensionID::MOZ_debug
:
5595 case WebGLExtensionID::WEBGL_debug_renderer_info
:
5596 return resistFingerprinting
||
5597 !StaticPrefs::webgl_enable_debug_renderer_info();
5599 case WebGLExtensionID::WEBGL_debug_shaders
:
5600 return resistFingerprinting
;
5607 bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext
,
5608 const dom::CallerType callerType
) const {
5609 if (IsExtensionForbiddenForCaller(ext
, callerType
)) return false;
5610 const auto& limits
= Limits();
5611 return limits
.supportedExtensions
[ext
];
5614 void ClientWebGLContext::GetSupportedExtensions(
5615 dom::Nullable
<nsTArray
<nsString
>>& retval
,
5616 const dom::CallerType callerType
) const {
5618 if (!mNotLost
) return;
5620 auto& retarr
= retval
.SetValue();
5621 for (const auto i
: MakeEnumeratedRange(WebGLExtensionID::Max
)) {
5622 if (!IsSupported(i
, callerType
)) continue;
5624 const auto& extStr
= GetExtensionName(i
);
5625 retarr
.AppendElement(NS_ConvertUTF8toUTF16(extStr
));
5631 void ClientWebGLContext::GetSupportedProfilesASTC(
5632 dom::Nullable
<nsTArray
<nsString
>>& retval
) const {
5634 if (!mNotLost
) return;
5635 const auto& limits
= Limits();
5637 auto& retarr
= retval
.SetValue();
5638 retarr
.AppendElement(u
"ldr"_ns
);
5639 if (limits
.astcHdr
) {
5640 retarr
.AppendElement(u
"hdr"_ns
);
5646 bool ClientWebGLContext::ShouldResistFingerprinting() const {
5647 if (mCanvasElement
) {
5648 // If we're constructed from a canvas element
5649 return nsContentUtils::ShouldResistFingerprinting(GetOwnerDoc());
5651 if (mOffscreenCanvas
) {
5652 // If we're constructed from an offscreen canvas
5653 return mOffscreenCanvas
->ShouldResistFingerprinting();
5655 // Last resort, just check the global preference
5656 return nsContentUtils::ShouldResistFingerprinting();
5659 uint32_t ClientWebGLContext::GetPrincipalHashValue() const {
5660 if (mCanvasElement
) {
5661 return mCanvasElement
->NodePrincipal()->GetHashValue();
5663 if (mOffscreenCanvas
) {
5664 nsIGlobalObject
* global
= mOffscreenCanvas
->GetOwnerGlobal();
5666 return global
->GetPrincipalHashValue();
5672 // ---------------------------
5674 void ClientWebGLContext::EnqueueError_ArgEnum(const char* const argName
,
5675 const GLenum val
) const {
5676 EnqueueError(LOCAL_GL_INVALID_ENUM
, "Bad `%s`: 0x%04x", argName
, val
);
5682 void ClientWebGLContext::AttachShader(WebGLProgramJS
& prog
,
5683 WebGLShaderJS
& shader
) const {
5684 const FuncScope
funcScope(*this, "attachShader");
5685 if (IsContextLost()) return;
5686 if (!prog
.ValidateUsable(*this, "program")) return;
5687 if (!shader
.ValidateUsable(*this, "shader")) return;
5689 auto& slot
= *MaybeFind(prog
.mNextLink_Shaders
, shader
.mType
);
5691 if (&shader
== slot
.shader
) {
5692 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`shader` is already attached.");
5694 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5695 "Only one of each type of"
5696 " shader may be attached to a program.");
5700 slot
= {&shader
, shader
.mKeepAliveWeak
.lock()};
5702 Run
<RPROC(AttachShader
)>(prog
.mId
, shader
.mId
);
5705 void ClientWebGLContext::BindAttribLocation(WebGLProgramJS
& prog
,
5706 const GLuint location
,
5707 const nsAString
& name
) const {
5708 const FuncScope
funcScope(*this, "detachShader");
5709 if (IsContextLost()) return;
5710 if (!prog
.ValidateUsable(*this, "program")) return;
5712 const auto& nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5713 Run
<RPROC(BindAttribLocation
)>(prog
.mId
, location
, nameU8
);
5716 void ClientWebGLContext::DetachShader(WebGLProgramJS
& prog
,
5717 const WebGLShaderJS
& shader
) const {
5718 const FuncScope
funcScope(*this, "detachShader");
5719 if (IsContextLost()) return;
5720 if (!prog
.ValidateUsable(*this, "program")) return;
5721 if (!shader
.ValidateUsable(*this, "shader")) return;
5723 auto& slot
= *MaybeFind(prog
.mNextLink_Shaders
, shader
.mType
);
5725 if (slot
.shader
!= &shader
) {
5726 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`shader` is not attached.");
5731 Run
<RPROC(DetachShader
)>(prog
.mId
, shader
.mId
);
5734 void ClientWebGLContext::GetAttachedShaders(
5735 const WebGLProgramJS
& prog
,
5736 dom::Nullable
<nsTArray
<RefPtr
<WebGLShaderJS
>>>& retval
) const {
5737 const FuncScope
funcScope(*this, "getAttachedShaders");
5738 if (IsContextLost()) return;
5739 if (!prog
.ValidateUsable(*this, "program")) return;
5741 auto& arr
= retval
.SetValue();
5742 for (const auto& pair
: prog
.mNextLink_Shaders
) {
5743 const auto& attachment
= pair
.second
;
5744 if (!attachment
.shader
) continue;
5745 arr
.AppendElement(attachment
.shader
);
5749 void ClientWebGLContext::LinkProgram(WebGLProgramJS
& prog
) const {
5750 const FuncScope
funcScope(*this, "linkProgram");
5751 if (IsContextLost()) return;
5752 if (!prog
.ValidateUsable(*this, "program")) return;
5754 if (!prog
.mActiveTfos
.empty()) {
5755 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5756 "Program still in use by active or paused"
5757 " Transform Feedback objects.");
5761 prog
.mResult
= std::make_shared
<webgl::LinkResult
>();
5762 prog
.mUniformLocByName
= Nothing();
5763 prog
.mUniformBlockBindings
= {};
5764 Run
<RPROC(LinkProgram
)>(prog
.mId
);
5767 void ClientWebGLContext::TransformFeedbackVaryings(
5768 WebGLProgramJS
& prog
, const dom::Sequence
<nsString
>& varyings
,
5769 const GLenum bufferMode
) const {
5770 const FuncScope
funcScope(*this, "transformFeedbackVaryings");
5771 if (IsContextLost()) return;
5772 if (!prog
.ValidateUsable(*this, "program")) return;
5774 std::vector
<std::string
> varyingsU8
;
5775 varyingsU8
.reserve(varyings
.Length());
5776 for (const auto& cur
: varyings
) {
5777 const auto curU8
= ToString(NS_ConvertUTF16toUTF8(cur
));
5778 varyingsU8
.push_back(curU8
);
5781 Run
<RPROC(TransformFeedbackVaryings
)>(prog
.mId
, varyingsU8
, bufferMode
);
5784 void ClientWebGLContext::UniformBlockBinding(WebGLProgramJS
& prog
,
5785 const GLuint blockIndex
,
5786 const GLuint blockBinding
) const {
5787 const FuncScope
funcScope(*this, "uniformBlockBinding");
5788 if (IsContextLost()) return;
5789 if (!prog
.ValidateUsable(*this, "program")) return;
5790 const auto& state
= State();
5792 (void)GetLinkResult(prog
);
5793 auto& list
= prog
.mUniformBlockBindings
;
5794 if (blockIndex
>= list
.size()) {
5796 LOCAL_GL_INVALID_VALUE
,
5797 "`blockIndex` (%u) must be less than ACTIVE_UNIFORM_BLOCKS (%zu).",
5798 blockIndex
, list
.size());
5801 if (blockBinding
>= state
.mBoundUbos
.size()) {
5802 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5803 "`blockBinding` (%u) must be less than "
5804 "MAX_UNIFORM_BUFFER_BINDINGS (%zu).",
5805 blockBinding
, state
.mBoundUbos
.size());
5809 list
[blockIndex
] = blockBinding
;
5810 Run
<RPROC(UniformBlockBinding
)>(prog
.mId
, blockIndex
, blockBinding
);
5813 // WebGLProgramJS link result reflection
5815 already_AddRefed
<WebGLActiveInfoJS
> ClientWebGLContext::GetActiveAttrib(
5816 const WebGLProgramJS
& prog
, const GLuint index
) {
5817 const FuncScope
funcScope(*this, "getActiveAttrib");
5818 if (IsContextLost()) return nullptr;
5819 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5821 const auto& res
= GetLinkResult(prog
);
5822 const auto& list
= res
.active
.activeAttribs
;
5823 if (index
>= list
.size()) {
5824 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5828 const auto& info
= list
[index
];
5829 return AsAddRefed(new WebGLActiveInfoJS(info
));
5832 already_AddRefed
<WebGLActiveInfoJS
> ClientWebGLContext::GetActiveUniform(
5833 const WebGLProgramJS
& prog
, const GLuint index
) {
5834 const FuncScope
funcScope(*this, "getActiveUniform");
5835 if (IsContextLost()) return nullptr;
5836 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5838 const auto& res
= GetLinkResult(prog
);
5839 const auto& list
= res
.active
.activeUniforms
;
5840 if (index
>= list
.size()) {
5841 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5845 const auto& info
= list
[index
];
5846 return AsAddRefed(new WebGLActiveInfoJS(info
));
5849 void ClientWebGLContext::GetActiveUniformBlockName(const WebGLProgramJS
& prog
,
5851 nsAString
& retval
) const {
5852 retval
.SetIsVoid(true);
5853 const FuncScope
funcScope(*this, "getActiveUniformBlockName");
5854 if (IsContextLost()) return;
5855 if (!prog
.ValidateUsable(*this, "program")) return;
5857 const auto& res
= GetLinkResult(prog
);
5859 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "Program has not been linked.");
5863 const auto& list
= res
.active
.activeUniformBlocks
;
5864 if (index
>= list
.size()) {
5865 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5869 const auto& block
= list
[index
];
5870 CopyUTF8toUTF16(block
.name
, retval
);
5873 void ClientWebGLContext::GetActiveUniformBlockParameter(
5874 JSContext
* const cx
, const WebGLProgramJS
& prog
, const GLuint index
,
5875 const GLenum pname
, JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) {
5876 retval
.set(JS::NullValue());
5877 const FuncScope
funcScope(*this, "getActiveUniformBlockParameter");
5878 if (IsContextLost()) return;
5879 if (!prog
.ValidateUsable(*this, "program")) return;
5881 const auto& res
= GetLinkResult(prog
);
5882 const auto& list
= res
.active
.activeUniformBlocks
;
5883 if (index
>= list
.size()) {
5884 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5887 const auto& block
= list
[index
];
5889 retval
.set([&]() -> JS::Value
{
5891 case LOCAL_GL_UNIFORM_BLOCK_BINDING
:
5892 return JS::NumberValue(prog
.mUniformBlockBindings
[index
]);
5894 case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE
:
5895 return JS::NumberValue(block
.dataSize
);
5897 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
:
5898 return JS::NumberValue(block
.activeUniformIndices
.size());
5900 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
: {
5901 const auto& indices
= block
.activeUniformIndices
;
5902 JS::Rooted
<JSObject
*> obj(
5904 dom::Uint32Array::Create(cx
, this, indices
.size(), indices
.data()));
5906 rv
= NS_ERROR_OUT_OF_MEMORY
;
5908 return JS::ObjectOrNullValue(obj
);
5911 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
:
5912 return JS::BooleanValue(block
.referencedByVertexShader
);
5914 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER
:
5915 return JS::BooleanValue(block
.referencedByFragmentShader
);
5918 EnqueueError_ArgEnum("pname", pname
);
5919 return JS::NullValue();
5924 void ClientWebGLContext::GetActiveUniforms(
5925 JSContext
* const cx
, const WebGLProgramJS
& prog
,
5926 const dom::Sequence
<GLuint
>& uniformIndices
, const GLenum pname
,
5927 JS::MutableHandle
<JS::Value
> retval
) const {
5928 retval
.set(JS::NullValue());
5929 const FuncScope
funcScope(*this, "getActiveUniforms");
5930 if (IsContextLost()) return;
5931 if (!prog
.ValidateUsable(*this, "program")) return;
5933 const auto& res
= GetLinkResult(prog
);
5934 const auto& list
= res
.active
.activeUniforms
;
5936 const auto count
= uniformIndices
.Length();
5937 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, count
));
5938 if (!array
) return; // Just bail.
5940 for (const auto i
: IntegerRange(count
)) {
5941 const auto index
= uniformIndices
[i
];
5942 if (index
>= list
.size()) {
5943 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5944 "`uniformIndices[%u]`: `%u` too large.", i
, index
);
5947 const auto& uniform
= list
[index
];
5949 JS::Rooted
<JS::Value
> value(cx
);
5951 case LOCAL_GL_UNIFORM_TYPE
:
5952 value
= JS::NumberValue(uniform
.elemType
);
5955 case LOCAL_GL_UNIFORM_SIZE
:
5956 value
= JS::NumberValue(uniform
.elemCount
);
5959 case LOCAL_GL_UNIFORM_BLOCK_INDEX
:
5960 value
= JS::NumberValue(uniform
.block_index
);
5963 case LOCAL_GL_UNIFORM_OFFSET
:
5964 value
= JS::NumberValue(uniform
.block_offset
);
5967 case LOCAL_GL_UNIFORM_ARRAY_STRIDE
:
5968 value
= JS::NumberValue(uniform
.block_arrayStride
);
5971 case LOCAL_GL_UNIFORM_MATRIX_STRIDE
:
5972 value
= JS::NumberValue(uniform
.block_matrixStride
);
5975 case LOCAL_GL_UNIFORM_IS_ROW_MAJOR
:
5976 value
= JS::BooleanValue(uniform
.block_isRowMajor
);
5980 EnqueueError_ArgEnum("pname", pname
);
5983 if (!JS_DefineElement(cx
, array
, i
, value
, JSPROP_ENUMERATE
)) return;
5986 retval
.setObject(*array
);
5989 already_AddRefed
<WebGLActiveInfoJS
>
5990 ClientWebGLContext::GetTransformFeedbackVarying(const WebGLProgramJS
& prog
,
5991 const GLuint index
) {
5992 const FuncScope
funcScope(*this, "getTransformFeedbackVarying");
5993 if (IsContextLost()) return nullptr;
5994 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5996 const auto& res
= GetLinkResult(prog
);
5997 const auto& list
= res
.active
.activeTfVaryings
;
5998 if (index
>= list
.size()) {
5999 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
6003 const auto& info
= list
[index
];
6004 return AsAddRefed(new WebGLActiveInfoJS(info
));
6007 GLint
ClientWebGLContext::GetAttribLocation(const WebGLProgramJS
& prog
,
6008 const nsAString
& name
) const {
6009 const FuncScope
funcScope(*this, "getAttribLocation");
6010 if (IsContextLost()) return -1;
6011 if (!prog
.ValidateUsable(*this, "program")) return -1;
6013 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
6014 const auto& res
= GetLinkResult(prog
);
6015 for (const auto& cur
: res
.active
.activeAttribs
) {
6016 if (cur
.name
== nameU8
) return cur
.location
;
6019 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
6021 EnqueueError(err
->type
, "%s", err
->info
.c_str());
6026 GLint
ClientWebGLContext::GetFragDataLocation(const WebGLProgramJS
& prog
,
6027 const nsAString
& name
) const {
6028 const FuncScope
funcScope(*this, "getFragDataLocation");
6029 if (IsContextLost()) return -1;
6030 if (!prog
.ValidateUsable(*this, "program")) return -1;
6032 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
6034 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
6041 const auto& inProcess
= mNotLost
->inProcess
;
6043 return inProcess
->GetFragDataLocation(prog
.mId
, nameU8
);
6045 const auto& child
= mNotLost
->outOfProcess
;
6046 child
->FlushPendingCmds();
6048 if (!child
->SendGetFragDataLocation(prog
.mId
, nameU8
, &ret
)) {
6055 GLuint
ClientWebGLContext::GetUniformBlockIndex(
6056 const WebGLProgramJS
& prog
, const nsAString
& blockName
) const {
6057 const FuncScope
funcScope(*this, "getUniformBlockIndex");
6058 if (IsContextLost()) return LOCAL_GL_INVALID_INDEX
;
6059 if (!prog
.ValidateUsable(*this, "program")) return LOCAL_GL_INVALID_INDEX
;
6061 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(blockName
));
6063 const auto& res
= GetLinkResult(prog
);
6064 const auto& list
= res
.active
.activeUniformBlocks
;
6065 for (const auto i
: IntegerRange(list
.size())) {
6066 const auto& cur
= list
[i
];
6067 if (cur
.name
== nameU8
) {
6071 return LOCAL_GL_INVALID_INDEX
;
6074 void ClientWebGLContext::GetUniformIndices(
6075 const WebGLProgramJS
& prog
, const dom::Sequence
<nsString
>& uniformNames
,
6076 dom::Nullable
<nsTArray
<GLuint
>>& retval
) const {
6077 const FuncScope
funcScope(*this, "getUniformIndices");
6078 if (IsContextLost()) return;
6079 if (!prog
.ValidateUsable(*this, "program")) return;
6081 const auto& res
= GetLinkResult(prog
);
6082 auto ret
= nsTArray
<GLuint
>(uniformNames
.Length());
6084 for (const auto& queriedNameU16
: uniformNames
) {
6085 const auto queriedName
= ToString(NS_ConvertUTF16toUTF8(queriedNameU16
));
6086 const auto impliedProperArrayQueriedName
= queriedName
+ "[0]";
6088 GLuint activeId
= LOCAL_GL_INVALID_INDEX
;
6089 for (const auto i
: IntegerRange(res
.active
.activeUniforms
.size())) {
6090 // O(N^2) ok for small N.
6091 const auto& activeInfoForI
= res
.active
.activeUniforms
[i
];
6092 if (queriedName
== activeInfoForI
.name
||
6093 impliedProperArrayQueriedName
== activeInfoForI
.name
) {
6098 ret
.AppendElement(activeId
);
6101 retval
.SetValue(std::move(ret
));
6104 already_AddRefed
<WebGLUniformLocationJS
> ClientWebGLContext::GetUniformLocation(
6105 const WebGLProgramJS
& prog
, const nsAString
& name
) const {
6106 const FuncScope
funcScope(*this, "getUniformLocation");
6107 if (IsContextLost()) return nullptr;
6108 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
6110 const auto& res
= GetLinkResult(prog
);
6112 if (!prog
.mUniformLocByName
) {
6113 // Cache a map from name->location.
6114 // Since the only way to set uniforms requires calling GetUniformLocation,
6115 // we expect apps to query most active uniforms once for each scalar or
6116 // array. NB: Uniform array setters do have overflow semantics, even though
6117 // uniform locations aren't guaranteed contiguous, but GetUniformLocation
6118 // must still be called once per array.
6119 prog
.mUniformLocByName
.emplace();
6121 for (const auto& activeUniform
: res
.active
.activeUniforms
) {
6122 if (activeUniform
.block_index
!= -1) continue;
6124 auto locName
= activeUniform
.name
;
6125 const auto indexed
= webgl::ParseIndexed(locName
);
6127 locName
= indexed
->name
;
6130 const auto err
= CheckGLSLVariableName(mIsWebGL2
, locName
);
6133 const auto baseLength
= locName
.size();
6134 for (const auto& pair
: activeUniform
.locByIndex
) {
6136 locName
.erase(baseLength
); // Erase previous "[N]".
6138 locName
+= std::to_string(pair
.first
);
6141 const auto locInfo
=
6142 WebGLProgramJS::UniformLocInfo
{pair
.second
, activeUniform
.elemType
};
6143 prog
.mUniformLocByName
->insert({locName
, locInfo
});
6147 const auto& locByName
= *(prog
.mUniformLocByName
);
6149 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
6150 auto loc
= MaybeFind(locByName
, nameU8
);
6152 loc
= MaybeFind(locByName
, nameU8
+ "[0]");
6155 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
6157 EnqueueError(err
->type
, "%s", err
->info
.c_str());
6162 return AsAddRefed(new WebGLUniformLocationJS(*this, prog
.mResult
,
6163 loc
->location
, loc
->elemType
));
6166 std::array
<uint16_t, 3> ValidUploadElemTypes(const GLenum elemType
) {
6167 std::vector
<GLenum
> ret
;
6170 ret
= {LOCAL_GL_FLOAT
, LOCAL_GL_INT
, LOCAL_GL_UNSIGNED_INT
};
6172 case LOCAL_GL_BOOL_VEC2
:
6173 ret
= {LOCAL_GL_FLOAT_VEC2
, LOCAL_GL_INT_VEC2
,
6174 LOCAL_GL_UNSIGNED_INT_VEC2
};
6176 case LOCAL_GL_BOOL_VEC3
:
6177 ret
= {LOCAL_GL_FLOAT_VEC3
, LOCAL_GL_INT_VEC3
,
6178 LOCAL_GL_UNSIGNED_INT_VEC3
};
6180 case LOCAL_GL_BOOL_VEC4
:
6181 ret
= {LOCAL_GL_FLOAT_VEC4
, LOCAL_GL_INT_VEC4
,
6182 LOCAL_GL_UNSIGNED_INT_VEC4
};
6185 case LOCAL_GL_SAMPLER_2D
:
6186 case LOCAL_GL_SAMPLER_3D
:
6187 case LOCAL_GL_SAMPLER_CUBE
:
6188 case LOCAL_GL_SAMPLER_2D_SHADOW
:
6189 case LOCAL_GL_SAMPLER_2D_ARRAY
:
6190 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
6191 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
6192 case LOCAL_GL_INT_SAMPLER_2D
:
6193 case LOCAL_GL_INT_SAMPLER_3D
:
6194 case LOCAL_GL_INT_SAMPLER_CUBE
:
6195 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
6196 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
6197 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
6198 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
6199 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
:
6200 ret
= {LOCAL_GL_INT
};
6208 std::array
<uint16_t, 3> arr
= {};
6209 MOZ_ASSERT(arr
[2] == 0);
6210 for (const auto i
: IntegerRange(ret
.size())) {
6211 arr
[i
] = AssertedCast
<uint16_t>(ret
[i
]);
6216 void ClientWebGLContext::GetProgramInfoLog(const WebGLProgramJS
& prog
,
6217 nsAString
& retval
) const {
6218 retval
.SetIsVoid(true);
6219 const FuncScope
funcScope(*this, "getProgramInfoLog");
6220 if (IsContextLost()) return;
6221 if (!prog
.ValidateUsable(*this, "program")) return;
6223 const auto& res
= GetLinkResult(prog
);
6224 CopyUTF8toUTF16(res
.log
, retval
);
6227 void ClientWebGLContext::GetProgramParameter(
6228 JSContext
* const js
, const WebGLProgramJS
& prog
, const GLenum pname
,
6229 JS::MutableHandle
<JS::Value
> retval
) const {
6230 retval
.set(JS::NullValue());
6231 const FuncScope
funcScope(*this, "getProgramParameter");
6232 if (IsContextLost()) return;
6233 if (!prog
.ValidateUsable(*this, "program")) return;
6235 retval
.set([&]() -> JS::Value
{
6237 case LOCAL_GL_DELETE_STATUS
:
6238 // "Is flagged for deletion?"
6239 return JS::BooleanValue(!prog
.mKeepAlive
);
6240 case LOCAL_GL_VALIDATE_STATUS
:
6241 return JS::BooleanValue(prog
.mLastValidate
);
6242 case LOCAL_GL_ATTACHED_SHADERS
: {
6244 for (const auto& pair
: prog
.mNextLink_Shaders
) {
6245 const auto& slot
= pair
.second
;
6250 return JS::NumberValue(shaders
);
6256 const auto& res
= GetLinkResult(prog
);
6259 case LOCAL_GL_LINK_STATUS
:
6260 return JS::BooleanValue(res
.success
);
6262 case LOCAL_GL_ACTIVE_ATTRIBUTES
:
6263 return JS::NumberValue(res
.active
.activeAttribs
.size());
6265 case LOCAL_GL_ACTIVE_UNIFORMS
:
6266 return JS::NumberValue(res
.active
.activeUniforms
.size());
6268 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE
:
6269 if (!mIsWebGL2
) break;
6270 return JS::NumberValue(res
.tfBufferMode
);
6272 case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS
:
6273 if (!mIsWebGL2
) break;
6274 return JS::NumberValue(res
.active
.activeTfVaryings
.size());
6276 case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS
:
6277 if (!mIsWebGL2
) break;
6278 return JS::NumberValue(res
.active
.activeUniformBlocks
.size());
6283 EnqueueError_ArgEnum("pname", pname
);
6284 return JS::NullValue();
6291 void ClientWebGLContext::CompileShader(WebGLShaderJS
& shader
) const {
6292 const FuncScope
funcScope(*this, "compileShader");
6293 if (IsContextLost()) return;
6294 if (!shader
.ValidateUsable(*this, "shader")) return;
6296 shader
.mResult
= {};
6297 Run
<RPROC(CompileShader
)>(shader
.mId
);
6300 void ClientWebGLContext::GetShaderInfoLog(const WebGLShaderJS
& shader
,
6301 nsAString
& retval
) const {
6302 retval
.SetIsVoid(true);
6303 const FuncScope
funcScope(*this, "getShaderInfoLog");
6304 if (IsContextLost()) return;
6305 if (!shader
.ValidateUsable(*this, "shader")) return;
6307 const auto& result
= GetCompileResult(shader
);
6308 CopyUTF8toUTF16(result
.log
, retval
);
6311 void ClientWebGLContext::GetShaderParameter(
6312 JSContext
* const cx
, const WebGLShaderJS
& shader
, const GLenum pname
,
6313 JS::MutableHandle
<JS::Value
> retval
) const {
6314 retval
.set(JS::NullValue());
6315 const FuncScope
funcScope(*this, "getShaderParameter");
6316 if (IsContextLost()) return;
6317 if (!shader
.ValidateUsable(*this, "shader")) return;
6319 retval
.set([&]() -> JS::Value
{
6321 case LOCAL_GL_SHADER_TYPE
:
6322 return JS::NumberValue(shader
.mType
);
6324 case LOCAL_GL_DELETE_STATUS
: // "Is flagged for deletion?"
6325 return JS::BooleanValue(!shader
.mKeepAlive
);
6327 case LOCAL_GL_COMPILE_STATUS
: {
6328 const auto& result
= GetCompileResult(shader
);
6329 return JS::BooleanValue(result
.success
);
6333 EnqueueError_ArgEnum("pname", pname
);
6334 return JS::NullValue();
6339 void ClientWebGLContext::GetShaderSource(const WebGLShaderJS
& shader
,
6340 nsAString
& retval
) const {
6341 retval
.SetIsVoid(true);
6342 const FuncScope
funcScope(*this, "getShaderSource");
6343 if (IsContextLost()) return;
6344 if (!shader
.ValidateUsable(*this, "shader")) return;
6346 CopyUTF8toUTF16(shader
.mSource
, retval
);
6349 void ClientWebGLContext::GetTranslatedShaderSource(const WebGLShaderJS
& shader
,
6350 nsAString
& retval
) const {
6351 retval
.SetIsVoid(true);
6352 const FuncScope
funcScope(*this, "getTranslatedShaderSource");
6353 if (IsContextLost()) return;
6354 if (!shader
.ValidateUsable(*this, "shader")) return;
6356 const auto& result
= GetCompileResult(shader
);
6357 CopyUTF8toUTF16(result
.translatedSource
, retval
);
6360 void ClientWebGLContext::ShaderSource(WebGLShaderJS
& shader
,
6361 const nsAString
& sourceU16
) const {
6362 const FuncScope
funcScope(*this, "shaderSource");
6363 if (IsContextLost()) return;
6364 if (!shader
.ValidateUsable(*this, "shader")) return;
6366 shader
.mSource
= ToString(NS_ConvertUTF16toUTF8(sourceU16
));
6367 Run
<RPROC(ShaderSource
)>(shader
.mId
, shader
.mSource
);
6372 const webgl::CompileResult
& ClientWebGLContext::GetCompileResult(
6373 const WebGLShaderJS
& shader
) const {
6374 if (shader
.mResult
.pending
) {
6375 shader
.mResult
= [&]() {
6376 const auto& inProcess
= mNotLost
->inProcess
;
6378 return inProcess
->GetCompileResult(shader
.mId
);
6380 const auto& child
= mNotLost
->outOfProcess
;
6381 child
->FlushPendingCmds();
6382 webgl::CompileResult ret
= {};
6383 if (!child
->SendGetCompileResult(shader
.mId
, &ret
)) {
6389 return shader
.mResult
;
6392 const webgl::LinkResult
& ClientWebGLContext::GetLinkResult(
6393 const WebGLProgramJS
& prog
) const {
6394 if (prog
.mResult
->pending
) {
6395 const auto notLost
=
6396 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
6397 if (!notLost
) return *(prog
.mResult
);
6399 *(prog
.mResult
) = [&]() {
6400 const auto& inProcess
= mNotLost
->inProcess
;
6402 return inProcess
->GetLinkResult(prog
.mId
);
6404 const auto& child
= mNotLost
->outOfProcess
;
6405 child
->FlushPendingCmds();
6406 webgl::LinkResult ret
;
6407 if (!child
->SendGetLinkResult(prog
.mId
, &ret
)) {
6413 prog
.mUniformBlockBindings
.resize(
6414 prog
.mResult
->active
.activeUniformBlocks
.size());
6416 auto& state
= State();
6417 if (state
.mCurrentProgram
== &prog
&& prog
.mResult
->success
) {
6418 state
.mActiveLinkResult
= prog
.mResult
;
6421 return *(prog
.mResult
);
6426 // ---------------------------
6428 bool ClientWebGLContext::ValidateArrayBufferView(
6429 const dom::ArrayBufferView
& view
, GLuint elemOffset
,
6430 GLuint elemCountOverride
, const GLenum errorEnum
, uint8_t** const out_bytes
,
6431 size_t* const out_byteLen
) const {
6432 view
.ComputeState();
6433 uint8_t* const bytes
= view
.Data();
6434 const size_t byteLen
= view
.Length();
6436 const auto& elemSize
= SizeOfViewElem(view
);
6438 size_t elemCount
= byteLen
/ elemSize
;
6439 if (elemOffset
> elemCount
) {
6440 EnqueueError(errorEnum
, "Invalid offset into ArrayBufferView.");
6443 elemCount
-= elemOffset
;
6445 if (elemCountOverride
) {
6446 if (elemCountOverride
> elemCount
) {
6447 EnqueueError(errorEnum
, "Invalid sub-length for ArrayBufferView.");
6450 elemCount
= elemCountOverride
;
6453 *out_bytes
= bytes
+ (elemOffset
* elemSize
);
6454 *out_byteLen
= elemCount
* elemSize
;
6458 // ---------------------------
6460 webgl::ObjectJS::ObjectJS(const ClientWebGLContext
& webgl
)
6461 : mGeneration(webgl
.mNotLost
), mId(webgl
.mNotLost
->state
.NextId()) {}
6465 WebGLFramebufferJS::WebGLFramebufferJS(const ClientWebGLContext
& webgl
,
6467 : webgl::ObjectJS(webgl
), mOpaque(opaque
) {
6468 (void)mAttachments
[LOCAL_GL_DEPTH_ATTACHMENT
];
6469 (void)mAttachments
[LOCAL_GL_STENCIL_ATTACHMENT
];
6470 if (!webgl
.mIsWebGL2
) {
6471 (void)mAttachments
[LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
];
6474 EnsureColorAttachments();
6477 void WebGLFramebufferJS::EnsureColorAttachments() {
6478 const auto& webgl
= Context();
6479 const auto& limits
= webgl
->Limits();
6480 auto maxColorDrawBuffers
= limits
.maxColorDrawBuffers
;
6481 if (!webgl
->mIsWebGL2
&&
6482 !webgl
->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
)) {
6483 maxColorDrawBuffers
= 1;
6485 for (const auto i
: IntegerRange(maxColorDrawBuffers
)) {
6486 (void)mAttachments
[LOCAL_GL_COLOR_ATTACHMENT0
+ i
];
6490 WebGLProgramJS::WebGLProgramJS(const ClientWebGLContext
& webgl
)
6491 : webgl::ObjectJS(webgl
),
6492 mKeepAlive(std::make_shared
<webgl::ProgramKeepAlive
>(*this)),
6493 mKeepAliveWeak(mKeepAlive
) {
6494 (void)mNextLink_Shaders
[LOCAL_GL_VERTEX_SHADER
];
6495 (void)mNextLink_Shaders
[LOCAL_GL_FRAGMENT_SHADER
];
6497 mResult
= std::make_shared
<webgl::LinkResult
>();
6500 WebGLShaderJS::WebGLShaderJS(const ClientWebGLContext
& webgl
, const GLenum type
)
6501 : webgl::ObjectJS(webgl
),
6503 mKeepAlive(std::make_shared
<webgl::ShaderKeepAlive
>(*this)),
6504 mKeepAliveWeak(mKeepAlive
) {}
6506 WebGLTransformFeedbackJS::WebGLTransformFeedbackJS(
6507 const ClientWebGLContext
& webgl
)
6508 : webgl::ObjectJS(webgl
),
6509 mAttribBuffers(webgl::kMaxTransformFeedbackSeparateAttribs
) {}
6511 WebGLVertexArrayJS::WebGLVertexArrayJS(const ClientWebGLContext
& webgl
)
6512 : webgl::ObjectJS(webgl
), mAttribBuffers(webgl
.Limits().maxVertexAttribs
) {}
6516 #define _(WebGLType) \
6517 JSObject* WebGLType##JS::WrapObject(JSContext* const cx, \
6518 JS::Handle<JSObject*> givenProto) { \
6519 return dom::WebGLType##_Binding::Wrap(cx, this, givenProto); \
6526 _(WebGLRenderbuffer
)
6531 _(WebGLTransformFeedback
)
6532 _(WebGLUniformLocation
)
6533 //_(WebGLVertexArray) // The webidl is `WebGLVertexArrayObject` :(
6537 JSObject
* WebGLVertexArrayJS::WrapObject(JSContext
* const cx
,
6538 JS::Handle
<JSObject
*> givenProto
) {
6539 return dom::WebGLVertexArrayObject_Binding::Wrap(cx
, this, givenProto
);
6542 bool WebGLActiveInfoJS::WrapObject(JSContext
* const cx
,
6543 JS::Handle
<JSObject
*> givenProto
,
6544 JS::MutableHandle
<JSObject
*> reflector
) {
6545 return dom::WebGLActiveInfo_Binding::Wrap(cx
, this, givenProto
, reflector
);
6548 bool WebGLShaderPrecisionFormatJS::WrapObject(
6549 JSContext
* const cx
, JS::Handle
<JSObject
*> givenProto
,
6550 JS::MutableHandle
<JSObject
*> reflector
) {
6551 return dom::WebGLShaderPrecisionFormat_Binding::Wrap(cx
, this, givenProto
,
6555 // ---------------------
6557 // Todo: Move this to RefPtr.h.
6558 template <typename T
>
6559 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6560 const RefPtr
<T
>& field
, const char* name
,
6562 ImplCycleCollectionTraverse(callback
, const_cast<RefPtr
<T
>&>(field
), name
,
6568 template <typename T
>
6569 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6570 const std::vector
<RefPtr
<T
>>& field
,
6571 const char* name
, uint32_t flags
) {
6572 for (const auto& cur
: field
) {
6573 ImplCycleCollectionTraverse(callback
, cur
, name
, flags
);
6577 template <typename T
>
6578 void ImplCycleCollectionUnlink(std::vector
<RefPtr
<T
>>& field
) {
6584 template <typename T
, size_t N
>
6585 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6586 const std::array
<RefPtr
<T
>, N
>& field
,
6587 const char* name
, uint32_t flags
) {
6588 for (const auto& cur
: field
) {
6589 ImplCycleCollectionTraverse(callback
, cur
, name
, flags
);
6593 template <typename T
, size_t N
>
6594 void ImplCycleCollectionUnlink(std::array
<RefPtr
<T
>, N
>& field
) {
6600 template <typename T
>
6601 void ImplCycleCollectionTraverse(
6602 nsCycleCollectionTraversalCallback
& callback
,
6603 const std::unordered_map
<GLenum
, RefPtr
<T
>>& field
, const char* name
,
6605 for (const auto& pair
: field
) {
6606 ImplCycleCollectionTraverse(callback
, pair
.second
, name
, flags
);
6610 template <typename T
>
6611 void ImplCycleCollectionUnlink(std::unordered_map
<GLenum
, RefPtr
<T
>>& field
) {
6617 void ImplCycleCollectionTraverse(
6618 nsCycleCollectionTraversalCallback
& callback
,
6619 const std::unordered_map
<GLenum
, WebGLFramebufferJS::Attachment
>& field
,
6620 const char* name
, uint32_t flags
) {
6621 for (const auto& pair
: field
) {
6622 const auto& attach
= pair
.second
;
6623 ImplCycleCollectionTraverse(callback
, attach
.rb
, name
, flags
);
6624 ImplCycleCollectionTraverse(callback
, attach
.tex
, name
, flags
);
6628 void ImplCycleCollectionUnlink(
6629 std::unordered_map
<GLenum
, WebGLFramebufferJS::Attachment
>& field
) {
6635 void ImplCycleCollectionTraverse(
6636 nsCycleCollectionTraversalCallback
& callback
,
6637 const std::unordered_map
<GLenum
, WebGLProgramJS::Attachment
>& field
,
6638 const char* name
, uint32_t flags
) {
6639 for (const auto& pair
: field
) {
6640 const auto& attach
= pair
.second
;
6641 ImplCycleCollectionTraverse(callback
, attach
.shader
, name
, flags
);
6645 void ImplCycleCollectionUnlink(
6646 std::unordered_map
<GLenum
, WebGLProgramJS::Attachment
>& field
) {
6652 void ImplCycleCollectionUnlink(
6653 const RefPtr
<ClientWebGLExtensionLoseContext
>& field
) {
6654 const_cast<RefPtr
<ClientWebGLExtensionLoseContext
>&>(field
) = nullptr;
6656 void ImplCycleCollectionUnlink(const RefPtr
<WebGLProgramJS
>& field
) {
6657 const_cast<RefPtr
<WebGLProgramJS
>&>(field
) = nullptr;
6659 void ImplCycleCollectionUnlink(const RefPtr
<WebGLShaderJS
>& field
) {
6660 const_cast<RefPtr
<WebGLShaderJS
>&>(field
) = nullptr;
6663 // ----------------------
6665 void ImplCycleCollectionTraverse(
6666 nsCycleCollectionTraversalCallback
& callback
,
6667 const std::shared_ptr
<webgl::NotLostData
>& field
, const char* name
,
6671 ImplCycleCollectionTraverse(callback
, field
->extensions
,
6672 "NotLostData.extensions", flags
);
6674 const auto& state
= field
->state
;
6676 ImplCycleCollectionTraverse(callback
, state
.mDefaultTfo
, "state.mDefaultTfo",
6678 ImplCycleCollectionTraverse(callback
, state
.mDefaultVao
, "state.mDefaultVao",
6681 ImplCycleCollectionTraverse(callback
, state
.mCurrentProgram
,
6682 "state.mCurrentProgram", flags
);
6684 ImplCycleCollectionTraverse(callback
, state
.mBoundBufferByTarget
,
6685 "state.mBoundBufferByTarget", flags
);
6686 ImplCycleCollectionTraverse(callback
, state
.mBoundUbos
, "state.mBoundUbos",
6688 ImplCycleCollectionTraverse(callback
, state
.mBoundDrawFb
,
6689 "state.mBoundDrawFb", flags
);
6690 ImplCycleCollectionTraverse(callback
, state
.mBoundReadFb
,
6691 "state.mBoundReadFb", flags
);
6692 ImplCycleCollectionTraverse(callback
, state
.mBoundRb
, "state.mBoundRb",
6694 ImplCycleCollectionTraverse(callback
, state
.mBoundTfo
, "state.mBoundTfo",
6696 ImplCycleCollectionTraverse(callback
, state
.mBoundVao
, "state.mBoundVao",
6698 ImplCycleCollectionTraverse(callback
, state
.mCurrentQueryByTarget
,
6699 "state.state.mCurrentQueryByTarget", flags
);
6701 for (const auto& texUnit
: state
.mTexUnits
) {
6702 ImplCycleCollectionTraverse(callback
, texUnit
.sampler
,
6703 "state.mTexUnits[].sampler", flags
);
6704 ImplCycleCollectionTraverse(callback
, texUnit
.texByTarget
,
6705 "state.mTexUnits[].texByTarget", flags
);
6709 void ImplCycleCollectionUnlink(std::shared_ptr
<webgl::NotLostData
>& field
) {
6711 const auto keepAlive
= field
;
6712 keepAlive
->extensions
= {};
6713 keepAlive
->state
= {};
6717 // -----------------------------------------------------
6719 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBufferJS
)
6720 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebufferJS
, mAttachments
)
6721 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgramJS
, mNextLink_Shaders
)
6722 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQueryJS
)
6723 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbufferJS
)
6724 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSamplerJS
)
6725 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShaderJS
)
6726 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSyncJS
)
6727 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTextureJS
)
6728 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLTransformFeedbackJS
, mAttribBuffers
,
6730 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocationJS
)
6731 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArrayJS
, mIndexBuffer
,
6736 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClientWebGLContext
)
6737 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
6738 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
6739 NS_INTERFACE_MAP_ENTRY(nsISupports
)
6740 NS_INTERFACE_MAP_END
6742 NS_IMPL_CYCLE_COLLECTING_ADDREF(ClientWebGLContext
)
6743 NS_IMPL_CYCLE_COLLECTING_RELEASE(ClientWebGLContext
)
6745 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(
6746 ClientWebGLContext
, mExtLoseContext
, mNotLost
,
6747 // Don't forget nsICanvasRenderingContextInternal:
6748 mCanvasElement
, mOffscreenCanvas
)
6750 // -----------------------------
6753 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGL##X##JS, AddRef) \
6754 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGL##X##JS, Release)
6765 _(TransformFeedback
)
6771 } // namespace mozilla