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"
8 #include "ClientWebGLExtensions.h"
10 #include "gfxCrashReporterUtils.h"
11 #include "HostWebGLContext.h"
12 #include "js/ScalarType.h" // js::Scalar::Type
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/dom/SanitizeRenderer.h"
15 #include "mozilla/dom/ToJSValue.h"
16 #include "mozilla/dom/WebGLContextEvent.h"
17 #include "mozilla/dom/WorkerCommon.h"
18 #include "mozilla/EnumeratedRange.h"
19 #include "mozilla/gfx/gfxVars.h"
20 #include "mozilla/ipc/Shmem.h"
21 #include "mozilla/layers/CompositorBridgeChild.h"
22 #include "mozilla/layers/ImageBridgeChild.h"
23 #include "mozilla/layers/LayerTransactionChild.h"
24 #include "mozilla/layers/OOPCanvasRenderer.h"
25 #include "mozilla/layers/TextureClientSharedSurface.h"
26 #include "mozilla/layers/WebRenderUserData.h"
27 #include "mozilla/layers/WebRenderCanvasRenderer.h"
28 #include "mozilla/Preferences.h"
29 #include "mozilla/ScopeExit.h"
30 #include "mozilla/StaticPrefs_webgl.h"
31 #include "nsContentUtils.h"
32 #include "nsDisplayList.h"
33 #include "TexUnpackBlob.h"
34 #include "WebGLMethodDispatcher.h"
35 #include "WebGLChild.h"
36 #include "WebGLValidateStrings.h"
40 webgl::NotLostData::NotLostData(ClientWebGLContext
& _context
)
41 : context(_context
) {}
43 webgl::NotLostData::~NotLostData() {
45 Unused
<< dom::WebGLChild::Send__delete__(outOfProcess
.get());
51 bool webgl::ObjectJS::ValidateForContext(
52 const ClientWebGLContext
& targetContext
, const char* const argName
) const {
53 if (!IsForContext(targetContext
)) {
54 targetContext
.EnqueueError(
55 LOCAL_GL_INVALID_OPERATION
,
56 "`%s` is from a different (or lost) WebGL context.", argName
);
62 void webgl::ObjectJS::WarnInvalidUse(const ClientWebGLContext
& targetContext
,
63 const char* const argName
) const {
64 if (!ValidateForContext(targetContext
, argName
)) return;
66 const auto errEnum
= ErrorOnDeleted();
67 targetContext
.EnqueueError(errEnum
, "Object `%s` is already deleted.",
71 static bool GetJSScalarFromGLType(GLenum type
,
72 js::Scalar::Type
* const out_scalarType
) {
75 *out_scalarType
= js::Scalar::Int8
;
78 case LOCAL_GL_UNSIGNED_BYTE
:
79 *out_scalarType
= js::Scalar::Uint8
;
83 *out_scalarType
= js::Scalar::Int16
;
86 case LOCAL_GL_HALF_FLOAT
:
87 case LOCAL_GL_HALF_FLOAT_OES
:
88 case LOCAL_GL_UNSIGNED_SHORT
:
89 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
90 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
91 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
92 *out_scalarType
= js::Scalar::Uint16
;
95 case LOCAL_GL_UNSIGNED_INT
:
96 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
:
97 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV
:
98 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
99 case LOCAL_GL_UNSIGNED_INT_24_8
:
100 *out_scalarType
= js::Scalar::Uint32
;
103 *out_scalarType
= js::Scalar::Int32
;
107 *out_scalarType
= js::Scalar::Float32
;
115 ClientWebGLContext::ClientWebGLContext(const bool webgl2
)
117 mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {}
119 ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); }
121 void ClientWebGLContext::JsWarning(const std::string
& utf8
) const {
122 if (!mCanvasElement
) {
126 if (!api
.Init(mCanvasElement
->OwnerDoc()->GetScopeObject())) {
129 const auto& cx
= api
.cx();
130 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
133 void AutoJsWarning(const std::string
& utf8
) {
134 const AutoJSContext cx
;
135 JS::WarnUTF8(cx
, "%s", utf8
.c_str());
140 bool ClientWebGLContext::DispatchEvent(const nsAString
& eventName
) const {
141 const auto kCanBubble
= CanBubble::eYes
;
142 const auto kIsCancelable
= Cancelable::eYes
;
143 bool useDefaultHandler
= true;
145 if (mCanvasElement
) {
146 nsContentUtils::DispatchTrustedEvent(
147 mCanvasElement
->OwnerDoc(), static_cast<nsIContent
*>(mCanvasElement
),
148 eventName
, kCanBubble
, kIsCancelable
, &useDefaultHandler
);
149 } else if (mOffscreenCanvas
) {
150 // OffscreenCanvas case
151 RefPtr
<dom::Event
> event
=
152 new dom::Event(mOffscreenCanvas
, nullptr, nullptr);
153 event
->InitEvent(eventName
, kCanBubble
, kIsCancelable
);
154 event
->SetTrusted(true);
155 useDefaultHandler
= mOffscreenCanvas
->DispatchEvent(
156 *event
, dom::CallerType::System
, IgnoreErrors());
158 return useDefaultHandler
;
163 void ClientWebGLContext::EmulateLoseContext() const {
164 const FuncScope
funcScope(*this, "loseContext");
165 if (mLossStatus
!= webgl::LossStatus::Ready
) {
166 JsWarning("loseContext: Already lost.");
168 mNextError
= LOCAL_GL_INVALID_OPERATION
;
172 OnContextLoss(webgl::ContextLossReason::Manual
);
175 void ClientWebGLContext::OnContextLoss(
176 const webgl::ContextLossReason reason
) const {
177 MOZ_ASSERT(NS_IsMainThread());
178 JsWarning("WebGL context was lost.");
181 for (const auto& ext
: mNotLost
->extensions
) {
183 ext
->mContext
= nullptr; // Detach.
185 mNotLost
= {}; // Lost now!
186 mNextError
= LOCAL_GL_CONTEXT_LOST_WEBGL
;
190 case webgl::ContextLossReason::Guilty
:
191 mLossStatus
= webgl::LossStatus::LostForever
;
194 case webgl::ContextLossReason::None
:
195 mLossStatus
= webgl::LossStatus::Lost
;
198 case webgl::ContextLossReason::Manual
:
199 mLossStatus
= webgl::LossStatus::LostManually
;
203 const auto weak
= WeakPtr
<const ClientWebGLContext
>(this);
204 const auto fnRun
= [weak
]() {
205 const auto strong
= RefPtr
<const ClientWebGLContext
>(weak
);
207 strong
->Event_webglcontextlost();
209 already_AddRefed
<mozilla::Runnable
> runnable
=
210 NS_NewRunnableFunction("enqueue Event_webglcontextlost", fnRun
);
211 NS_DispatchToCurrentThread(std::move(runnable
));
214 void ClientWebGLContext::Event_webglcontextlost() const {
215 const bool useDefaultHandler
= DispatchEvent(u
"webglcontextlost"_ns
);
216 if (useDefaultHandler
) {
217 mLossStatus
= webgl::LossStatus::LostForever
;
220 if (mLossStatus
== webgl::LossStatus::Lost
) {
221 RestoreContext(webgl::LossStatus::Lost
);
225 void ClientWebGLContext::RestoreContext(
226 const webgl::LossStatus requiredStatus
) const {
227 if (requiredStatus
!= mLossStatus
) {
229 "restoreContext: Only valid iff context lost with loseContext().");
231 mNextError
= LOCAL_GL_INVALID_OPERATION
;
235 MOZ_RELEASE_ASSERT(mLossStatus
== webgl::LossStatus::Lost
||
236 mLossStatus
== webgl::LossStatus::LostManually
);
238 if (mAwaitingRestore
) return;
239 mAwaitingRestore
= true;
241 const auto weak
= WeakPtr
<const ClientWebGLContext
>(this);
242 const auto fnRun
= [weak
]() {
243 const auto strong
= RefPtr
<const ClientWebGLContext
>(weak
);
245 strong
->Event_webglcontextrestored();
247 already_AddRefed
<mozilla::Runnable
> runnable
=
248 NS_NewRunnableFunction("enqueue Event_webglcontextrestored", fnRun
);
249 NS_DispatchToCurrentThread(std::move(runnable
));
252 void ClientWebGLContext::Event_webglcontextrestored() const {
253 mAwaitingRestore
= false;
254 mLossStatus
= webgl::LossStatus::Ready
;
257 const uvec2 requestSize
= {mCanvasElement
->Width(), mCanvasElement
->Height()};
258 const auto mutThis
= const_cast<ClientWebGLContext
*>(
259 this); // TODO: Make context loss non-mutable.
260 if (!mutThis
->CreateHostContext(requestSize
)) {
261 mLossStatus
= webgl::LossStatus::LostForever
;
265 (void)DispatchEvent(u
"webglcontextrestored"_ns
);
270 void ClientWebGLContext::ThrowEvent_WebGLContextCreationError(
271 const std::string
& text
) const {
273 msg
.AppendPrintf("Failed to create WebGL context: %s", text
.c_str());
274 JsWarning(msg
.BeginReading());
276 RefPtr
<dom::EventTarget
> target
= mCanvasElement
;
277 if (!target
&& mOffscreenCanvas
) {
278 target
= mOffscreenCanvas
;
279 } else if (!target
) {
283 const auto kEventName
= u
"webglcontextcreationerror"_ns
;
285 dom::WebGLContextEventInit eventInit
;
286 // eventInit.mCancelable = true; // The spec says this, but it's silly.
287 eventInit
.mStatusMessage
= NS_ConvertASCIItoUTF16(text
.c_str());
289 const RefPtr
<dom::WebGLContextEvent
> event
=
290 dom::WebGLContextEvent::Constructor(target
, kEventName
, eventInit
);
291 event
->SetTrusted(true);
293 target
->DispatchEvent(*event
);
298 // If we are running WebGL in this process then call the HostWebGLContext
299 // method directly. Otherwise, dispatch over IPC.
300 template <typename MethodType
, MethodType method
, typename
... Args
>
301 void ClientWebGLContext::Run(Args
&&... args
) const {
303 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
304 if (IsContextLost()) return;
306 const auto& inProcess
= notLost
->inProcess
;
308 return (inProcess
.get()->*method
)(std::forward
<Args
>(args
)...);
311 const auto& child
= notLost
->outOfProcess
;
313 const auto id
= IdByMethod
<MethodType
, method
>();
315 const auto size
= webgl::SerializedSize(id
, args
...);
316 const auto maybeDest
= child
->AllocPendingCmdBytes(size
);
318 JsWarning("Failed to allocate internal command buffer.");
319 OnContextLoss(webgl::ContextLossReason::None
);
322 const auto& destBytes
= *maybeDest
;
323 webgl::Serialize(destBytes
, id
, args
...);
326 // -------------------------------------------------------------------------
327 // Client-side helper methods. Dispatch to a Host method.
328 // -------------------------------------------------------------------------
330 #define RPROC(_METHOD) \
331 decltype(&HostWebGLContext::_METHOD), &HostWebGLContext::_METHOD
333 // ------------------------- Composition, etc -------------------------
335 void ClientWebGLContext::OnBeforePaintTransaction() {
336 const RefPtr
<layers::ImageBridgeChild
> imageBridge
=
337 layers::ImageBridgeChild::GetSingleton();
339 const auto texType
= layers::TexTypeForWebgl(imageBridge
);
340 Present(nullptr, texType
);
343 void ClientWebGLContext::EndComposition() {
344 // Mark ourselves as no longer invalidated.
350 void ClientWebGLContext::Present(WebGLFramebufferJS
* const xrFb
,
351 const layers::TextureType type
,
353 if (!mIsCanvasDirty
&& !xrFb
) return;
355 mIsCanvasDirty
= false;
358 Run
<RPROC(Present
)>(xrFb
? xrFb
->mId
: 0, type
, webvr
);
361 Maybe
<layers::SurfaceDescriptor
> ClientWebGLContext::GetFrontBuffer(
362 WebGLFramebufferJS
* const fb
, bool vr
) {
363 const auto notLost
= mNotLost
;
364 if (IsContextLost()) return {};
366 const auto& inProcess
= mNotLost
->inProcess
;
368 return inProcess
->GetFrontBuffer(fb
? fb
->mId
: 0, vr
);
371 const auto& child
= mNotLost
->outOfProcess
;
372 child
->FlushPendingCmds();
373 Maybe
<layers::SurfaceDescriptor
> ret
;
374 if (!child
->SendGetFrontBuffer(fb
? fb
->mId
: 0, vr
, &ret
)) return {};
378 void ClientWebGLContext::ClearVRSwapChain() { Run
<RPROC(ClearVRSwapChain
)>(); }
382 already_AddRefed
<layers::Layer
> ClientWebGLContext::GetCanvasLayer(
383 nsDisplayListBuilder
* builder
, Layer
* oldLayer
, LayerManager
* manager
) {
384 if (!mResetLayer
&& oldLayer
) {
385 RefPtr
<layers::Layer
> ret
= oldLayer
;
389 RefPtr
<CanvasLayer
> canvasLayer
= manager
->CreateCanvasLayer();
391 NS_WARNING("CreateCanvasLayer returned null!");
395 const auto canvasRenderer
= canvasLayer
->CreateOrGetCanvasRenderer();
396 if (!InitializeCanvasRenderer(builder
, canvasRenderer
)) return nullptr;
400 flags
|= Layer::CONTENT_OPAQUE
;
402 canvasLayer
->SetContentFlags(flags
);
405 return canvasLayer
.forget();
408 bool ClientWebGLContext::UpdateWebRenderCanvasData(
409 nsDisplayListBuilder
* aBuilder
, WebRenderCanvasData
* aCanvasData
) {
410 CanvasRenderer
* renderer
= aCanvasData
->GetCanvasRenderer();
412 if (!mResetLayer
&& renderer
) {
416 renderer
= aCanvasData
->CreateCanvasRenderer();
417 if (!InitializeCanvasRenderer(aBuilder
, renderer
)) {
418 // Clear CanvasRenderer of WebRenderCanvasData
419 aCanvasData
->ClearCanvasRenderer();
423 MOZ_ASSERT(renderer
);
428 bool ClientWebGLContext::InitializeCanvasRenderer(
429 nsDisplayListBuilder
* aBuilder
, CanvasRenderer
* aRenderer
) {
430 const FuncScope
funcScope(*this, "<InitializeCanvasRenderer>");
431 if (IsContextLost()) return false;
433 layers::CanvasRendererData data
;
434 data
.mContext
= mSharedPtrPtr
;
435 data
.mOriginPos
= gl::OriginPos::BottomLeft
;
437 const auto& options
= *mInitialOptions
;
438 const auto& size
= DrawingBufferSize();
439 data
.mIsOpaque
= !options
.alpha
;
440 data
.mIsAlphaPremult
= !options
.alpha
|| options
.premultipliedAlpha
;
441 data
.mSize
= {size
.x
, size
.y
};
443 if (aBuilder
->IsPaintingToWindow() && mCanvasElement
) {
444 data
.mDoPaintCallbacks
= true;
447 aRenderer
->Initialize(data
);
448 aRenderer
->SetDirty();
452 layers::LayersBackend
ClientWebGLContext::GetCompositorBackendType() const {
453 if (mCanvasElement
) {
454 return mCanvasElement
->GetCompositorBackendType();
455 } else if (mOffscreenCanvas
) {
456 return mOffscreenCanvas
->GetCompositorBackendType();
459 return layers::LayersBackend::LAYERS_NONE
;
462 mozilla::dom::Document
* ClientWebGLContext::GetOwnerDoc() const {
463 MOZ_ASSERT(mCanvasElement
);
464 if (!mCanvasElement
) {
467 return mCanvasElement
->OwnerDoc();
470 void ClientWebGLContext::Commit() {
471 if (mOffscreenCanvas
) {
472 mOffscreenCanvas
->CommitFrameToCompositor();
476 void ClientWebGLContext::GetCanvas(
477 dom::Nullable
<dom::OwningHTMLCanvasElementOrOffscreenCanvas
>& retval
) {
478 if (mCanvasElement
) {
479 MOZ_RELEASE_ASSERT(!mOffscreenCanvas
, "GFX: Canvas is offscreen.");
481 if (mCanvasElement
->IsInNativeAnonymousSubtree()) {
484 retval
.SetValue().SetAsHTMLCanvasElement() = mCanvasElement
;
486 } else if (mOffscreenCanvas
) {
487 retval
.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas
;
493 void ClientWebGLContext::GetContextAttributes(
494 dom::Nullable
<dom::WebGLContextAttributes
>& retval
) {
496 const FuncScope
funcScope(*this, "getContextAttributes");
497 if (IsContextLost()) return;
499 dom::WebGLContextAttributes
& result
= retval
.SetValue();
501 const auto& options
= mNotLost
->info
.options
;
503 result
.mAlpha
.Construct(options
.alpha
);
504 result
.mDepth
= options
.depth
;
505 result
.mStencil
= options
.stencil
;
506 result
.mAntialias
.Construct(options
.antialias
);
507 result
.mPremultipliedAlpha
= options
.premultipliedAlpha
;
508 result
.mPreserveDrawingBuffer
= options
.preserveDrawingBuffer
;
509 result
.mFailIfMajorPerformanceCaveat
= options
.failIfMajorPerformanceCaveat
;
510 result
.mPowerPreference
= options
.powerPreference
;
513 // -----------------------
516 ClientWebGLContext::SetDimensions(const int32_t signedWidth
,
517 const int32_t signedHeight
) {
518 const FuncScope
funcScope(*this, "<SetDimensions>");
519 MOZ_ASSERT(mInitialOptions
);
521 if (mLossStatus
!= webgl::LossStatus::Ready
) {
522 // Attempted resize of a lost context.
526 uvec2 size
= {static_cast<uint32_t>(signedWidth
),
527 static_cast<uint32_t>(signedHeight
)};
534 const auto prevRequestedSize
= mRequestedSize
;
535 mRequestedSize
= size
;
537 mResetLayer
= true; // Always treat this as resize.
540 auto& state
= State();
542 auto curSize
= prevRequestedSize
;
543 if (state
.mDrawingBufferSize
) {
544 curSize
= *state
.mDrawingBufferSize
;
546 if (size
== curSize
) return NS_OK
; // MUST skip no-op resize
548 state
.mDrawingBufferSize
= Nothing();
549 Run
<RPROC(Resize
)>(size
);
556 // Context (re-)creation
558 if (!CreateHostContext(size
)) {
559 return NS_ERROR_FAILURE
;
564 static bool IsWebglOutOfProcessEnabled() {
565 bool useOop
= StaticPrefs::webgl_out_of_process();
567 if (!gfx::gfxVars::AllowWebglOop()) {
570 if (StaticPrefs::webgl_out_of_process_force()) {
576 static inline bool StartsWith(const std::string
& haystack
,
577 const std::string
& needle
) {
578 return haystack
.find(needle
) == 0;
581 bool ClientWebGLContext::CreateHostContext(const uvec2
& requestedSize
) {
582 const auto pNotLost
= std::make_shared
<webgl::NotLostData
>(*this);
583 auto& notLost
= *pNotLost
;
585 auto res
= [&]() -> Result
<Ok
, std::string
> {
586 auto options
= *mInitialOptions
;
587 if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) {
588 options
.failIfMajorPerformanceCaveat
= false;
591 if (options
.failIfMajorPerformanceCaveat
) {
592 const auto backend
= GetCompositorBackendType();
593 bool isCompositorSlow
= false;
594 isCompositorSlow
|= (backend
== layers::LayersBackend::LAYERS_BASIC
);
595 isCompositorSlow
|= (backend
== layers::LayersBackend::LAYERS_WR
&&
596 gfx::gfxVars::UseSoftwareWebRender());
598 if (isCompositorSlow
) {
600 "failIfMajorPerformanceCaveat: Compositor is not"
601 " hardware-accelerated.");
605 const bool resistFingerprinting
= ShouldResistFingerprinting();
607 const auto& principal
= GetCanvas()->NodePrincipal();
608 const auto principalKey
= principal
->GetHashValue();
609 const auto initDesc
= webgl::InitContextDesc
{
610 mIsWebGL2
, resistFingerprinting
, requestedSize
, options
, principalKey
};
614 auto useOop
= IsWebglOutOfProcessEnabled();
615 if (XRE_IsParentProcess()) {
621 HostWebGLContext::Create({this, nullptr}, initDesc
, ¬Lost
.info
);
627 ScopedGfxFeatureReporter
reporter("IpcWebGL");
629 auto* const cbc
= layers::CompositorBridgeChild::Get();
632 return Err("!CompositorBridgeChild::Get()");
635 RefPtr
<dom::WebGLChild
> outOfProcess
= new dom::WebGLChild(*this);
637 static_cast<dom::WebGLChild
*>(cbc
->SendPWebGLConstructor(outOfProcess
));
639 return Err("SendPWebGLConstructor failed");
642 if (!outOfProcess
->SendInitialize(initDesc
, ¬Lost
.info
)) {
643 return Err("WebGL actor Initialize failed");
646 notLost
.outOfProcess
= outOfProcess
;
647 reporter
.SetSuccessful();
651 auto str
= res
.unwrapErr();
652 if (StartsWith(str
, "failIfMajorPerformanceCaveat")) {
654 " (about:config override available:"
655 " webgl.disable-fail-if-major-performance-caveat)";
657 notLost
.info
.error
= str
;
659 if (!notLost
.info
.error
.empty()) {
660 ThrowEvent_WebGLContextCreationError(notLost
.info
.error
);
667 const auto& limits
= Limits();
668 auto& state
= State();
669 state
.mDefaultTfo
= new WebGLTransformFeedbackJS(*this);
670 state
.mDefaultVao
= new WebGLVertexArrayJS(*this);
672 state
.mBoundTfo
= state
.mDefaultTfo
;
673 state
.mBoundVao
= state
.mDefaultVao
;
675 (void)state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
];
677 state
.mTexUnits
.resize(limits
.maxTexUnits
);
678 state
.mBoundUbos
.resize(limits
.maxUniformBufferBindings
);
681 webgl::TypedQuad initVal
;
682 const float fData
[4] = {0, 0, 0, 1};
683 memcpy(initVal
.data
, fData
, sizeof(initVal
.data
));
684 state
.mGenericVertexAttribs
.resize(limits
.maxVertexAttribs
, initVal
);
687 const auto& size
= DrawingBufferSize();
688 state
.mViewport
= {0, 0, static_cast<int32_t>(size
.x
),
689 static_cast<int32_t>(size
.y
)};
690 state
.mScissor
= state
.mViewport
;
693 // Insert keys to enable slots:
694 (void)state
.mBoundBufferByTarget
[LOCAL_GL_COPY_READ_BUFFER
];
695 (void)state
.mBoundBufferByTarget
[LOCAL_GL_COPY_WRITE_BUFFER
];
696 (void)state
.mBoundBufferByTarget
[LOCAL_GL_PIXEL_PACK_BUFFER
];
697 (void)state
.mBoundBufferByTarget
[LOCAL_GL_PIXEL_UNPACK_BUFFER
];
698 (void)state
.mBoundBufferByTarget
[LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
];
699 (void)state
.mBoundBufferByTarget
[LOCAL_GL_UNIFORM_BUFFER
];
701 (void)state
.mCurrentQueryByTarget
[LOCAL_GL_ANY_SAMPLES_PASSED
];
702 //(void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE];
703 //// Same slot as ANY_SAMPLES_PASSED.
705 .mCurrentQueryByTarget
[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
];
713 uvec2
ClientWebGLContext::DrawingBufferSize() {
714 if (IsContextLost()) return {};
716 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
717 auto& state
= State();
718 auto& size
= state
.mDrawingBufferSize
;
721 const auto& inProcess
= mNotLost
->inProcess
;
723 size
= Some(inProcess
->DrawingBufferSize());
725 const auto& child
= mNotLost
->outOfProcess
;
726 child
->FlushPendingCmds();
728 if (!child
->SendDrawingBufferSize(&actual
)) return {};
736 void ClientWebGLContext::OnMemoryPressure() {
737 if (IsContextLost()) return;
739 const auto& inProcess
= mNotLost
->inProcess
;
741 return inProcess
->OnMemoryPressure();
743 const auto& child
= mNotLost
->outOfProcess
;
744 (void)child
->SendOnMemoryPressure();
748 ClientWebGLContext::SetContextOptions(JSContext
* cx
,
749 JS::Handle
<JS::Value
> options
,
750 ErrorResult
& aRvForDictionaryInit
) {
751 if (mInitialOptions
&& options
.isNullOrUndefined()) return NS_OK
;
753 dom::WebGLContextAttributes attributes
;
754 if (!attributes
.Init(cx
, options
)) {
755 aRvForDictionaryInit
.Throw(NS_ERROR_UNEXPECTED
);
756 return NS_ERROR_UNEXPECTED
;
759 WebGLContextOptions newOpts
;
761 newOpts
.stencil
= attributes
.mStencil
;
762 newOpts
.depth
= attributes
.mDepth
;
763 newOpts
.premultipliedAlpha
= attributes
.mPremultipliedAlpha
;
764 newOpts
.preserveDrawingBuffer
= attributes
.mPreserveDrawingBuffer
;
765 newOpts
.failIfMajorPerformanceCaveat
=
766 attributes
.mFailIfMajorPerformanceCaveat
;
767 newOpts
.xrCompatible
= attributes
.mXrCompatible
;
768 newOpts
.powerPreference
= attributes
.mPowerPreference
;
769 newOpts
.enableDebugRendererInfo
=
770 StaticPrefs::webgl_enable_debug_renderer_info();
771 MOZ_ASSERT(mCanvasElement
|| mOffscreenCanvas
);
772 newOpts
.shouldResistFingerprinting
=
774 // If we're constructed from a canvas element
775 nsContentUtils::ShouldResistFingerprinting(GetOwnerDoc())
777 // If we're constructed from an offscreen canvas
778 nsContentUtils::ShouldResistFingerprinting(
779 mOffscreenCanvas
->GetOwnerGlobal()->PrincipalOrNull());
781 if (attributes
.mAlpha
.WasPassed()) {
782 newOpts
.alpha
= attributes
.mAlpha
.Value();
784 if (attributes
.mAntialias
.WasPassed()) {
785 newOpts
.antialias
= attributes
.mAntialias
.Value();
788 // Don't do antialiasing if we've disabled MSAA.
789 if (!StaticPrefs::webgl_msaa_samples()) {
790 newOpts
.antialias
= false;
793 if (mInitialOptions
&& *mInitialOptions
!= newOpts
) {
794 // Err if the options asked for aren't the same as what they were
796 return NS_ERROR_FAILURE
;
799 mXRCompatible
= attributes
.mXrCompatible
;
801 mInitialOptions
.emplace(newOpts
);
805 void ClientWebGLContext::DidRefresh() { Run
<RPROC(DidRefresh
)>(); }
807 already_AddRefed
<gfx::SourceSurface
> ClientWebGLContext::GetSurfaceSnapshot(
808 gfxAlphaType
* const out_alphaType
) {
809 const FuncScope
funcScope(*this, "<GetSurfaceSnapshot>");
810 if (IsContextLost()) return nullptr;
812 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
814 auto ret
= BackBufferSnapshot();
815 if (!ret
) return nullptr;
819 const auto& options
= mNotLost
->info
.options
;
821 auto srcAlphaType
= gfxAlphaType::Opaque
;
823 if (options
.premultipliedAlpha
) {
824 srcAlphaType
= gfxAlphaType::Premult
;
826 srcAlphaType
= gfxAlphaType::NonPremult
;
831 *out_alphaType
= srcAlphaType
;
833 // Expects Opaque or Premult
834 if (srcAlphaType
== gfxAlphaType::NonPremult
) {
835 const auto nonPremultSurf
= ret
;
836 const auto& size
= nonPremultSurf
->GetSize();
837 const auto format
= nonPremultSurf
->GetFormat();
838 ret
= gfx::Factory::CreateDataSourceSurface(size
, format
, /*zero=*/false);
839 gfxUtils::PremultiplyDataSurface(nonPremultSurf
, ret
);
846 RefPtr
<gfx::SourceSurface
> ClientWebGLContext::GetFrontBufferSnapshot(
847 const bool requireAlphaPremult
) {
848 const FuncScope
funcScope(*this, "<GetSurfaceSnapshot>");
849 if (IsContextLost()) return nullptr;
851 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
853 const auto& options
= mNotLost
->info
.options
;
855 const auto surfFormat
= options
.alpha
? gfx::SurfaceFormat::B8G8R8A8
856 : gfx::SurfaceFormat::B8G8R8X8
;
858 const auto fnNewSurf
= [&](const uvec2 size
) {
859 const auto stride
= size
.x
* 4;
860 return RefPtr
<gfx::DataSourceSurface
>(
861 gfx::Factory::CreateDataSourceSurfaceWithStride({size
.x
, size
.y
},
866 auto snapshot
= [&]() -> RefPtr
<gfx::DataSourceSurface
> {
867 const auto& inProcess
= mNotLost
->inProcess
;
869 const auto surfSize
= inProcess
->GetFrontBufferSize();
870 const auto stride
= surfSize
.x
* 4;
871 const auto byteSize
= stride
* surfSize
.y
;
872 const auto surf
= fnNewSurf(surfSize
);
873 if (!surf
) return nullptr;
875 const gfx::DataSourceSurface::ScopedMap
map(
876 surf
, gfx::DataSourceSurface::READ_WRITE
);
877 if (!map
.IsMapped()) {
881 MOZ_RELEASE_ASSERT(map
.GetStride() == static_cast<int64_t>(stride
));
882 auto range
= Range
<uint8_t>{map
.GetData(), byteSize
};
883 if (!inProcess
->FrontBufferSnapshotInto(range
)) return nullptr;
887 const auto& child
= mNotLost
->outOfProcess
;
888 child
->FlushPendingCmds();
889 webgl::FrontBufferSnapshotIpc res
;
890 if (!child
->SendGetFrontBufferSnapshot(&res
)) {
893 const auto& surfSize
= res
.surfSize
;
894 const webgl::RaiiShmem shmem
{child
, res
.shmem
};
895 const auto& shmemBytes
= shmem
.ByteRange();
896 if (!surfSize
.x
) return nullptr; // Zero means failure.
898 const auto stride
= surfSize
.x
* 4;
899 const auto byteSize
= stride
* surfSize
.y
;
901 const auto surf
= fnNewSurf(surfSize
);
902 if (!surf
) return nullptr;
905 const gfx::DataSourceSurface::ScopedMap
map(
906 surf
, gfx::DataSourceSurface::READ_WRITE
);
907 if (!map
.IsMapped()) {
911 MOZ_RELEASE_ASSERT(map
.GetStride() == static_cast<int64_t>(stride
));
912 MOZ_RELEASE_ASSERT(shmemBytes
.length() == byteSize
);
913 memcpy(map
.GetData(), shmemBytes
.begin().get(), byteSize
);
917 if (!snapshot
) return nullptr;
919 if (requireAlphaPremult
&& options
.alpha
&& !options
.premultipliedAlpha
) {
920 const auto nonPremultSurf
= snapshot
;
921 const auto& size
= nonPremultSurf
->GetSize();
922 const auto format
= nonPremultSurf
->GetFormat();
924 gfx::Factory::CreateDataSourceSurface(size
, format
, /*zero=*/false);
926 gfxCriticalNote
<< "CreateDataSourceSurface failed for size " << size
;
928 gfxUtils::PremultiplyDataSurface(nonPremultSurf
, snapshot
);
934 RefPtr
<gfx::DataSourceSurface
> ClientWebGLContext::BackBufferSnapshot() {
935 if (IsContextLost()) return nullptr;
937 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
939 const auto& options
= mNotLost
->info
.options
;
940 const auto& state
= State();
942 const auto drawFbWas
= state
.mBoundDrawFb
;
943 const auto readFbWas
= state
.mBoundReadFb
;
945 Find(state
.mBoundBufferByTarget
, LOCAL_GL_PIXEL_PACK_BUFFER
);
947 const auto size
= DrawingBufferSize();
951 BindFramebuffer(LOCAL_GL_FRAMEBUFFER
, nullptr);
953 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, nullptr);
956 auto reset
= MakeScopeExit([&] {
957 if (drawFbWas
== readFbWas
) {
958 BindFramebuffer(LOCAL_GL_FRAMEBUFFER
, drawFbWas
);
960 BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, drawFbWas
);
961 BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, readFbWas
);
964 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, pboWas
);
968 const auto surfFormat
= options
.alpha
? gfx::SurfaceFormat::B8G8R8A8
969 : gfx::SurfaceFormat::B8G8R8X8
;
970 const auto stride
= size
.x
* 4;
971 RefPtr
<gfx::DataSourceSurface
> surf
=
972 gfx::Factory::CreateDataSourceSurfaceWithStride(
973 {size
.x
, size
.y
}, surfFormat
, stride
, /*zero=*/true);
975 if (NS_WARN_IF(!surf
)) return nullptr;
978 const gfx::DataSourceSurface::ScopedMap
map(
979 surf
, gfx::DataSourceSurface::READ_WRITE
);
980 if (!map
.IsMapped()) {
984 MOZ_ASSERT(static_cast<uint32_t>(map
.GetStride()) == stride
);
986 const auto desc
= webgl::ReadPixelsDesc
{{0, 0}, size
};
987 const auto range
= Range
<uint8_t>(map
.GetData(), stride
* size
.y
);
988 DoReadPixels(desc
, range
);
990 const auto begin
= range
.begin().get();
992 std::vector
<uint8_t> temp
;
994 for (const auto i
: IntegerRange(size
.y
/ 2)) {
995 const auto top
= begin
+ stride
* i
;
996 const auto bottom
= begin
+ stride
* (size
.y
- 1 - i
);
997 memcpy(temp
.data(), top
, stride
);
998 memcpy(top
, bottom
, stride
);
999 gfxUtils::ConvertBGRAtoRGBA(top
, stride
);
1001 memcpy(bottom
, temp
.data(), stride
);
1002 gfxUtils::ConvertBGRAtoRGBA(bottom
, stride
);
1006 const auto middle
= begin
+ stride
* (size
.y
/ 2);
1007 gfxUtils::ConvertBGRAtoRGBA(middle
, stride
);
1014 UniquePtr
<uint8_t[]> ClientWebGLContext::GetImageBuffer(int32_t* out_format
) {
1017 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1019 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
1020 if (!snapshot
) return nullptr;
1022 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1024 const auto& premultAlpha
= mNotLost
->info
.options
.premultipliedAlpha
;
1025 return gfxUtils::GetImageBuffer(dataSurface
, premultAlpha
, out_format
);
1029 ClientWebGLContext::GetInputStream(const char* mimeType
,
1030 const nsAString
& encoderOptions
,
1031 nsIInputStream
** out_stream
) {
1032 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1034 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
1035 if (!snapshot
) return NS_ERROR_FAILURE
;
1037 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1038 const auto& premultAlpha
= mNotLost
->info
.options
.premultipliedAlpha
;
1039 return gfxUtils::GetInputStream(dataSurface
, premultAlpha
, mimeType
,
1040 encoderOptions
, out_stream
);
1043 // ------------------------- Client WebGL Objects -------------------------
1044 // ------------------------- Create/Destroy/Is -------------------------
1046 template <typename T
>
1047 static already_AddRefed
<T
> AsAddRefed(T
* ptr
) {
1052 template <typename T
>
1053 static RefPtr
<T
> AsRefPtr(T
* ptr
) {
1057 already_AddRefed
<WebGLBufferJS
> ClientWebGLContext::CreateBuffer() const {
1058 const FuncScope
funcScope(*this, "createBuffer");
1059 if (IsContextLost()) return nullptr;
1061 auto ret
= AsRefPtr(new WebGLBufferJS(*this));
1062 Run
<RPROC(CreateBuffer
)>(ret
->mId
);
1063 return ret
.forget();
1066 already_AddRefed
<WebGLFramebufferJS
> ClientWebGLContext::CreateFramebuffer()
1068 const FuncScope
funcScope(*this, "createFramebuffer");
1069 if (IsContextLost()) return nullptr;
1071 auto ret
= AsRefPtr(new WebGLFramebufferJS(*this));
1072 Run
<RPROC(CreateFramebuffer
)>(ret
->mId
);
1073 return ret
.forget();
1076 already_AddRefed
<WebGLFramebufferJS
>
1077 ClientWebGLContext::CreateOpaqueFramebuffer(
1078 const webgl::OpaqueFramebufferOptions
& options
) const {
1079 const FuncScope
funcScope(*this, "createOpaqueFramebuffer");
1080 if (IsContextLost()) return nullptr;
1082 auto ret
= AsRefPtr(new WebGLFramebufferJS(*this, true));
1084 const auto& inProcess
= mNotLost
->inProcess
;
1086 if (!inProcess
->CreateOpaqueFramebuffer(ret
->mId
, options
)) {
1089 return ret
.forget();
1091 const auto& child
= mNotLost
->outOfProcess
;
1092 child
->FlushPendingCmds();
1094 if (!child
->SendCreateOpaqueFramebuffer(ret
->mId
, options
, &ok
))
1096 if (!ok
) return nullptr;
1097 return ret
.forget();
1100 already_AddRefed
<WebGLProgramJS
> ClientWebGLContext::CreateProgram() const {
1101 const FuncScope
funcScope(*this, "createProgram");
1102 if (IsContextLost()) return nullptr;
1104 auto ret
= AsRefPtr(new WebGLProgramJS(*this));
1105 Run
<RPROC(CreateProgram
)>(ret
->mId
);
1106 return ret
.forget();
1109 already_AddRefed
<WebGLQueryJS
> ClientWebGLContext::CreateQuery() const {
1110 const FuncScope
funcScope(*this, "createQuery");
1111 if (IsContextLost()) return nullptr;
1113 auto ret
= AsRefPtr(new WebGLQueryJS(*this));
1114 Run
<RPROC(CreateQuery
)>(ret
->mId
);
1115 return ret
.forget();
1118 already_AddRefed
<WebGLRenderbufferJS
> ClientWebGLContext::CreateRenderbuffer()
1120 const FuncScope
funcScope(*this, "createRenderbuffer");
1121 if (IsContextLost()) return nullptr;
1123 auto ret
= AsRefPtr(new WebGLRenderbufferJS(*this));
1124 Run
<RPROC(CreateRenderbuffer
)>(ret
->mId
);
1125 return ret
.forget();
1128 already_AddRefed
<WebGLSamplerJS
> ClientWebGLContext::CreateSampler() const {
1129 const FuncScope
funcScope(*this, "createSampler");
1130 if (IsContextLost()) return nullptr;
1132 auto ret
= AsRefPtr(new WebGLSamplerJS(*this));
1133 Run
<RPROC(CreateSampler
)>(ret
->mId
);
1134 return ret
.forget();
1137 already_AddRefed
<WebGLShaderJS
> ClientWebGLContext::CreateShader(
1138 const GLenum type
) const {
1139 const FuncScope
funcScope(*this, "createShader");
1140 if (IsContextLost()) return nullptr;
1143 case LOCAL_GL_VERTEX_SHADER
:
1144 case LOCAL_GL_FRAGMENT_SHADER
:
1147 EnqueueError_ArgEnum("type", type
);
1151 auto ret
= AsRefPtr(new WebGLShaderJS(*this, type
));
1152 Run
<RPROC(CreateShader
)>(ret
->mId
, ret
->mType
);
1153 return ret
.forget();
1156 already_AddRefed
<WebGLSyncJS
> ClientWebGLContext::FenceSync(
1157 const GLenum condition
, const GLbitfield flags
) const {
1158 const FuncScope
funcScope(*this, "fenceSync");
1159 if (IsContextLost()) return nullptr;
1161 if (condition
!= LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE
) {
1162 EnqueueError_ArgEnum("condition", condition
);
1167 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`flags` must be 0.");
1171 auto ret
= AsRefPtr(new WebGLSyncJS(*this));
1172 Run
<RPROC(CreateSync
)>(ret
->mId
);
1174 auto& availRunnable
= EnsureAvailabilityRunnable();
1175 availRunnable
.mSyncs
.push_back(ret
.get());
1176 ret
->mCanBeAvailable
= false;
1178 return ret
.forget();
1181 already_AddRefed
<WebGLTextureJS
> ClientWebGLContext::CreateTexture() const {
1182 const FuncScope
funcScope(*this, "createTexture");
1183 if (IsContextLost()) return nullptr;
1185 auto ret
= AsRefPtr(new WebGLTextureJS(*this));
1186 Run
<RPROC(CreateTexture
)>(ret
->mId
);
1187 return ret
.forget();
1190 already_AddRefed
<WebGLTransformFeedbackJS
>
1191 ClientWebGLContext::CreateTransformFeedback() const {
1192 const FuncScope
funcScope(*this, "createTransformFeedback");
1193 if (IsContextLost()) return nullptr;
1195 auto ret
= AsRefPtr(new WebGLTransformFeedbackJS(*this));
1196 Run
<RPROC(CreateTransformFeedback
)>(ret
->mId
);
1197 return ret
.forget();
1200 already_AddRefed
<WebGLVertexArrayJS
> ClientWebGLContext::CreateVertexArray()
1202 const FuncScope
funcScope(*this, "createVertexArray");
1203 if (IsContextLost()) return nullptr;
1205 auto ret
= AsRefPtr(new WebGLVertexArrayJS(*this));
1206 Run
<RPROC(CreateVertexArray
)>(ret
->mId
);
1207 return ret
.forget();
1212 static bool ValidateOrSkipForDelete(const ClientWebGLContext
& context
,
1213 const webgl::ObjectJS
* const obj
) {
1214 if (!obj
) return false;
1215 if (!obj
->ValidateForContext(context
, "obj")) return false;
1216 if (obj
->IsDeleted()) return false;
1220 void ClientWebGLContext::DeleteBuffer(WebGLBufferJS
* const obj
) {
1221 const FuncScope
funcScope(*this, "deleteBuffer");
1222 if (IsContextLost()) return;
1223 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1224 auto& state
= State();
1226 // Unbind from all bind points and bound containers
1229 for (const auto i
: IntegerRange(state
.mBoundUbos
.size())) {
1230 if (state
.mBoundUbos
[i
] == obj
) {
1231 BindBufferBase(LOCAL_GL_UNIFORM_BUFFER
, i
, nullptr);
1235 // TFO only if not active
1236 if (!state
.mBoundTfo
->mActiveOrPaused
) {
1237 const auto& buffers
= state
.mBoundTfo
->mAttribBuffers
;
1238 for (const auto i
: IntegerRange(buffers
.size())) {
1239 if (buffers
[i
] == obj
) {
1240 BindBufferBase(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
, i
, nullptr);
1245 // Generic/global bind points
1246 for (const auto& pair
: state
.mBoundBufferByTarget
) {
1247 if (pair
.second
== obj
) {
1248 BindBuffer(pair
.first
, nullptr);
1253 if (state
.mBoundVao
->mIndexBuffer
== obj
) {
1254 BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER
, nullptr);
1257 const auto& vaoBuffers
= state
.mBoundVao
->mAttribBuffers
;
1258 Maybe
<WebGLBufferJS
*> toRestore
;
1259 for (const auto i
: IntegerRange(vaoBuffers
.size())) {
1260 if (vaoBuffers
[i
] == obj
) {
1263 Some(state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
].get());
1265 BindBuffer(LOCAL_GL_ARRAY_BUFFER
, nullptr);
1268 VertexAttribPointer(i
, 4, LOCAL_GL_FLOAT
, false, 0, 0);
1271 if (toRestore
&& *toRestore
) {
1272 BindBuffer(LOCAL_GL_ARRAY_BUFFER
, *toRestore
);
1277 obj
->mDeleteRequested
= true;
1278 Run
<RPROC(DeleteBuffer
)>(obj
->mId
);
1281 void ClientWebGLContext::DeleteFramebuffer(WebGLFramebufferJS
* const obj
,
1282 bool canDeleteOpaque
) {
1283 const FuncScope
funcScope(*this, "deleteFramebuffer");
1284 if (IsContextLost()) return;
1285 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1286 if (!canDeleteOpaque
&& obj
->mOpaque
) {
1288 LOCAL_GL_INVALID_OPERATION
,
1289 "An opaque framebuffer's attachments cannot be inspected or changed.");
1292 const auto& state
= State();
1295 const auto fnDetach
= [&](const GLenum target
,
1296 const WebGLFramebufferJS
* const fb
) {
1298 BindFramebuffer(target
, nullptr);
1301 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1302 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1304 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1305 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1308 obj
->mDeleteRequested
= true;
1309 Run
<RPROC(DeleteFramebuffer
)>(obj
->mId
);
1312 void ClientWebGLContext::DeleteProgram(WebGLProgramJS
* const obj
) const {
1313 const FuncScope
funcScope(*this, "deleteProgram");
1314 if (IsContextLost()) return;
1315 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1319 obj
->mKeepAlive
= nullptr;
1322 webgl::ProgramKeepAlive::~ProgramKeepAlive() {
1323 if (!mParent
) return;
1324 const auto& context
= mParent
->Context();
1325 if (!context
) return;
1326 context
->DoDeleteProgram(*mParent
);
1329 void ClientWebGLContext::DoDeleteProgram(WebGLProgramJS
& obj
) const {
1330 obj
.mNextLink_Shaders
= {};
1331 Run
<RPROC(DeleteProgram
)>(obj
.mId
);
1334 static GLenum
QuerySlotTarget(const GLenum specificTarget
);
1336 void ClientWebGLContext::DeleteQuery(WebGLQueryJS
* const obj
) {
1337 const FuncScope
funcScope(*this, "deleteQuery");
1338 if (IsContextLost()) return;
1339 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1340 const auto& state
= State();
1342 // Unbind if current
1345 const auto slotTarget
= QuerySlotTarget(obj
->mTarget
);
1346 const auto& curForTarget
=
1347 *MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
1349 if (curForTarget
== obj
) {
1350 EndQuery(obj
->mTarget
);
1354 obj
->mDeleteRequested
= true;
1355 Run
<RPROC(DeleteQuery
)>(obj
->mId
);
1358 void ClientWebGLContext::DeleteRenderbuffer(WebGLRenderbufferJS
* const obj
) {
1359 const FuncScope
funcScope(*this, "deleteRenderbuffer");
1360 if (IsContextLost()) return;
1361 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1362 const auto& state
= State();
1365 if (state
.mBoundRb
== obj
) {
1366 BindRenderbuffer(LOCAL_GL_RENDERBUFFER
, nullptr);
1369 // Unbind from bound FBs
1370 const auto fnDetach
= [&](const GLenum target
,
1371 const WebGLFramebufferJS
* const fb
) {
1373 for (const auto& pair
: fb
->mAttachments
) {
1374 if (pair
.second
.rb
== obj
) {
1375 FramebufferRenderbuffer(target
, pair
.first
, LOCAL_GL_RENDERBUFFER
,
1380 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1381 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1383 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1384 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1387 obj
->mDeleteRequested
= true;
1388 Run
<RPROC(DeleteRenderbuffer
)>(obj
->mId
);
1391 void ClientWebGLContext::DeleteSampler(WebGLSamplerJS
* const obj
) {
1392 const FuncScope
funcScope(*this, "deleteSampler");
1393 if (IsContextLost()) return;
1394 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1395 const auto& state
= State();
1398 for (const auto i
: IntegerRange(state
.mTexUnits
.size())) {
1399 if (state
.mTexUnits
[i
].sampler
== obj
) {
1400 BindSampler(i
, nullptr);
1404 obj
->mDeleteRequested
= true;
1405 Run
<RPROC(DeleteSampler
)>(obj
->mId
);
1408 void ClientWebGLContext::DeleteShader(WebGLShaderJS
* const obj
) const {
1409 const FuncScope
funcScope(*this, "deleteShader");
1410 if (IsContextLost()) return;
1411 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1415 obj
->mKeepAlive
= nullptr;
1418 webgl::ShaderKeepAlive::~ShaderKeepAlive() {
1419 if (!mParent
) return;
1420 const auto& context
= mParent
->Context();
1421 if (!context
) return;
1422 context
->DoDeleteShader(*mParent
);
1425 void ClientWebGLContext::DoDeleteShader(const WebGLShaderJS
& obj
) const {
1426 Run
<RPROC(DeleteShader
)>(obj
.mId
);
1429 void ClientWebGLContext::DeleteSync(WebGLSyncJS
* const obj
) const {
1430 const FuncScope
funcScope(*this, "deleteSync");
1431 if (IsContextLost()) return;
1432 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1434 // Nothing to unbind
1436 obj
->mDeleteRequested
= true;
1437 Run
<RPROC(DeleteSync
)>(obj
->mId
);
1440 void ClientWebGLContext::DeleteTexture(WebGLTextureJS
* const obj
) {
1441 const FuncScope
funcScope(*this, "deleteTexture");
1442 if (IsContextLost()) return;
1443 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1444 auto& state
= State();
1447 const auto& target
= obj
->mTarget
;
1449 // Unbind from tex units
1450 Maybe
<uint32_t> restoreTexUnit
;
1451 for (const auto i
: IntegerRange(state
.mTexUnits
.size())) {
1452 if (state
.mTexUnits
[i
].texByTarget
[target
] == obj
) {
1453 if (!restoreTexUnit
) {
1454 restoreTexUnit
= Some(state
.mActiveTexUnit
);
1456 ActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
1457 BindTexture(target
, nullptr);
1460 if (restoreTexUnit
) {
1461 ActiveTexture(LOCAL_GL_TEXTURE0
+ *restoreTexUnit
);
1464 // Unbind from bound FBs
1465 const auto fnDetach
= [&](const GLenum target
,
1466 const WebGLFramebufferJS
* const fb
) {
1468 for (const auto& pair
: fb
->mAttachments
) {
1469 if (pair
.second
.tex
== obj
) {
1470 FramebufferRenderbuffer(target
, pair
.first
, LOCAL_GL_RENDERBUFFER
,
1475 if (state
.mBoundDrawFb
== state
.mBoundReadFb
) {
1476 fnDetach(LOCAL_GL_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1478 fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER
, state
.mBoundDrawFb
.get());
1479 fnDetach(LOCAL_GL_READ_FRAMEBUFFER
, state
.mBoundReadFb
.get());
1483 obj
->mDeleteRequested
= true;
1484 Run
<RPROC(DeleteTexture
)>(obj
->mId
);
1487 void ClientWebGLContext::DeleteTransformFeedback(
1488 WebGLTransformFeedbackJS
* const obj
) {
1489 const FuncScope
funcScope(*this, "deleteTransformFeedback");
1490 if (IsContextLost()) return;
1491 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1492 const auto& state
= State();
1494 if (obj
->mActiveOrPaused
) {
1495 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
1496 "Transform Feedback object still active or paused.");
1501 if (state
.mBoundTfo
== obj
) {
1502 BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK
, nullptr);
1505 obj
->mDeleteRequested
= true;
1506 Run
<RPROC(DeleteTransformFeedback
)>(obj
->mId
);
1509 void ClientWebGLContext::DeleteVertexArray(WebGLVertexArrayJS
* const obj
) {
1510 const FuncScope
funcScope(*this, "deleteVertexArray");
1511 if (IsContextLost()) return;
1512 if (!ValidateOrSkipForDelete(*this, obj
)) return;
1513 const auto& state
= State();
1516 if (state
.mBoundVao
== obj
) {
1517 BindVertexArray(nullptr);
1520 obj
->mDeleteRequested
= true;
1521 Run
<RPROC(DeleteVertexArray
)>(obj
->mId
);
1526 bool ClientWebGLContext::IsBuffer(const WebGLBufferJS
* const obj
) const {
1527 const FuncScope
funcScope(*this, "isBuffer");
1528 if (IsContextLost()) return false;
1530 return obj
&& obj
->IsUsable(*this) &&
1531 obj
->mKind
!= webgl::BufferKind::Undefined
;
1534 bool ClientWebGLContext::IsFramebuffer(
1535 const WebGLFramebufferJS
* const obj
) const {
1536 const FuncScope
funcScope(*this, "isFramebuffer");
1537 if (IsContextLost()) return false;
1539 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1542 bool ClientWebGLContext::IsProgram(const WebGLProgramJS
* const obj
) const {
1543 const FuncScope
funcScope(*this, "isProgram");
1544 if (IsContextLost()) return false;
1546 return obj
&& obj
->IsUsable(*this);
1549 bool ClientWebGLContext::IsQuery(const WebGLQueryJS
* const obj
) const {
1550 const FuncScope
funcScope(*this, "isQuery");
1551 if (IsContextLost()) return false;
1553 return obj
&& obj
->IsUsable(*this) && obj
->mTarget
;
1556 bool ClientWebGLContext::IsRenderbuffer(
1557 const WebGLRenderbufferJS
* const obj
) const {
1558 const FuncScope
funcScope(*this, "isRenderbuffer");
1559 if (IsContextLost()) return false;
1561 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1564 bool ClientWebGLContext::IsSampler(const WebGLSamplerJS
* const obj
) const {
1565 const FuncScope
funcScope(*this, "isSampler");
1566 if (IsContextLost()) return false;
1568 return obj
&& obj
->IsUsable(*this);
1571 bool ClientWebGLContext::IsShader(const WebGLShaderJS
* const obj
) const {
1572 const FuncScope
funcScope(*this, "isShader");
1573 if (IsContextLost()) return false;
1575 return obj
&& obj
->IsUsable(*this);
1578 bool ClientWebGLContext::IsSync(const WebGLSyncJS
* const obj
) const {
1579 const FuncScope
funcScope(*this, "isSync");
1580 if (IsContextLost()) return false;
1582 return obj
&& obj
->IsUsable(*this);
1585 bool ClientWebGLContext::IsTexture(const WebGLTextureJS
* const obj
) const {
1586 const FuncScope
funcScope(*this, "isTexture");
1587 if (IsContextLost()) return false;
1589 return obj
&& obj
->IsUsable(*this) && obj
->mTarget
;
1592 bool ClientWebGLContext::IsTransformFeedback(
1593 const WebGLTransformFeedbackJS
* const obj
) const {
1594 const FuncScope
funcScope(*this, "isTransformFeedback");
1595 if (IsContextLost()) return false;
1597 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1600 bool ClientWebGLContext::IsVertexArray(
1601 const WebGLVertexArrayJS
* const obj
) const {
1602 const FuncScope
funcScope(*this, "isVertexArray");
1603 if (IsContextLost()) return false;
1605 return obj
&& obj
->IsUsable(*this) && obj
->mHasBeenBound
;
1608 // ------------------------- GL State -------------------------
1610 void ClientWebGLContext::Disable(GLenum cap
) const { Run
<RPROC(Disable
)>(cap
); }
1612 void ClientWebGLContext::Enable(GLenum cap
) const { Run
<RPROC(Enable
)>(cap
); }
1614 bool ClientWebGLContext::IsEnabled(GLenum cap
) const {
1615 const FuncScope
funcScope(*this, "isEnabled");
1616 const auto notLost
= mNotLost
;
1617 if (IsContextLost()) return false;
1619 const auto& inProcess
= notLost
->inProcess
;
1621 return inProcess
->IsEnabled(cap
);
1623 const auto& child
= notLost
->outOfProcess
;
1624 child
->FlushPendingCmds();
1626 if (!child
->SendIsEnabled(cap
, &ret
)) return false;
1630 void ClientWebGLContext::GetInternalformatParameter(
1631 JSContext
* cx
, GLenum target
, GLenum internalformat
, GLenum pname
,
1632 JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) {
1633 const FuncScope
funcScope(*this, "getInternalformatParameter");
1634 retval
.set(JS::NullValue());
1635 const auto notLost
=
1636 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
1637 if (IsContextLost()) return;
1639 const auto& inProcessContext
= notLost
->inProcess
;
1640 Maybe
<std::vector
<int32_t>> maybe
;
1641 if (inProcessContext
) {
1642 maybe
= inProcessContext
->GetInternalformatParameter(target
, internalformat
,
1645 const auto& child
= notLost
->outOfProcess
;
1646 child
->FlushPendingCmds();
1647 if (!child
->SendGetInternalformatParameter(target
, internalformat
, pname
,
1656 // zero-length array indicates out-of-memory
1658 dom::Int32Array::Create(cx
, this, maybe
->size(), maybe
->data());
1660 rv
= NS_ERROR_OUT_OF_MEMORY
;
1662 retval
.setObjectOrNull(obj
);
1665 static JS::Value
StringValue(JSContext
* cx
, const std::string
& str
,
1667 JSString
* jsStr
= JS_NewStringCopyN(cx
, str
.data(), str
.size());
1669 er
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1670 return JS::NullValue();
1673 return JS::StringValue(jsStr
);
1676 template <typename T
>
1677 bool ToJSValueOrNull(JSContext
* const cx
, const RefPtr
<T
>& ptr
,
1678 JS::MutableHandle
<JS::Value
> retval
) {
1680 retval
.set(JS::NullValue());
1683 return dom::ToJSValue(cx
, ptr
, retval
);
1686 template <typename T
, typename U
, typename S
>
1687 static JS::Value
CreateAs(JSContext
* cx
, nsWrapperCache
* creator
, const S
& src
,
1690 T::Create(cx
, creator
, src
.size(), reinterpret_cast<U
>(src
.data()));
1692 rv
= NS_ERROR_OUT_OF_MEMORY
;
1694 return JS::ObjectOrNullValue(obj
);
1697 template <typename T
, typename S
>
1698 static JS::Value
Create(JSContext
* cx
, nsWrapperCache
* creator
, const S
& src
,
1700 return CreateAs
<T
, decltype(&src
[0]), S
>(cx
, creator
, src
, rv
);
1703 Maybe
<double> ClientWebGLContext::GetNumber(const GLenum pname
) {
1704 MOZ_ASSERT(!IsContextLost());
1706 const auto& inProcess
= mNotLost
->inProcess
;
1708 return inProcess
->GetNumber(pname
);
1711 const auto& child
= mNotLost
->outOfProcess
;
1712 child
->FlushPendingCmds();
1715 if (!child
->SendGetNumber(pname
, &ret
)) {
1721 Maybe
<std::string
> ClientWebGLContext::GetString(const GLenum pname
) {
1722 MOZ_ASSERT(!IsContextLost());
1724 const auto& inProcess
= mNotLost
->inProcess
;
1726 return inProcess
->GetString(pname
);
1729 const auto& child
= mNotLost
->outOfProcess
;
1730 child
->FlushPendingCmds();
1732 Maybe
<std::string
> ret
;
1733 if (!child
->SendGetString(pname
, &ret
)) {
1739 void ClientWebGLContext::GetParameter(JSContext
* cx
, GLenum pname
,
1740 JS::MutableHandle
<JS::Value
> retval
,
1741 ErrorResult
& rv
, const bool debug
) {
1742 retval
.set(JS::NullValue());
1743 const FuncScope
funcScope(*this, "getParameter");
1744 if (IsContextLost()) return;
1745 const auto& limits
= Limits();
1746 const auto& state
= State();
1750 const auto fnSetRetval_Buffer
= [&](const GLenum target
) {
1751 const auto buffer
= *MaybeFind(state
.mBoundBufferByTarget
, target
);
1752 (void)ToJSValueOrNull(cx
, buffer
, retval
);
1754 const auto fnSetRetval_Tex
= [&](const GLenum texTarget
) {
1755 const auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
1756 const auto tex
= Find(texUnit
.texByTarget
, texTarget
, nullptr);
1757 (void)ToJSValueOrNull(cx
, tex
, retval
);
1761 case LOCAL_GL_ARRAY_BUFFER_BINDING
:
1762 fnSetRetval_Buffer(LOCAL_GL_ARRAY_BUFFER
);
1765 case LOCAL_GL_CURRENT_PROGRAM
:
1766 (void)ToJSValueOrNull(cx
, state
.mCurrentProgram
, retval
);
1769 case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING
:
1770 (void)ToJSValueOrNull(cx
, state
.mBoundVao
->mIndexBuffer
, retval
);
1773 case LOCAL_GL_FRAMEBUFFER_BINDING
:
1774 (void)ToJSValueOrNull(cx
, state
.mBoundDrawFb
, retval
);
1777 case LOCAL_GL_RENDERBUFFER_BINDING
:
1778 (void)ToJSValueOrNull(cx
, state
.mBoundRb
, retval
);
1781 case LOCAL_GL_TEXTURE_BINDING_2D
:
1782 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D
);
1785 case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP
:
1786 fnSetRetval_Tex(LOCAL_GL_TEXTURE_CUBE_MAP
);
1789 case LOCAL_GL_VERTEX_ARRAY_BINDING
: {
1791 !IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object
))
1794 auto ret
= state
.mBoundVao
;
1795 if (ret
== state
.mDefaultVao
) {
1798 (void)ToJSValueOrNull(cx
, ret
, retval
);
1802 case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
:
1803 retval
.set(JS::NumberValue(limits
.maxTexUnits
));
1805 case LOCAL_GL_MAX_TEXTURE_SIZE
:
1806 retval
.set(JS::NumberValue(limits
.maxTex2dSize
));
1808 case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE
:
1809 retval
.set(JS::NumberValue(limits
.maxTexCubeSize
));
1811 case LOCAL_GL_MAX_VERTEX_ATTRIBS
:
1812 retval
.set(JS::NumberValue(limits
.maxVertexAttribs
));
1815 case LOCAL_GL_MAX_VIEWS_OVR
:
1816 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
1817 retval
.set(JS::NumberValue(limits
.maxMultiviewLayers
));
1822 case LOCAL_GL_PACK_ALIGNMENT
:
1823 retval
.set(JS::NumberValue(state
.mPixelPackState
.alignment
));
1825 case LOCAL_GL_UNPACK_ALIGNMENT
:
1826 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.mUnpackAlignment
));
1829 case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL
:
1830 retval
.set(JS::BooleanValue(state
.mPixelUnpackState
.mFlipY
));
1832 case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL
:
1833 retval
.set(JS::BooleanValue(state
.mPixelUnpackState
.mPremultiplyAlpha
));
1835 case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL
:
1837 JS::NumberValue(state
.mPixelUnpackState
.mColorspaceConversion
));
1844 case LOCAL_GL_DEPTH_RANGE
:
1845 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mDepthRange
, rv
));
1848 case LOCAL_GL_ALIASED_POINT_SIZE_RANGE
:
1850 Create
<dom::Float32Array
>(cx
, this, limits
.pointSizeRange
, rv
));
1853 case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE
:
1855 Create
<dom::Float32Array
>(cx
, this, limits
.lineWidthRange
, rv
));
1859 case LOCAL_GL_COLOR_CLEAR_VALUE
:
1860 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mClearColor
, rv
));
1863 case LOCAL_GL_BLEND_COLOR
:
1864 retval
.set(Create
<dom::Float32Array
>(cx
, this, state
.mBlendColor
, rv
));
1868 case LOCAL_GL_MAX_VIEWPORT_DIMS
:
1869 retval
.set(CreateAs
<dom::Int32Array
, const int32_t*>(
1870 cx
, this, limits
.maxViewportDims
, rv
));
1874 case LOCAL_GL_SCISSOR_BOX
:
1875 retval
.set(Create
<dom::Int32Array
>(cx
, this, state
.mScissor
, rv
));
1878 case LOCAL_GL_VIEWPORT
:
1879 retval
.set(Create
<dom::Int32Array
>(cx
, this, state
.mViewport
, rv
));
1883 case LOCAL_GL_COLOR_WRITEMASK
: {
1884 JS::Rooted
<JS::Value
> arr(cx
);
1885 const auto& src
= state
.mColorWriteMask
;
1886 if (!dom::ToJSValue(cx
, src
.data(), src
.size(), &arr
)) {
1887 rv
= NS_ERROR_OUT_OF_MEMORY
;
1894 case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS
:
1895 retval
.set(Create
<dom::Uint32Array
>(cx
, this,
1896 state
.mCompressedTextureFormats
, rv
));
1902 case LOCAL_GL_COPY_READ_BUFFER_BINDING
:
1903 fnSetRetval_Buffer(LOCAL_GL_COPY_READ_BUFFER
);
1906 case LOCAL_GL_COPY_WRITE_BUFFER_BINDING
:
1907 fnSetRetval_Buffer(LOCAL_GL_COPY_WRITE_BUFFER
);
1910 case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
:
1911 (void)ToJSValueOrNull(cx
, state
.mBoundDrawFb
, retval
);
1914 case LOCAL_GL_PIXEL_PACK_BUFFER_BINDING
:
1915 fnSetRetval_Buffer(LOCAL_GL_PIXEL_PACK_BUFFER
);
1918 case LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING
:
1919 fnSetRetval_Buffer(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
1922 case LOCAL_GL_READ_FRAMEBUFFER_BINDING
:
1923 (void)ToJSValueOrNull(cx
, state
.mBoundReadFb
, retval
);
1926 case LOCAL_GL_SAMPLER_BINDING
: {
1927 const auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
1928 (void)ToJSValueOrNull(cx
, texUnit
.sampler
, retval
);
1932 case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY
:
1933 fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D_ARRAY
);
1936 case LOCAL_GL_TEXTURE_BINDING_3D
:
1937 fnSetRetval_Tex(LOCAL_GL_TEXTURE_3D
);
1940 case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING
: {
1941 auto ret
= state
.mBoundTfo
;
1942 if (ret
== state
.mDefaultTfo
) {
1945 (void)ToJSValueOrNull(cx
, ret
, retval
);
1949 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING
:
1950 fnSetRetval_Buffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
);
1953 case LOCAL_GL_UNIFORM_BUFFER_BINDING
:
1954 fnSetRetval_Buffer(LOCAL_GL_UNIFORM_BUFFER
);
1957 case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
:
1958 retval
.set(JS::NumberValue(limits
.maxTransformFeedbackSeparateAttribs
));
1960 case LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS
:
1961 retval
.set(JS::NumberValue(limits
.maxUniformBufferBindings
));
1963 case LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
:
1964 retval
.set(JS::NumberValue(limits
.uniformBufferOffsetAlignment
));
1966 case LOCAL_GL_MAX_3D_TEXTURE_SIZE
:
1967 retval
.set(JS::NumberValue(limits
.maxTex3dSize
));
1969 case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS
:
1970 retval
.set(JS::NumberValue(limits
.maxTexArrayLayers
));
1973 case LOCAL_GL_PACK_ROW_LENGTH
:
1974 retval
.set(JS::NumberValue(state
.mPixelPackState
.rowLength
));
1976 case LOCAL_GL_PACK_SKIP_PIXELS
:
1977 retval
.set(JS::NumberValue(state
.mPixelPackState
.skipPixels
));
1979 case LOCAL_GL_PACK_SKIP_ROWS
:
1980 retval
.set(JS::NumberValue(state
.mPixelPackState
.skipRows
));
1983 case LOCAL_GL_UNPACK_IMAGE_HEIGHT
:
1984 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.mUnpackImageHeight
));
1986 case LOCAL_GL_UNPACK_ROW_LENGTH
:
1987 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.mUnpackRowLength
));
1989 case LOCAL_GL_UNPACK_SKIP_IMAGES
:
1990 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.mUnpackSkipImages
));
1992 case LOCAL_GL_UNPACK_SKIP_PIXELS
:
1993 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.mUnpackSkipPixels
));
1995 case LOCAL_GL_UNPACK_SKIP_ROWS
:
1996 retval
.set(JS::NumberValue(state
.mPixelUnpackState
.mUnpackSkipRows
));
2004 const char* ret
= nullptr;
2007 case LOCAL_GL_VENDOR
:
2008 case LOCAL_GL_RENDERER
:
2012 case LOCAL_GL_VERSION
:
2020 case LOCAL_GL_SHADING_LANGUAGE_VERSION
:
2022 ret
= "WebGL GLSL ES 3.00";
2024 ret
= "WebGL GLSL ES 1.0";
2028 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL
:
2029 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL
: {
2030 if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info
)) {
2031 EnqueueError_ArgEnum("pname", pname
);
2035 const char* overridePref
;
2038 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL
:
2039 overridePref
= "webgl.renderer-string-override";
2040 driverEnum
= LOCAL_GL_RENDERER
;
2042 case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL
:
2043 overridePref
= "webgl.vendor-string-override";
2044 driverEnum
= LOCAL_GL_VENDOR
;
2050 nsCString overrideStr
;
2051 const auto res
= Preferences::GetCString(overridePref
, overrideStr
);
2052 if (NS_SUCCEEDED(res
) && overrideStr
.Length() > 0) {
2053 retval
.set(StringValue(cx
, overrideStr
.BeginReading(), rv
));
2057 const auto maybe
= GetString(driverEnum
);
2059 std::string renderer
= *maybe
;
2060 mozilla::dom::SanitizeRenderer(renderer
);
2061 retval
.set(StringValue(cx
, renderer
, rv
));
2071 retval
.set(StringValue(cx
, ret
, rv
));
2078 bool debugOnly
= false;
2079 bool asString
= false;
2082 case LOCAL_GL_EXTENSIONS
:
2083 case LOCAL_GL_RENDERER
:
2084 case LOCAL_GL_VENDOR
:
2085 case LOCAL_GL_VERSION
:
2086 case dom::MOZ_debug_Binding::WSI_INFO
:
2091 case dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION
:
2099 if (debugOnly
&& !debug
) {
2100 EnqueueError_ArgEnum("pname", pname
);
2107 const auto maybe
= GetString(pname
);
2110 if (pname
== dom::MOZ_debug_Binding::WSI_INFO
) {
2111 nsPrintfCString
more("\nIsWebglOutOfProcessEnabled: %i",
2112 int(IsWebglOutOfProcessEnabled()));
2113 str
+= more
.BeginReading();
2115 retval
.set(StringValue(cx
, str
.c_str(), rv
));
2118 const auto maybe
= GetNumber(pname
);
2122 case LOCAL_GL_BLEND
:
2123 case LOCAL_GL_CULL_FACE
:
2124 case LOCAL_GL_DEPTH_TEST
:
2125 case LOCAL_GL_DEPTH_WRITEMASK
:
2126 case LOCAL_GL_DITHER
:
2127 case LOCAL_GL_POLYGON_OFFSET_FILL
:
2128 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
:
2129 case LOCAL_GL_SAMPLE_COVERAGE
:
2130 case LOCAL_GL_SAMPLE_COVERAGE_INVERT
:
2131 case LOCAL_GL_SCISSOR_TEST
:
2132 case LOCAL_GL_STENCIL_TEST
:
2134 case LOCAL_GL_RASTERIZER_DISCARD
:
2135 case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE
:
2136 case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED
:
2137 retval
.set(JS::BooleanValue(*maybe
));
2141 retval
.set(JS::NumberValue(*maybe
));
2148 void ClientWebGLContext::GetBufferParameter(
2149 JSContext
* cx
, GLenum target
, GLenum pname
,
2150 JS::MutableHandle
<JS::Value
> retval
) const {
2151 retval
.set(JS::NullValue());
2152 if (IsContextLost()) return;
2154 const auto maybe
= [&]() {
2155 const auto& inProcess
= mNotLost
->inProcess
;
2157 return inProcess
->GetBufferParameter(target
, pname
);
2159 const auto& child
= mNotLost
->outOfProcess
;
2160 child
->FlushPendingCmds();
2162 if (!child
->SendGetBufferParameter(target
, pname
, &ret
)) {
2168 retval
.set(JS::NumberValue(*maybe
));
2172 bool IsFramebufferTarget(const bool isWebgl2
, const GLenum target
) {
2174 case LOCAL_GL_FRAMEBUFFER
:
2177 case LOCAL_GL_DRAW_FRAMEBUFFER
:
2178 case LOCAL_GL_READ_FRAMEBUFFER
:
2186 void ClientWebGLContext::GetFramebufferAttachmentParameter(
2187 JSContext
* const cx
, const GLenum target
, const GLenum attachment
,
2188 const GLenum pname
, JS::MutableHandle
<JS::Value
> retval
,
2189 ErrorResult
& rv
) const {
2190 retval
.set(JS::NullValue());
2191 const FuncScope
funcScope(*this, "getFramebufferAttachmentParameter");
2192 if (IsContextLost()) return;
2194 const auto& state
= State();
2196 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
2197 EnqueueError_ArgEnum("target", target
);
2200 auto fb
= state
.mBoundDrawFb
;
2201 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
2202 fb
= state
.mBoundReadFb
;
2205 const auto fnGet
= [&](const GLenum pname
) {
2206 const auto fbId
= fb
? fb
->mId
: 0;
2208 const auto& inProcess
= mNotLost
->inProcess
;
2210 return inProcess
->GetFramebufferAttachmentParameter(fbId
, attachment
,
2213 const auto& child
= mNotLost
->outOfProcess
;
2214 child
->FlushPendingCmds();
2216 if (!child
->SendGetFramebufferAttachmentParameter(fbId
, attachment
, pname
,
2225 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
2226 "An opaque framebuffer's attachments cannot be inspected or "
2230 auto attachmentSlotEnum
= attachment
;
2231 if (mIsWebGL2
&& attachment
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
2232 // In webgl2, DEPTH_STENCIL is valid iff the DEPTH and STENCIL images
2233 // match, so check if the server errors.
2234 const auto maybe
= fnGet(LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
);
2236 attachmentSlotEnum
= LOCAL_GL_DEPTH_ATTACHMENT
;
2239 const auto maybeSlot
= fb
->GetAttachment(attachmentSlotEnum
);
2241 EnqueueError_ArgEnum("attachment", attachment
);
2244 const auto& attached
= *maybeSlot
;
2248 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
) {
2250 (void)ToJSValueOrNull(cx
, attached
.rb
, retval
);
2252 if (!mIsWebGL2
&& !attached
.tex
) {
2253 EnqueueError_ArgEnum("pname", pname
);
2256 (void)ToJSValueOrNull(cx
, attached
.tex
, retval
);
2262 const auto maybe
= fnGet(pname
);
2264 retval
.set(JS::NumberValue(*maybe
));
2268 void ClientWebGLContext::GetRenderbufferParameter(
2269 JSContext
* cx
, GLenum target
, GLenum pname
,
2270 JS::MutableHandle
<JS::Value
> retval
) const {
2271 retval
.set(JS::NullValue());
2272 const FuncScope
funcScope(*this, "getRenderbufferParameter");
2273 if (IsContextLost()) return;
2275 if (target
!= LOCAL_GL_RENDERBUFFER
) {
2276 EnqueueError_ArgEnum("target", target
);
2280 const auto& state
= State();
2281 const auto& rb
= state
.mBoundRb
;
2282 const auto rbId
= rb
? rb
->mId
: 0;
2283 const auto maybe
= [&]() {
2284 const auto& inProcess
= mNotLost
->inProcess
;
2286 return inProcess
->GetRenderbufferParameter(rbId
, pname
);
2288 const auto& child
= mNotLost
->outOfProcess
;
2289 child
->FlushPendingCmds();
2291 if (!child
->SendGetRenderbufferParameter(rbId
, pname
, &ret
)) {
2297 retval
.set(JS::NumberValue(*maybe
));
2301 void ClientWebGLContext::GetIndexedParameter(
2302 JSContext
* cx
, GLenum target
, GLuint index
,
2303 JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) const {
2304 retval
.set(JS::NullValue());
2305 const FuncScope
funcScope(*this, "getIndexedParameter");
2306 if (IsContextLost()) return;
2308 const auto& state
= State();
2311 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING
: {
2312 const auto& list
= state
.mBoundTfo
->mAttribBuffers
;
2313 if (index
>= list
.size()) {
2314 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2315 "`index` (%u) >= MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS",
2319 (void)ToJSValueOrNull(cx
, list
[index
], retval
);
2323 case LOCAL_GL_UNIFORM_BUFFER_BINDING
: {
2324 const auto& list
= state
.mBoundUbos
;
2325 if (index
>= list
.size()) {
2326 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2327 "`index` (%u) >= MAX_UNIFORM_BUFFER_BINDINGS", index
);
2330 (void)ToJSValueOrNull(cx
, list
[index
], retval
);
2335 const auto maybe
= [&]() {
2336 const auto& inProcess
= mNotLost
->inProcess
;
2338 return inProcess
->GetIndexedParameter(target
, index
);
2340 const auto& child
= mNotLost
->outOfProcess
;
2341 child
->FlushPendingCmds();
2343 if (!child
->SendGetIndexedParameter(target
, index
, &ret
)) {
2349 retval
.set(JS::NumberValue(*maybe
));
2353 void ClientWebGLContext::GetUniform(JSContext
* const cx
,
2354 const WebGLProgramJS
& prog
,
2355 const WebGLUniformLocationJS
& loc
,
2356 JS::MutableHandle
<JS::Value
> retval
) {
2357 retval
.set(JS::NullValue());
2358 const FuncScope
funcScope(*this, "getUniform");
2359 if (IsContextLost()) return;
2360 if (!prog
.ValidateUsable(*this, "prog")) return;
2361 if (!loc
.ValidateUsable(*this, "loc")) return;
2363 const auto& activeLinkResult
= GetActiveLinkResult();
2364 if (!activeLinkResult
) {
2365 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No active linked Program.");
2368 const auto& reqLinkInfo
= loc
.mParent
.lock();
2369 if (reqLinkInfo
.get() != activeLinkResult
) {
2370 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
2371 "UniformLocation is not from the current active Program.");
2375 const auto res
= [&]() {
2376 const auto& inProcess
= mNotLost
->inProcess
;
2378 return inProcess
->GetUniform(prog
.mId
, loc
.mLocation
);
2380 const auto& child
= mNotLost
->outOfProcess
;
2381 child
->FlushPendingCmds();
2382 webgl::GetUniformData ret
;
2383 if (!child
->SendGetUniform(prog
.mId
, loc
.mLocation
, &ret
)) {
2388 if (!res
.type
) return;
2390 const auto elemCount
= ElemTypeComponents(res
.type
);
2391 MOZ_ASSERT(elemCount
);
2395 retval
.set(JS::BooleanValue(res
.data
[0]));
2398 case LOCAL_GL_FLOAT
: {
2399 const auto ptr
= reinterpret_cast<const float*>(res
.data
);
2400 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2403 case LOCAL_GL_INT
: {
2404 const auto ptr
= reinterpret_cast<const int32_t*>(res
.data
);
2405 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2408 case LOCAL_GL_UNSIGNED_INT
:
2409 case LOCAL_GL_SAMPLER_2D
:
2410 case LOCAL_GL_SAMPLER_3D
:
2411 case LOCAL_GL_SAMPLER_CUBE
:
2412 case LOCAL_GL_SAMPLER_2D_SHADOW
:
2413 case LOCAL_GL_SAMPLER_2D_ARRAY
:
2414 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
2415 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
2416 case LOCAL_GL_INT_SAMPLER_2D
:
2417 case LOCAL_GL_INT_SAMPLER_3D
:
2418 case LOCAL_GL_INT_SAMPLER_CUBE
:
2419 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
2420 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
2421 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
2422 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
2423 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
: {
2424 const auto ptr
= reinterpret_cast<const uint32_t*>(res
.data
);
2425 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, *ptr
, retval
));
2431 case LOCAL_GL_BOOL_VEC2
:
2432 case LOCAL_GL_BOOL_VEC3
:
2433 case LOCAL_GL_BOOL_VEC4
: {
2434 const auto intArr
= reinterpret_cast<const int32_t*>(res
.data
);
2435 bool boolArr
[4] = {};
2436 for (const auto i
: IntegerRange(elemCount
)) {
2437 boolArr
[i
] = bool(intArr
[i
]);
2439 MOZ_ALWAYS_TRUE(dom::ToJSValue(cx
, boolArr
, elemCount
, retval
));
2443 case LOCAL_GL_FLOAT_VEC2
:
2444 case LOCAL_GL_FLOAT_VEC3
:
2445 case LOCAL_GL_FLOAT_VEC4
:
2446 case LOCAL_GL_FLOAT_MAT2
:
2447 case LOCAL_GL_FLOAT_MAT3
:
2448 case LOCAL_GL_FLOAT_MAT4
:
2449 case LOCAL_GL_FLOAT_MAT2x3
:
2450 case LOCAL_GL_FLOAT_MAT2x4
:
2451 case LOCAL_GL_FLOAT_MAT3x2
:
2452 case LOCAL_GL_FLOAT_MAT3x4
:
2453 case LOCAL_GL_FLOAT_MAT4x2
:
2454 case LOCAL_GL_FLOAT_MAT4x3
: {
2455 const auto ptr
= reinterpret_cast<const float*>(res
.data
);
2456 JSObject
* obj
= dom::Float32Array::Create(cx
, this, elemCount
, ptr
);
2458 retval
.set(JS::ObjectOrNullValue(obj
));
2462 case LOCAL_GL_INT_VEC2
:
2463 case LOCAL_GL_INT_VEC3
:
2464 case LOCAL_GL_INT_VEC4
: {
2465 const auto ptr
= reinterpret_cast<const int32_t*>(res
.data
);
2466 JSObject
* obj
= dom::Int32Array::Create(cx
, this, elemCount
, ptr
);
2468 retval
.set(JS::ObjectOrNullValue(obj
));
2472 case LOCAL_GL_UNSIGNED_INT_VEC2
:
2473 case LOCAL_GL_UNSIGNED_INT_VEC3
:
2474 case LOCAL_GL_UNSIGNED_INT_VEC4
: {
2475 const auto ptr
= reinterpret_cast<const uint32_t*>(res
.data
);
2476 JSObject
* obj
= dom::Uint32Array::Create(cx
, this, elemCount
, ptr
);
2478 retval
.set(JS::ObjectOrNullValue(obj
));
2483 MOZ_CRASH("GFX: Invalid elemType.");
2487 already_AddRefed
<WebGLShaderPrecisionFormatJS
>
2488 ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype
,
2489 const GLenum precisiontype
) {
2490 if (IsContextLost()) return nullptr;
2491 const auto info
= [&]() {
2492 const auto& inProcess
= mNotLost
->inProcess
;
2494 return inProcess
->GetShaderPrecisionFormat(shadertype
, precisiontype
);
2496 const auto& child
= mNotLost
->outOfProcess
;
2497 child
->FlushPendingCmds();
2498 Maybe
<webgl::ShaderPrecisionFormat
> ret
;
2499 if (!child
->SendGetShaderPrecisionFormat(shadertype
, precisiontype
, &ret
)) {
2505 if (!info
) return nullptr;
2506 return AsAddRefed(new WebGLShaderPrecisionFormatJS(*info
));
2509 void ClientWebGLContext::BlendColor(GLclampf r
, GLclampf g
, GLclampf b
,
2511 const FuncScope
funcScope(*this, "blendColor");
2512 if (IsContextLost()) return;
2513 auto& state
= State();
2515 auto& cache
= state
.mBlendColor
;
2521 Run
<RPROC(BlendColor
)>(r
, g
, b
, a
);
2524 void ClientWebGLContext::BlendEquationSeparate(GLenum modeRGB
,
2526 Run
<RPROC(BlendEquationSeparate
)>(modeRGB
, modeAlpha
);
2529 void ClientWebGLContext::BlendFuncSeparate(GLenum srcRGB
, GLenum dstRGB
,
2530 GLenum srcAlpha
, GLenum dstAlpha
) {
2531 Run
<RPROC(BlendFuncSeparate
)>(srcRGB
, dstRGB
, srcAlpha
, dstAlpha
);
2534 GLenum
ClientWebGLContext::CheckFramebufferStatus(GLenum target
) {
2535 if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
2537 const auto& inProcess
= mNotLost
->inProcess
;
2539 return inProcess
->CheckFramebufferStatus(target
);
2541 const auto& child
= mNotLost
->outOfProcess
;
2542 child
->FlushPendingCmds();
2544 if (!child
->SendCheckFramebufferStatus(target
, &ret
)) {
2550 void ClientWebGLContext::Clear(GLbitfield mask
) {
2551 Run
<RPROC(Clear
)>(mask
);
2558 void ClientWebGLContext::ClearBufferTv(const GLenum buffer
,
2559 const GLint drawBuffer
,
2560 const webgl::AttribBaseType type
,
2561 const Range
<const uint8_t>& view
,
2562 const GLuint srcElemOffset
) {
2563 const FuncScope
funcScope(*this, "clearBufferu?[fi]v");
2564 if (IsContextLost()) return;
2566 const auto byteOffset
= CheckedInt
<size_t>(srcElemOffset
) * sizeof(float);
2567 if (!byteOffset
.isValid() || byteOffset
.value() > view
.length()) {
2568 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`srcOffset` too large for `values`.");
2571 webgl::TypedQuad data
;
2574 auto dataSize
= sizeof(data
.data
);
2576 case LOCAL_GL_COLOR
:
2579 case LOCAL_GL_DEPTH
:
2580 dataSize
= sizeof(float);
2583 case LOCAL_GL_STENCIL
:
2584 dataSize
= sizeof(int32_t);
2588 EnqueueError_ArgEnum("buffer", buffer
);
2592 const auto requiredBytes
= byteOffset
+ dataSize
;
2593 if (!requiredBytes
.isValid() || requiredBytes
.value() > view
.length()) {
2594 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`values` too small.");
2598 memcpy(data
.data
, view
.begin().get() + byteOffset
.value(), dataSize
);
2599 Run
<RPROC(ClearBufferTv
)>(buffer
, drawBuffer
, data
);
2604 void ClientWebGLContext::ClearBufferfi(GLenum buffer
, GLint drawBuffer
,
2605 GLfloat depth
, GLint stencil
) {
2606 Run
<RPROC(ClearBufferfi
)>(buffer
, drawBuffer
, depth
, stencil
);
2613 void ClientWebGLContext::ClearColor(GLclampf r
, GLclampf g
, GLclampf b
,
2615 const FuncScope
funcScope(*this, "clearColor");
2616 if (IsContextLost()) return;
2617 auto& state
= State();
2619 auto& cache
= state
.mClearColor
;
2625 Run
<RPROC(ClearColor
)>(r
, g
, b
, a
);
2628 void ClientWebGLContext::ClearDepth(GLclampf v
) { Run
<RPROC(ClearDepth
)>(v
); }
2630 void ClientWebGLContext::ClearStencil(GLint v
) { Run
<RPROC(ClearStencil
)>(v
); }
2632 void ClientWebGLContext::ColorMask(WebGLboolean r
, WebGLboolean g
,
2633 WebGLboolean b
, WebGLboolean a
) {
2634 const FuncScope
funcScope(*this, "colorMask");
2635 if (IsContextLost()) return;
2636 auto& state
= State();
2638 state
.mColorWriteMask
= {r
, g
, b
, a
};
2640 Run
<RPROC(ColorMask
)>(r
, g
, b
, a
);
2643 void ClientWebGLContext::CullFace(GLenum face
) { Run
<RPROC(CullFace
)>(face
); }
2645 void ClientWebGLContext::DepthFunc(GLenum func
) { Run
<RPROC(DepthFunc
)>(func
); }
2647 void ClientWebGLContext::DepthMask(WebGLboolean b
) { Run
<RPROC(DepthMask
)>(b
); }
2649 void ClientWebGLContext::DepthRange(GLclampf zNear
, GLclampf zFar
) {
2650 const FuncScope
funcScope(*this, "depthRange");
2651 if (IsContextLost()) return;
2652 auto& state
= State();
2654 state
.mDepthRange
= {zNear
, zFar
};
2656 Run
<RPROC(DepthRange
)>(zNear
, zFar
);
2659 void ClientWebGLContext::Flush() {
2660 const FuncScope
funcScope(*this, "flush");
2661 const auto notLost
= mNotLost
;
2662 if (IsContextLost()) return;
2664 Run
<RPROC(Flush
)>();
2666 if (notLost
->inProcess
) return;
2667 const auto& child
= mNotLost
->outOfProcess
;
2668 child
->FlushPendingCmds();
2671 void ClientWebGLContext::Finish() {
2672 if (IsContextLost()) return;
2674 const auto& inProcess
= mNotLost
->inProcess
;
2676 inProcess
->Finish();
2679 const auto& child
= mNotLost
->outOfProcess
;
2680 child
->FlushPendingCmds();
2681 (void)child
->SendFinish();
2684 void ClientWebGLContext::FrontFace(GLenum mode
) { Run
<RPROC(FrontFace
)>(mode
); }
2686 GLenum
ClientWebGLContext::GetError() {
2687 const auto notLost
= mNotLost
;
2689 const auto ret
= mNextError
;
2693 if (IsContextLost()) return 0;
2695 const auto& inProcess
= notLost
->inProcess
;
2697 return inProcess
->GetError();
2699 const auto& child
= notLost
->outOfProcess
;
2700 child
->FlushPendingCmds();
2702 if (!child
->SendGetError(&ret
)) {
2708 void ClientWebGLContext::Hint(GLenum target
, GLenum mode
) {
2709 Run
<RPROC(Hint
)>(target
, mode
);
2712 void ClientWebGLContext::LineWidth(GLfloat width
) {
2713 Run
<RPROC(LineWidth
)>(width
);
2716 Maybe
<webgl::ErrorInfo
> SetPixelUnpack(const bool isWebgl2
,
2717 WebGLPixelStore
* const unpacking
,
2718 const GLenum pname
, const GLint param
);
2720 void ClientWebGLContext::PixelStorei(const GLenum pname
, const GLint iparam
) {
2721 const FuncScope
funcScope(*this, "pixelStorei");
2722 if (IsContextLost()) return;
2723 if (!ValidateNonNegative("param", iparam
)) return;
2724 const auto param
= static_cast<uint32_t>(iparam
);
2726 auto& state
= State();
2727 auto& packState
= state
.mPixelPackState
;
2729 case LOCAL_GL_PACK_ALIGNMENT
:
2737 EnqueueError(LOCAL_GL_INVALID_VALUE
,
2738 "PACK_ALIGNMENT must be one of [1,2,4,8], was %i.",
2742 packState
.alignment
= param
;
2745 case LOCAL_GL_PACK_ROW_LENGTH
:
2746 if (!mIsWebGL2
) break;
2747 packState
.rowLength
= param
;
2750 case LOCAL_GL_PACK_SKIP_PIXELS
:
2751 if (!mIsWebGL2
) break;
2752 packState
.skipPixels
= param
;
2755 case LOCAL_GL_PACK_SKIP_ROWS
:
2756 if (!mIsWebGL2
) break;
2757 packState
.skipRows
= param
;
2760 case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH
:
2761 if (!IsSupported(WebGLExtensionID::MOZ_debug
)) {
2762 EnqueueError_ArgEnum("pname", pname
);
2772 SetPixelUnpack(mIsWebGL2
, &state
.mPixelUnpackState
, pname
, iparam
);
2779 void ClientWebGLContext::PolygonOffset(GLfloat factor
, GLfloat units
) {
2780 Run
<RPROC(PolygonOffset
)>(factor
, units
);
2783 void ClientWebGLContext::SampleCoverage(GLclampf value
, WebGLboolean invert
) {
2784 Run
<RPROC(SampleCoverage
)>(value
, invert
);
2787 void ClientWebGLContext::Scissor(GLint x
, GLint y
, GLsizei width
,
2789 const FuncScope
funcScope(*this, "scissor");
2790 if (IsContextLost()) return;
2791 auto& state
= State();
2793 if (!ValidateNonNegative("width", width
) ||
2794 !ValidateNonNegative("height", height
)) {
2798 state
.mScissor
= {x
, y
, width
, height
};
2800 Run
<RPROC(Scissor
)>(x
, y
, width
, height
);
2803 void ClientWebGLContext::StencilFuncSeparate(GLenum face
, GLenum func
,
2804 GLint ref
, GLuint mask
) {
2805 Run
<RPROC(StencilFuncSeparate
)>(face
, func
, ref
, mask
);
2808 void ClientWebGLContext::StencilMaskSeparate(GLenum face
, GLuint mask
) {
2809 Run
<RPROC(StencilMaskSeparate
)>(face
, mask
);
2812 void ClientWebGLContext::StencilOpSeparate(GLenum face
, GLenum sfail
,
2813 GLenum dpfail
, GLenum dppass
) {
2814 Run
<RPROC(StencilOpSeparate
)>(face
, sfail
, dpfail
, dppass
);
2817 void ClientWebGLContext::Viewport(GLint x
, GLint y
, GLsizei width
,
2819 const FuncScope
funcScope(*this, "viewport");
2820 if (IsContextLost()) return;
2821 auto& state
= State();
2823 if (!ValidateNonNegative("width", width
) ||
2824 !ValidateNonNegative("height", height
)) {
2828 state
.mViewport
= {x
, y
, width
, height
};
2830 Run
<RPROC(Viewport
)>(x
, y
, width
, height
);
2833 // ------------------------- Buffer Objects -------------------------
2835 Maybe
<const webgl::ErrorInfo
> ValidateBindBuffer(
2836 const GLenum target
, const webgl::BufferKind curKind
) {
2837 if (curKind
== webgl::BufferKind::Undefined
) return {};
2839 auto requiredKind
= webgl::BufferKind::NonIndex
;
2841 case LOCAL_GL_COPY_READ_BUFFER
:
2842 case LOCAL_GL_COPY_WRITE_BUFFER
:
2843 return {}; // Always ok
2845 case LOCAL_GL_ELEMENT_ARRAY_BUFFER
:
2846 requiredKind
= webgl::BufferKind::Index
;
2853 if (curKind
!= requiredKind
) {
2854 const auto fnKindStr
= [&](const webgl::BufferKind kind
) {
2855 if (kind
== webgl::BufferKind::Index
) return "ELEMENT_ARRAY_BUFFER";
2856 return "non-ELEMENT_ARRAY_BUFFER";
2858 const auto info
= nsPrintfCString(
2859 "Buffer previously bound to %s cannot be now bound to %s.",
2860 fnKindStr(curKind
), fnKindStr(requiredKind
));
2862 webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
, info
.BeginReading()});
2868 Maybe
<webgl::ErrorInfo
> CheckBindBufferRange(
2869 const GLenum target
, const GLuint index
, const bool isBuffer
,
2870 const uint64_t offset
, const uint64_t size
, const webgl::Limits
& limits
) {
2871 const auto fnSome
= [&](const GLenum type
, const nsACString
& info
) {
2872 return Some(webgl::ErrorInfo
{type
, info
.BeginReading()});
2876 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
:
2877 if (index
>= limits
.maxTransformFeedbackSeparateAttribs
) {
2878 const auto info
= nsPrintfCString(
2879 "`index` (%u) must be less than "
2880 "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS (%u).",
2881 index
, limits
.maxTransformFeedbackSeparateAttribs
);
2882 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
2886 if (offset
% 4 != 0 || size
% 4 != 0) {
2888 nsPrintfCString("`offset` (%" PRIu64
") and `size` (%" PRIu64
2889 ") must both be aligned to 4 for"
2890 " TRANSFORM_FEEDBACK_BUFFER.",
2892 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
2897 case LOCAL_GL_UNIFORM_BUFFER
:
2898 if (index
>= limits
.maxUniformBufferBindings
) {
2899 const auto info
= nsPrintfCString(
2900 "`index` (%u) must be less than MAX_UNIFORM_BUFFER_BINDINGS (%u).",
2901 index
, limits
.maxUniformBufferBindings
);
2902 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
2906 if (offset
% limits
.uniformBufferOffsetAlignment
!= 0) {
2908 nsPrintfCString("`offset` (%" PRIu64
2909 ") must be aligned to "
2910 "UNIFORM_BUFFER_OFFSET_ALIGNMENT (%u).",
2911 offset
, limits
.uniformBufferOffsetAlignment
);
2912 return fnSome(LOCAL_GL_INVALID_VALUE
, info
);
2919 nsPrintfCString("Unrecognized `target`: 0x%04x", target
);
2920 return fnSome(LOCAL_GL_INVALID_ENUM
, info
);
2929 void ClientWebGLContext::BindBuffer(const GLenum target
,
2930 WebGLBufferJS
* const buffer
) {
2931 const FuncScope
funcScope(*this, "bindBuffer");
2932 if (IsContextLost()) return;
2933 if (buffer
&& !buffer
->ValidateUsable(*this, "buffer")) return;
2936 // Check for INVALID_ENUM
2938 auto& state
= State();
2939 auto* slot
= &(state
.mBoundVao
->mIndexBuffer
);
2940 if (target
!= LOCAL_GL_ELEMENT_ARRAY_BUFFER
) {
2941 const auto itr
= state
.mBoundBufferByTarget
.find(target
);
2942 if (itr
== state
.mBoundBufferByTarget
.end()) {
2943 EnqueueError_ArgEnum("target", target
);
2946 slot
= &(itr
->second
);
2951 auto kind
= webgl::BufferKind::Undefined
;
2953 kind
= buffer
->mKind
;
2955 const auto err
= ValidateBindBuffer(target
, kind
);
2957 EnqueueError(err
->type
, "%s", err
->info
.c_str());
2962 // Validation complete
2964 if (buffer
&& buffer
->mKind
== webgl::BufferKind::Undefined
) {
2965 if (target
== LOCAL_GL_ELEMENT_ARRAY_BUFFER
) {
2966 buffer
->mKind
= webgl::BufferKind::Index
;
2968 buffer
->mKind
= webgl::BufferKind::NonIndex
;
2975 Run
<RPROC(BindBuffer
)>(target
, buffer
? buffer
->mId
: 0);
2980 void ClientWebGLContext::BindBufferRangeImpl(const GLenum target
,
2982 WebGLBufferJS
* const buffer
,
2983 const uint64_t offset
,
2984 const uint64_t size
) {
2985 if (buffer
&& !buffer
->ValidateUsable(*this, "buffer")) return;
2986 auto& state
= State();
2990 const auto& limits
= Limits();
2992 CheckBindBufferRange(target
, index
, bool(buffer
), offset
, size
, limits
);
2994 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3000 auto kind
= webgl::BufferKind::Undefined
;
3002 kind
= buffer
->mKind
;
3004 err
= ValidateBindBuffer(target
, kind
);
3006 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3010 if (target
== LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
) {
3011 if (state
.mTfActiveAndNotPaused
) {
3012 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3013 "Cannot change TRANSFORM_FEEDBACK_BUFFER while "
3014 "TransformFeedback is active and not paused.");
3020 // Validation complete
3022 if (buffer
&& buffer
->mKind
== webgl::BufferKind::Undefined
) {
3023 buffer
->mKind
= webgl::BufferKind::NonIndex
;
3029 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER
:
3030 state
.mBoundTfo
->mAttribBuffers
[index
] = buffer
;
3033 case LOCAL_GL_UNIFORM_BUFFER
:
3034 state
.mBoundUbos
[index
] = buffer
;
3038 MOZ_CRASH("Bad `target`");
3040 state
.mBoundBufferByTarget
[target
] = buffer
;
3044 Run
<RPROC(BindBufferRange
)>(target
, index
, buffer
? buffer
->mId
: 0, offset
,
3048 void ClientWebGLContext::GetBufferSubData(GLenum target
, GLintptr srcByteOffset
,
3049 const dom::ArrayBufferView
& dstData
,
3050 GLuint dstElemOffset
,
3051 GLuint dstElemCountOverride
) {
3052 const FuncScope
funcScope(*this, "getBufferSubData");
3053 if (IsContextLost()) return;
3054 const auto notLost
=
3055 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
3056 if (!ValidateNonNegative("srcByteOffset", srcByteOffset
)) return;
3060 if (!ValidateArrayBufferView(dstData
, dstElemOffset
, dstElemCountOverride
,
3061 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
3064 const auto destView
= Range
<uint8_t>{bytes
, byteLen
};
3066 const auto& inProcessContext
= notLost
->inProcess
;
3067 if (inProcessContext
) {
3068 inProcessContext
->GetBufferSubData(target
, srcByteOffset
, destView
);
3072 const auto& child
= notLost
->outOfProcess
;
3073 child
->FlushPendingCmds();
3074 mozilla::ipc::Shmem rawShmem
;
3075 if (!child
->SendGetBufferSubData(target
, srcByteOffset
, destView
.length(),
3079 const webgl::RaiiShmem shmem
{child
, rawShmem
};
3081 const auto shmemView
= shmem
.ByteRange();
3082 MOZ_RELEASE_ASSERT(shmemView
.length() == 1 + destView
.length());
3084 const auto ok
= bool(*(shmemView
.begin().get()));
3085 const auto srcView
=
3086 Range
<const uint8_t>{shmemView
.begin() + 1, shmemView
.end()};
3088 Memcpy(destView
.begin(), srcView
.begin(), srcView
.length());
3094 void ClientWebGLContext::BufferData(GLenum target
, WebGLsizeiptr rawSize
,
3096 const FuncScope
funcScope(*this, "bufferData");
3097 if (!ValidateNonNegative("size", rawSize
)) return;
3099 const auto size
= MaybeAs
<size_t>(rawSize
);
3101 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
, "`size` too large for platform.");
3105 const auto data
= RawBuffer
<>{*size
};
3106 Run
<RPROC(BufferData
)>(target
, data
, usage
);
3109 void ClientWebGLContext::BufferData(
3110 GLenum target
, const dom::Nullable
<dom::ArrayBuffer
>& maybeSrc
,
3112 const FuncScope
funcScope(*this, "bufferData");
3113 if (!ValidateNonNull("src", maybeSrc
)) return;
3114 const auto& src
= maybeSrc
.Value();
3117 const auto range
= Range
<const uint8_t>{src
.Data(), src
.Length()};
3118 Run
<RPROC(BufferData
)>(target
, RawBuffer
<>(range
), usage
);
3121 void ClientWebGLContext::BufferData(GLenum target
,
3122 const dom::ArrayBufferView
& src
,
3123 GLenum usage
, GLuint srcElemOffset
,
3124 GLuint srcElemCountOverride
) {
3125 const FuncScope
funcScope(*this, "bufferData");
3128 if (!ValidateArrayBufferView(src
, srcElemOffset
, srcElemCountOverride
,
3129 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
3132 const auto range
= Range
<const uint8_t>{bytes
, byteLen
};
3133 Run
<RPROC(BufferData
)>(target
, RawBuffer
<>(range
), usage
);
3138 void ClientWebGLContext::BufferSubData(GLenum target
,
3139 WebGLsizeiptr dstByteOffset
,
3140 const dom::ArrayBuffer
& src
) {
3141 const FuncScope
funcScope(*this, "bufferSubData");
3143 const auto range
= Range
<const uint8_t>{src
.Data(), src
.Length()};
3144 Run
<RPROC(BufferSubData
)>(target
, dstByteOffset
, RawBuffer
<>(range
));
3147 void ClientWebGLContext::BufferSubData(GLenum target
,
3148 WebGLsizeiptr dstByteOffset
,
3149 const dom::ArrayBufferView
& src
,
3150 GLuint srcElemOffset
,
3151 GLuint srcElemCountOverride
) {
3152 const FuncScope
funcScope(*this, "bufferSubData");
3155 if (!ValidateArrayBufferView(src
, srcElemOffset
, srcElemCountOverride
,
3156 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
3159 const auto range
= Range
<const uint8_t>{bytes
, byteLen
};
3160 Run
<RPROC(BufferSubData
)>(target
, dstByteOffset
, RawBuffer
<>(range
));
3163 void ClientWebGLContext::CopyBufferSubData(GLenum readTarget
,
3165 GLintptr readOffset
,
3166 GLintptr writeOffset
,
3168 const FuncScope
funcScope(*this, "copyBufferSubData");
3169 if (!ValidateNonNegative("readOffset", readOffset
) ||
3170 !ValidateNonNegative("writeOffset", writeOffset
) ||
3171 !ValidateNonNegative("size", size
)) {
3174 Run
<RPROC(CopyBufferSubData
)>(
3175 readTarget
, writeTarget
, static_cast<uint64_t>(readOffset
),
3176 static_cast<uint64_t>(writeOffset
), static_cast<uint64_t>(size
));
3179 // -------------------------- Framebuffer Objects --------------------------
3181 void ClientWebGLContext::BindFramebuffer(const GLenum target
,
3182 WebGLFramebufferJS
* const fb
) {
3183 const FuncScope
funcScope(*this, "bindFramebuffer");
3184 if (IsContextLost()) return;
3185 if (fb
&& !fb
->ValidateUsable(*this, "fb")) return;
3187 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
3188 EnqueueError_ArgEnum("target", target
);
3194 auto& state
= State();
3197 case LOCAL_GL_FRAMEBUFFER
:
3198 state
.mBoundDrawFb
= fb
;
3199 state
.mBoundReadFb
= fb
;
3202 case LOCAL_GL_DRAW_FRAMEBUFFER
:
3203 state
.mBoundDrawFb
= fb
;
3205 case LOCAL_GL_READ_FRAMEBUFFER
:
3206 state
.mBoundReadFb
= fb
;
3216 fb
->mHasBeenBound
= true;
3219 Run
<RPROC(BindFramebuffer
)>(target
, fb
? fb
->mId
: 0);
3224 void ClientWebGLContext::FramebufferTexture2D(GLenum target
, GLenum attachSlot
,
3225 GLenum bindImageTarget
,
3226 WebGLTextureJS
* const tex
,
3227 GLint mipLevel
) const {
3228 const FuncScope
funcScope(*this, "framebufferTexture2D");
3229 if (IsContextLost()) return;
3231 const auto bindTexTarget
= ImageToTexTarget(bindImageTarget
);
3232 uint32_t zLayer
= 0;
3233 switch (bindTexTarget
) {
3234 case LOCAL_GL_TEXTURE_2D
:
3236 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3237 zLayer
= bindImageTarget
- LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
;
3240 EnqueueError_ArgEnum("imageTarget", bindImageTarget
);
3245 !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap
)) {
3246 if (mipLevel
!= 0) {
3247 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3248 "mipLevel != 0 requires OES_fbo_render_mipmap.");
3253 FramebufferAttach(target
, attachSlot
, bindImageTarget
, nullptr, tex
,
3254 static_cast<uint32_t>(mipLevel
), zLayer
, 0);
3257 Maybe
<webgl::ErrorInfo
> CheckFramebufferAttach(const GLenum bindImageTarget
,
3258 const GLenum curTexTarget
,
3259 const uint32_t mipLevel
,
3260 const uint32_t zLayerBase
,
3261 const uint32_t zLayerCount
,
3262 const webgl::Limits
& limits
) {
3263 if (!curTexTarget
) {
3265 webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3266 "`tex` not yet bound. Call bindTexture first."});
3269 auto texTarget
= curTexTarget
;
3270 if (bindImageTarget
) {
3271 // FramebufferTexture2D
3272 const auto bindTexTarget
= ImageToTexTarget(bindImageTarget
);
3273 if (curTexTarget
!= bindTexTarget
) {
3274 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3275 "`tex` cannot be rebound to a new target."});
3278 switch (bindTexTarget
) {
3279 case LOCAL_GL_TEXTURE_2D
:
3280 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3283 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_ENUM
,
3284 "`tex` must have been bound to target "
3285 "TEXTURE_2D or TEXTURE_CUBE_MAP."});
3287 texTarget
= bindTexTarget
;
3289 // FramebufferTextureLayer/Multiview
3290 switch (curTexTarget
) {
3291 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3292 case LOCAL_GL_TEXTURE_3D
:
3295 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_OPERATION
,
3296 "`tex` must have been bound to target "
3297 "TEXTURE_2D_ARRAY or TEXTURE_3D."});
3300 MOZ_ASSERT(texTarget
);
3303 switch (texTarget
) {
3304 case LOCAL_GL_TEXTURE_2D
:
3305 maxSize
= limits
.maxTex2dSize
;
3308 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3309 maxSize
= limits
.maxTexCubeSize
;
3312 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3313 maxSize
= limits
.maxTex2dSize
;
3314 maxZ
= limits
.maxTexArrayLayers
;
3316 case LOCAL_GL_TEXTURE_3D
:
3317 maxSize
= limits
.maxTex3dSize
;
3318 maxZ
= limits
.maxTex3dSize
;
3323 const auto maxMipLevel
= FloorLog2(maxSize
);
3324 if (mipLevel
> maxMipLevel
) {
3325 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
,
3326 "`mipLevel` too large for texture target."});
3328 const auto requiredZLayers
= CheckedInt
<uint32_t>(zLayerBase
) + zLayerCount
;
3329 if (!requiredZLayers
.isValid() || requiredZLayers
.value() > maxZ
) {
3330 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
,
3331 "`zLayer` too large for texture target."});
3337 void ClientWebGLContext::FramebufferAttach(
3338 const GLenum target
, const GLenum attachSlot
, const GLenum bindImageTarget
,
3339 WebGLRenderbufferJS
* const rb
, WebGLTextureJS
* const tex
,
3340 const uint32_t mipLevel
, const uint32_t zLayerBase
,
3341 const uint32_t numViewLayers
) const {
3342 if (rb
&& !rb
->ValidateUsable(*this, "rb")) return;
3343 if (tex
&& !tex
->ValidateUsable(*this, "tex")) return;
3344 const auto& state
= State();
3345 const auto& limits
= Limits();
3347 if (!IsFramebufferTarget(mIsWebGL2
, target
)) {
3348 EnqueueError_ArgEnum("target", target
);
3351 auto fb
= state
.mBoundDrawFb
;
3352 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
3353 fb
= state
.mBoundReadFb
;
3356 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No framebuffer bound.");
3362 LOCAL_GL_INVALID_OPERATION
,
3363 "An opaque framebuffer's attachments cannot be inspected or changed.");
3368 // Multiview-specific validation skipped by Host.
3370 if (tex
&& numViewLayers
) {
3371 if (tex
->mTarget
!= LOCAL_GL_TEXTURE_2D_ARRAY
) {
3372 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3373 "`tex` must have been bound to target TEXTURE_2D_ARRAY.");
3376 if (numViewLayers
> limits
.maxMultiviewLayers
) {
3377 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3378 "`numViews` (%u) must be <= MAX_VIEWS (%u).", numViewLayers
,
3379 limits
.maxMultiviewLayers
);
3386 webgl::ObjectId id
= 0;
3388 auto zLayerCount
= numViewLayers
;
3393 CheckFramebufferAttach(bindImageTarget
, tex
->mTarget
, mipLevel
,
3394 zLayerBase
, zLayerCount
, limits
);
3396 EnqueueError(err
->type
, "%s", err
->info
.c_str());
3401 if (!rb
->mHasBeenBound
) {
3402 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3403 "`rb` has not yet been bound with BindRenderbuffer.");
3410 // But DEPTH_STENCIL in webgl2 is actually two slots!
3412 const auto fnAttachTo
= [&](const GLenum actualAttachSlot
) {
3413 const auto slot
= fb
->GetAttachment(actualAttachSlot
);
3415 EnqueueError_ArgEnum("attachment", actualAttachSlot
);
3422 Run
<RPROC(FramebufferAttach
)>(target
, actualAttachSlot
, bindImageTarget
, id
,
3423 mipLevel
, zLayerBase
, numViewLayers
);
3426 if (mIsWebGL2
&& attachSlot
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
3427 fnAttachTo(LOCAL_GL_DEPTH_ATTACHMENT
);
3428 fnAttachTo(LOCAL_GL_STENCIL_ATTACHMENT
);
3430 fnAttachTo(attachSlot
);
3433 if (bindImageTarget
) {
3435 rb
->mHasBeenBound
= true;
3438 tex
->mTarget
= ImageToTexTarget(bindImageTarget
);
3445 void ClientWebGLContext::BlitFramebuffer(GLint srcX0
, GLint srcY0
, GLint srcX1
,
3446 GLint srcY1
, GLint dstX0
, GLint dstY0
,
3447 GLint dstX1
, GLint dstY1
,
3448 GLbitfield mask
, GLenum filter
) {
3449 Run
<RPROC(BlitFramebuffer
)>(srcX0
, srcY0
, srcX1
, srcY1
, dstX0
, dstY0
, dstX1
,
3450 dstY1
, mask
, filter
);
3455 void ClientWebGLContext::InvalidateFramebuffer(
3456 GLenum target
, const dom::Sequence
<GLenum
>& attachments
,
3457 ErrorResult
& unused
) {
3458 const auto range
= MakeRange(attachments
);
3459 const auto& buffer
= RawBufferView(range
);
3460 Run
<RPROC(InvalidateFramebuffer
)>(target
, buffer
);
3462 // Never invalidate the backbuffer, so never needs AfterDrawCall.
3465 void ClientWebGLContext::InvalidateSubFramebuffer(
3466 GLenum target
, const dom::Sequence
<GLenum
>& attachments
, GLint x
, GLint y
,
3467 GLsizei width
, GLsizei height
, ErrorResult
& unused
) {
3468 const auto range
= MakeRange(attachments
);
3469 const auto& buffer
= RawBufferView(range
);
3470 Run
<RPROC(InvalidateSubFramebuffer
)>(target
, buffer
, x
, y
, width
, height
);
3472 // Never invalidate the backbuffer, so never needs AfterDrawCall.
3475 void ClientWebGLContext::ReadBuffer(GLenum mode
) {
3476 Run
<RPROC(ReadBuffer
)>(mode
);
3479 // ----------------------- Renderbuffer objects -----------------------
3481 void ClientWebGLContext::BindRenderbuffer(const GLenum target
,
3482 WebGLRenderbufferJS
* const rb
) {
3483 const FuncScope
funcScope(*this, "bindRenderbuffer");
3484 if (IsContextLost()) return;
3485 if (rb
&& !rb
->ValidateUsable(*this, "rb")) return;
3486 auto& state
= State();
3488 if (target
!= LOCAL_GL_RENDERBUFFER
) {
3489 EnqueueError_ArgEnum("target", target
);
3493 state
.mBoundRb
= rb
;
3495 rb
->mHasBeenBound
= true;
3499 void ClientWebGLContext::RenderbufferStorageMultisample(GLenum target
,
3501 GLenum internalFormat
,
3503 GLsizei height
) const {
3504 const FuncScope
funcScope(*this, "renderbufferStorageMultisample");
3505 if (IsContextLost()) return;
3507 if (target
!= LOCAL_GL_RENDERBUFFER
) {
3508 EnqueueError_ArgEnum("target", target
);
3512 const auto& state
= State();
3514 const auto& rb
= state
.mBoundRb
;
3516 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No renderbuffer bound");
3520 if (!ValidateNonNegative("width", width
) ||
3521 !ValidateNonNegative("height", height
) ||
3522 !ValidateNonNegative("samples", samples
)) {
3526 if (internalFormat
== LOCAL_GL_DEPTH_STENCIL
&& samples
> 0) {
3527 // While our backend supports it trivially, the spec forbids it.
3528 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3529 "WebGL 1's DEPTH_STENCIL format may not be multisampled. Use "
3530 "DEPTH24_STENCIL8 when `samples > 0`.");
3534 Run
<RPROC(RenderbufferStorageMultisample
)>(
3535 rb
->mId
, static_cast<uint32_t>(samples
), internalFormat
,
3536 static_cast<uint32_t>(width
), static_cast<uint32_t>(height
));
3539 // --------------------------- Texture objects ---------------------------
3541 void ClientWebGLContext::ActiveTexture(const GLenum texUnitEnum
) {
3542 const FuncScope
funcScope(*this, "activeTexture");
3543 if (IsContextLost()) return;
3545 if (texUnitEnum
< LOCAL_GL_TEXTURE0
) {
3546 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3547 "`texture` (0x%04x) must be >= TEXTURE0 (0x%04x).",
3548 texUnitEnum
, LOCAL_GL_TEXTURE0
);
3552 const auto texUnit
= texUnitEnum
- LOCAL_GL_TEXTURE0
;
3554 auto& state
= State();
3555 if (texUnit
>= state
.mTexUnits
.size()) {
3556 EnqueueError(LOCAL_GL_INVALID_VALUE
,
3557 "TEXTURE%u must be < MAX_COMBINED_TEXTURE_IMAGE_UNITS (%zu).",
3558 texUnit
, state
.mTexUnits
.size());
3564 state
.mActiveTexUnit
= texUnit
;
3565 Run
<RPROC(ActiveTexture
)>(texUnit
);
3568 static bool IsTexTarget(const GLenum texTarget
, const bool webgl2
) {
3569 switch (texTarget
) {
3570 case LOCAL_GL_TEXTURE_2D
:
3571 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3574 case LOCAL_GL_TEXTURE_2D_ARRAY
:
3575 case LOCAL_GL_TEXTURE_3D
:
3583 void ClientWebGLContext::BindTexture(const GLenum texTarget
,
3584 WebGLTextureJS
* const tex
) {
3585 const FuncScope
funcScope(*this, "bindTexture");
3586 if (IsContextLost()) return;
3587 if (tex
&& !tex
->ValidateUsable(*this, "tex")) return;
3589 if (!IsTexTarget(texTarget
, mIsWebGL2
)) {
3590 EnqueueError_ArgEnum("texTarget", texTarget
);
3594 if (tex
&& tex
->mTarget
) {
3595 if (texTarget
!= tex
->mTarget
) {
3596 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3597 "Texture previously bound to %s cannot be bound now to %s.",
3598 EnumString(tex
->mTarget
).c_str(),
3599 EnumString(texTarget
).c_str());
3604 auto& state
= State();
3605 auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
3606 texUnit
.texByTarget
[texTarget
] = tex
;
3608 tex
->mTarget
= texTarget
;
3611 Run
<RPROC(BindTexture
)>(texTarget
, tex
? tex
->mId
: 0);
3614 void ClientWebGLContext::GenerateMipmap(GLenum texTarget
) const {
3615 Run
<RPROC(GenerateMipmap
)>(texTarget
);
3618 void ClientWebGLContext::GetTexParameter(
3619 JSContext
* cx
, GLenum texTarget
, GLenum pname
,
3620 JS::MutableHandle
<JS::Value
> retval
) const {
3621 retval
.set(JS::NullValue());
3622 const FuncScope
funcScope(*this, "getTexParameter");
3623 if (IsContextLost()) return;
3624 auto& state
= State();
3626 auto& texUnit
= state
.mTexUnits
[state
.mActiveTexUnit
];
3628 const auto& tex
= Find(texUnit
.texByTarget
, texTarget
, nullptr);
3630 if (!IsTexTarget(texTarget
, mIsWebGL2
)) {
3631 EnqueueError_ArgEnum("texTarget", texTarget
);
3633 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No texture bound to %s[%u].",
3634 EnumString(texTarget
).c_str(), state
.mActiveTexUnit
);
3639 const auto maybe
= [&]() {
3640 const auto& inProcess
= mNotLost
->inProcess
;
3642 return inProcess
->GetTexParameter(tex
->mId
, pname
);
3644 const auto& child
= mNotLost
->outOfProcess
;
3645 child
->FlushPendingCmds();
3647 if (!child
->SendGetTexParameter(tex
->mId
, pname
, &ret
)) {
3655 case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT
:
3656 retval
.set(JS::BooleanValue(*maybe
));
3660 retval
.set(JS::NumberValue(*maybe
));
3666 void ClientWebGLContext::TexParameterf(GLenum texTarget
, GLenum pname
,
3668 Run
<RPROC(TexParameter_base
)>(texTarget
, pname
, FloatOrInt(param
));
3671 void ClientWebGLContext::TexParameteri(GLenum texTarget
, GLenum pname
,
3673 Run
<RPROC(TexParameter_base
)>(texTarget
, pname
, FloatOrInt(param
));
3676 ////////////////////////////////////
3678 static GLenum
JSTypeMatchUnpackTypeError(GLenum unpackType
,
3679 js::Scalar::Type jsType
) {
3680 bool matches
= false;
3681 switch (unpackType
) {
3683 matches
= (jsType
== js::Scalar::Type::Int8
);
3686 case LOCAL_GL_UNSIGNED_BYTE
:
3687 matches
= (jsType
== js::Scalar::Type::Uint8
||
3688 jsType
== js::Scalar::Type::Uint8Clamped
);
3691 case LOCAL_GL_SHORT
:
3692 matches
= (jsType
== js::Scalar::Type::Int16
);
3695 case LOCAL_GL_UNSIGNED_SHORT
:
3696 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
3697 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
3698 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
3699 case LOCAL_GL_HALF_FLOAT
:
3700 case LOCAL_GL_HALF_FLOAT_OES
:
3701 matches
= (jsType
== js::Scalar::Type::Uint16
);
3705 matches
= (jsType
== js::Scalar::Type::Int32
);
3708 case LOCAL_GL_UNSIGNED_INT
:
3709 case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
:
3710 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
3711 case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV
:
3712 case LOCAL_GL_UNSIGNED_INT_24_8
:
3713 matches
= (jsType
== js::Scalar::Type::Uint32
);
3716 case LOCAL_GL_FLOAT
:
3717 matches
= (jsType
== js::Scalar::Type::Float32
);
3720 case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV
:
3721 matches
= false; // No valid jsType, but we allow uploads with null.
3725 return LOCAL_GL_INVALID_ENUM
;
3727 if (!matches
) return LOCAL_GL_INVALID_OPERATION
;
3731 static std::string
ToString(const js::Scalar::Type type
) {
3734 case js::Scalar::Type::X: \
3749 return std::string("#") + std::to_string(UnderlyingValue(type
));
3752 /////////////////////////////////////////////////
3754 static inline uvec2
CastUvec2(const ivec2
& val
) {
3755 return {static_cast<uint32_t>(val
.x
), static_cast<uint32_t>(val
.y
)};
3758 static inline uvec3
CastUvec3(const ivec3
& val
) {
3759 return {static_cast<uint32_t>(val
.x
), static_cast<uint32_t>(val
.y
),
3760 static_cast<uint32_t>(val
.z
)};
3763 template <typename T
>
3764 Range
<T
> SubRange(const Range
<T
>& full
, const size_t offset
,
3765 const size_t length
) {
3766 const auto newBegin
= full
.begin() + offset
;
3767 return Range
<T
>{newBegin
, newBegin
+ length
};
3770 static inline size_t SizeOfViewElem(const dom::ArrayBufferView
& view
) {
3771 const auto& elemType
= view
.Type();
3772 if (elemType
== js::Scalar::MaxTypedArrayViewType
) // DataViews.
3775 return js::Scalar::byteSize(elemType
);
3778 Maybe
<Range
<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView
& view
,
3780 GLuint elemCountOverride
) {
3781 const auto byteRange
= MakeRangeAbv(view
); // In bytes.
3782 const auto bytesPerElem
= SizeOfViewElem(view
);
3784 auto elemCount
= byteRange
.length() / bytesPerElem
;
3785 if (elemOffset
> elemCount
) return {};
3786 elemCount
-= elemOffset
;
3788 if (elemCountOverride
) {
3789 if (elemCountOverride
> elemCount
) return {};
3790 elemCount
= elemCountOverride
;
3792 const auto subrange
=
3793 SubRange(byteRange
, elemOffset
* bytesPerElem
, elemCount
* bytesPerElem
);
3794 return Some(subrange
);
3799 static bool IsTexTargetForDims(const GLenum texTarget
, const bool webgl2
,
3800 const uint8_t funcDims
) {
3801 if (!IsTexTarget(texTarget
, webgl2
)) return false;
3802 switch (texTarget
) {
3803 case LOCAL_GL_TEXTURE_2D
:
3804 case LOCAL_GL_TEXTURE_CUBE_MAP
:
3805 return funcDims
== 2;
3808 return funcDims
== 3;
3812 void ClientWebGLContext::TexStorage(uint8_t funcDims
, GLenum texTarget
,
3813 GLsizei levels
, GLenum internalFormat
,
3814 const ivec3
& size
) const {
3815 const FuncScope
funcScope(*this, "texStorage[23]D");
3816 if (IsContextLost()) return;
3817 if (!IsTexTargetForDims(texTarget
, mIsWebGL2
, funcDims
)) {
3818 EnqueueError_ArgEnum("texTarget", texTarget
);
3821 Run
<RPROC(TexStorage
)>(texTarget
, static_cast<uint32_t>(levels
),
3822 internalFormat
, CastUvec3(size
));
3826 // TODO: Move these definitions into statics here.
3827 Maybe
<webgl::TexUnpackBlobDesc
> FromImageBitmap(
3828 GLenum target
, uvec3 size
, const dom::ImageBitmap
& imageBitmap
,
3829 ErrorResult
* const out_rv
);
3831 webgl::TexUnpackBlobDesc
FromImageData(GLenum target
, uvec3 size
,
3832 const dom::ImageData
& imageData
,
3833 dom::Uint8ClampedArray
* const scopedArr
);
3835 Maybe
<webgl::TexUnpackBlobDesc
> FromDomElem(const ClientWebGLContext
&,
3836 GLenum target
, uvec3 size
,
3837 const dom::Element
& src
,
3838 ErrorResult
* const out_error
);
3839 } // namespace webgl
3841 void webgl::TexUnpackBlobDesc::Shrink(const webgl::PackingInfo
& pi
) {
3843 if (!size
.x
|| !size
.y
|| !size
.z
) return;
3844 MOZ_ASSERT(unpacking
.mUnpackRowLength
);
3845 MOZ_ASSERT(unpacking
.mUnpackImageHeight
);
3848 if (!GetBytesPerPixel(pi
, &bpp
)) return;
3850 const auto bytesPerRowUnaligned
=
3851 CheckedInt
<size_t>(unpacking
.mUnpackRowLength
) * bpp
;
3852 const auto bytesPerRowStride
=
3853 RoundUpToMultipleOf(bytesPerRowUnaligned
, unpacking
.mUnpackAlignment
);
3855 const auto bytesPerImageStride
=
3856 bytesPerRowStride
* unpacking
.mUnpackImageHeight
;
3857 // SKIP_IMAGES is ignored for 2D targets, but at worst this just makes our
3858 // shrinking less accurate.
3860 CheckedInt
<size_t>(unpacking
.mUnpackSkipImages
) + size
.z
;
3861 const auto bytesUpperBound
= bytesPerImageStride
* images
;
3863 if (bytesUpperBound
.isValid()) {
3864 cpuData
->Shrink(bytesUpperBound
.value());
3870 void ClientWebGLContext::TexImage(uint8_t funcDims
, GLenum imageTarget
,
3871 GLint level
, GLenum respecFormat
,
3872 const ivec3
& offset
, const ivec3
& isize
,
3873 GLint border
, const webgl::PackingInfo
& pi
,
3874 const TexImageSource
& src
) const {
3875 const FuncScope
funcScope(*this, "tex(Sub)Image[23]D");
3876 if (IsContextLost()) return;
3877 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
3878 EnqueueError_ArgEnum("imageTarget", imageTarget
);
3882 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
3885 const auto explicitSize
= CastUvec3(isize
);
3889 // Demarcate the region within which GC is disallowed. Typed arrays can move
3890 // their data during a GC, so this will allow the rooting hazard analysis to
3891 // report if a GC is possible while any data pointers extracted from the
3892 // typed array are still live.
3893 dom::Uint8ClampedArray scopedArr
;
3896 bool isDataUpload
= false;
3897 auto desc
= [&]() -> Maybe
<webgl::TexUnpackBlobDesc
> {
3898 if (src
.mPboOffset
) {
3899 isDataUpload
= true;
3900 const auto offset
= static_cast<uint64_t>(*src
.mPboOffset
);
3901 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
3903 gfxAlphaType::NonPremult
,
3909 isDataUpload
= true;
3910 const auto& view
= *src
.mView
;
3911 const auto& jsType
= view
.Type();
3912 const auto err
= JSTypeMatchUnpackTypeError(pi
.type
, jsType
);
3914 case LOCAL_GL_INVALID_ENUM
:
3915 EnqueueError_ArgEnum("unpackType", pi
.type
);
3917 case LOCAL_GL_INVALID_OPERATION
:
3918 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3919 "ArrayBufferView type %s not compatible with `type` %s.",
3920 ToString(jsType
).c_str(), EnumString(pi
.type
).c_str());
3926 const auto range
= GetRangeFromView(view
, src
.mViewElemOffset
,
3927 src
.mViewElemLengthOverride
);
3929 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`source` too small.");
3932 return Some(webgl::TexUnpackBlobDesc
{imageTarget
,
3934 gfxAlphaType::NonPremult
,
3935 Some(RawBuffer
<>{*range
}),
3939 if (src
.mImageBitmap
) {
3940 return webgl::FromImageBitmap(imageTarget
, explicitSize
,
3941 *(src
.mImageBitmap
), src
.mOut_error
);
3944 if (src
.mImageData
) {
3945 return Some(webgl::FromImageData(imageTarget
, explicitSize
,
3946 *(src
.mImageData
), &scopedArr
));
3950 return webgl::FromDomElem(*this, imageTarget
, explicitSize
,
3951 *(src
.mDomElem
), src
.mOut_error
);
3954 return Some(webgl::TexUnpackBlobDesc
{
3955 imageTarget
, explicitSize
, gfxAlphaType::NonPremult
, {}, {}});
3963 // Further, for uploads from TexImageSource, implied UNPACK_ROW_LENGTH and
3964 // UNPACK_ALIGNMENT are not strictly defined. These restrictions ensure
3965 // consistent and efficient behavior regardless of implied UNPACK_ params.
3967 Maybe
<uvec2
> structuredSrcSize
;
3968 if (desc
->dataSurf
|| desc
->sd
) {
3969 structuredSrcSize
= Some(desc
->imageSize
);
3971 if (structuredSrcSize
) {
3972 auto& size
= desc
->size
;
3974 size
.x
= structuredSrcSize
->x
;
3977 size
.y
= structuredSrcSize
->x
;
3981 const auto& rawUnpacking
= State().mPixelUnpackState
;
3982 const bool isSubrect
=
3983 (rawUnpacking
.mUnpackImageHeight
|| rawUnpacking
.mUnpackSkipImages
||
3984 rawUnpacking
.mUnpackRowLength
|| rawUnpacking
.mUnpackSkipRows
||
3985 rawUnpacking
.mUnpackSkipPixels
);
3986 if (isDataUpload
&& isSubrect
) {
3987 if (rawUnpacking
.mFlipY
|| rawUnpacking
.mPremultiplyAlpha
) {
3988 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
3989 "Non-DOM-Element uploads with alpha-premult"
3990 " or y-flip do not support subrect selection.");
3991 scopedArr
.Reset(); // (For the hazard analysis) Done with the data.
3996 rawUnpacking
.ForUseWith(desc
->imageTarget
, desc
->size
, structuredSrcSize
);
4001 auto fallbackReason
= BlitPreventReason(level
, offset
, pi
, *desc
);
4003 const auto& sd
= *(desc
->sd
);
4004 const auto sdType
= sd
.type();
4005 const auto& contextInfo
= mNotLost
->info
;
4007 const bool canUploadViaSd
= contextInfo
.uploadableSdTypes
[sdType
];
4008 if (!canUploadViaSd
) {
4009 const nsPrintfCString
msg(
4010 "Fast uploads for resource type %i not implemented.", int(sdType
));
4011 fallbackReason
.reset();
4012 fallbackReason
.emplace(ToString(msg
));
4015 if (StaticPrefs::webgl_disable_DOM_blit_uploads()) {
4016 fallbackReason
.reset();
4017 fallbackReason
.emplace("DOM blit uploads are disabled.");
4020 if (fallbackReason
) {
4021 EnqueuePerfWarning("Missed GPU-copy fast-path: %s",
4022 fallbackReason
->c_str());
4024 const auto& image
= desc
->image
;
4025 const RefPtr
<gfx::SourceSurface
> surf
= image
->GetAsSourceSurface();
4027 // WARNING: OSX can lose our MakeCurrent here.
4028 desc
->dataSurf
= surf
->GetDataSurface();
4030 if (!desc
->dataSurf
) {
4031 EnqueueError(LOCAL_GL_OUT_OF_MEMORY
,
4032 "Failed to retrieve source bytes for CPU upload.");
4035 desc
->sd
= Nothing();
4038 desc
->image
= nullptr;
4044 Run
<RPROC(TexImage
)>(static_cast<uint32_t>(level
), respecFormat
,
4045 CastUvec3(offset
), pi
, std::move(*desc
));
4046 scopedArr
.Reset(); // (For the hazard analysis) Done with the data.
4049 void ClientWebGLContext::CompressedTexImage(bool sub
, uint8_t funcDims
,
4050 GLenum imageTarget
, GLint level
,
4051 GLenum format
, const ivec3
& offset
,
4052 const ivec3
& isize
, GLint border
,
4053 const TexImageSource
& src
,
4054 GLsizei pboImageSize
) const {
4055 const FuncScope
funcScope(*this, "compressedTex(Sub)Image[23]D");
4056 if (IsContextLost()) return;
4057 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4058 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4062 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4067 Maybe
<uint64_t> pboOffset
;
4069 const auto maybe
= GetRangeFromView(*src
.mView
, src
.mViewElemOffset
,
4070 src
.mViewElemLengthOverride
);
4072 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`source` too small.");
4075 range
= RawBuffer
<>{*maybe
};
4076 } else if (src
.mPboOffset
) {
4077 if (!ValidateNonNegative("offset", *src
.mPboOffset
)) return;
4078 pboOffset
= Some(*src
.mPboOffset
);
4080 MOZ_CRASH("impossible");
4083 // We don't need to shrink `range` because valid calls require `range` to
4084 // match requirements exactly.
4086 Run
<RPROC(CompressedTexImage
)>(
4087 sub
, imageTarget
, static_cast<uint32_t>(level
), format
, CastUvec3(offset
),
4088 CastUvec3(isize
), range
, static_cast<uint32_t>(pboImageSize
), pboOffset
);
4091 void ClientWebGLContext::CopyTexImage(uint8_t funcDims
, GLenum imageTarget
,
4092 GLint level
, GLenum respecFormat
,
4093 const ivec3
& dstOffset
,
4094 const ivec2
& srcOffset
, const ivec2
& size
,
4095 GLint border
) const {
4096 const FuncScope
funcScope(*this, "copy(Sub)Image[23]D");
4097 if (IsContextLost()) return;
4098 if (!IsTexTargetForDims(ImageToTexTarget(imageTarget
), mIsWebGL2
, funcDims
)) {
4099 EnqueueError_ArgEnum("imageTarget", imageTarget
);
4103 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`border` must be 0.");
4106 Run
<RPROC(CopyTexImage
)>(imageTarget
, static_cast<uint32_t>(level
),
4107 respecFormat
, CastUvec3(dstOffset
), srcOffset
,
4111 // ------------------- Programs and shaders --------------------------------
4113 void ClientWebGLContext::UseProgram(WebGLProgramJS
* const prog
) {
4114 const FuncScope
funcScope(*this, "useProgram");
4115 if (IsContextLost()) return;
4116 if (prog
&& !prog
->ValidateUsable(*this, "prog")) return;
4118 auto& state
= State();
4120 if (state
.mTfActiveAndNotPaused
) {
4121 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4122 "Transform feedback is active and not paused.");
4127 const auto& res
= GetLinkResult(*prog
);
4129 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4130 "Program must be linked successfully.");
4137 state
.mCurrentProgram
= prog
;
4138 state
.mProgramKeepAlive
= prog
? prog
->mKeepAliveWeak
.lock() : nullptr;
4139 state
.mActiveLinkResult
= prog
? prog
->mResult
: nullptr;
4141 Run
<RPROC(UseProgram
)>(prog
? prog
->mId
: 0);
4144 void ClientWebGLContext::ValidateProgram(WebGLProgramJS
& prog
) const {
4145 const FuncScope
funcScope(*this, "validateProgram");
4146 if (IsContextLost()) return;
4147 if (!prog
.ValidateUsable(*this, "prog")) return;
4149 prog
.mLastValidate
= [&]() {
4150 const auto& inProcess
= mNotLost
->inProcess
;
4152 return inProcess
->ValidateProgram(prog
.mId
);
4154 const auto& child
= mNotLost
->outOfProcess
;
4155 child
->FlushPendingCmds();
4157 if (!child
->SendValidateProgram(prog
.mId
, &ret
)) {
4164 // ------------------------ Uniforms and attributes ------------------------
4166 Maybe
<double> ClientWebGLContext::GetVertexAttribPriv(const GLuint index
,
4167 const GLenum pname
) {
4168 const auto& inProcess
= mNotLost
->inProcess
;
4170 return inProcess
->GetVertexAttrib(index
, pname
);
4172 const auto& child
= mNotLost
->outOfProcess
;
4173 child
->FlushPendingCmds();
4175 if (!child
->SendGetVertexAttrib(index
, pname
, &ret
)) {
4181 void ClientWebGLContext::GetVertexAttrib(JSContext
* cx
, GLuint index
,
4183 JS::MutableHandle
<JS::Value
> retval
,
4185 retval
.set(JS::NullValue());
4186 const FuncScope
funcScope(*this, "getVertexAttrib");
4187 if (IsContextLost()) return;
4188 const auto& state
= State();
4190 const auto& genericAttribs
= state
.mGenericVertexAttribs
;
4191 if (index
>= genericAttribs
.size()) {
4192 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` (%u) >= MAX_VERTEX_ATTRIBS",
4198 case LOCAL_GL_CURRENT_VERTEX_ATTRIB
: {
4199 JS::RootedObject
obj(cx
);
4201 const auto& attrib
= genericAttribs
[index
];
4202 switch (attrib
.type
) {
4203 case webgl::AttribBaseType::Float
:
4204 obj
= dom::Float32Array::Create(
4205 cx
, this, 4, reinterpret_cast<const float*>(attrib
.data
));
4207 case webgl::AttribBaseType::Int
:
4208 obj
= dom::Int32Array::Create(
4209 cx
, this, 4, reinterpret_cast<const int32_t*>(attrib
.data
));
4211 case webgl::AttribBaseType::Uint
:
4212 obj
= dom::Uint32Array::Create(
4213 cx
, this, 4, reinterpret_cast<const uint32_t*>(attrib
.data
));
4215 case webgl::AttribBaseType::Boolean
:
4216 MOZ_CRASH("impossible");
4220 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
4223 retval
.set(JS::ObjectValue(*obj
));
4227 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
: {
4228 const auto& buffers
= state
.mBoundVao
->mAttribBuffers
;
4229 const auto& buffer
= buffers
[index
];
4230 (void)ToJSValueOrNull(cx
, buffer
, retval
);
4234 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
:
4235 // Disallowed from JS, but allowed in Host.
4236 EnqueueError_ArgEnum("pname", pname
);
4243 const auto maybe
= GetVertexAttribPriv(index
, pname
);
4246 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED
:
4247 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
:
4248 case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER
:
4249 retval
.set(JS::BooleanValue(*maybe
));
4253 retval
.set(JS::NumberValue(*maybe
));
4259 void ClientWebGLContext::UniformData(const GLenum funcElemType
,
4260 const WebGLUniformLocationJS
* const loc
,
4262 const Range
<const uint8_t>& bytes
,
4264 GLuint elemCountOverride
) const {
4265 const FuncScope
funcScope(*this, "uniform setter");
4266 if (IsContextLost()) return;
4268 const auto& activeLinkResult
= GetActiveLinkResult();
4269 if (!activeLinkResult
) {
4270 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No active linked Program.");
4276 auto availCount
= bytes
.length() / sizeof(float);
4277 if (elemOffset
> availCount
) {
4278 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`elemOffset` too large for `data`.");
4281 availCount
-= elemOffset
;
4282 if (elemCountOverride
) {
4283 if (elemCountOverride
> availCount
) {
4284 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4285 "`elemCountOverride` too large for `data`.");
4288 availCount
= elemCountOverride
;
4293 const auto channels
= ElemTypeComponents(funcElemType
);
4294 if (!availCount
|| availCount
% channels
!= 0) {
4295 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4296 "`values` length (%u) must be a positive "
4297 "integer multiple of size of %s.",
4298 availCount
, EnumString(funcElemType
).c_str());
4304 uint32_t locId
= -1;
4305 if (MOZ_LIKELY(loc
)) {
4306 locId
= loc
->mLocation
;
4307 if (!loc
->ValidateUsable(*this, "location")) return;
4311 const auto& reqLinkInfo
= loc
->mParent
.lock();
4312 if (reqLinkInfo
.get() != activeLinkResult
) {
4313 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4314 "UniformLocation is not from the current active Program.");
4320 bool funcMatchesLocation
= false;
4321 for (const auto allowed
: loc
->mValidUploadElemTypes
) {
4322 funcMatchesLocation
|= (funcElemType
== allowed
);
4324 if (MOZ_UNLIKELY(!funcMatchesLocation
)) {
4325 std::string validSetters
;
4326 for (const auto allowed
: loc
->mValidUploadElemTypes
) {
4327 validSetters
+= EnumString(allowed
);
4328 validSetters
+= '/';
4330 validSetters
.pop_back(); // Cheekily discard the extra trailing '/'.
4332 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4333 "Uniform's `type` requires uniform setter of type %s.",
4334 validSetters
.c_str());
4341 const auto ptr
= bytes
.begin().get() + (elemOffset
* sizeof(float));
4342 const auto range
= Range
<const uint8_t>{ptr
, availCount
* sizeof(float)};
4343 Run
<RPROC(UniformData
)>(locId
, transpose
, RawBuffer
<>(range
));
4348 void ClientWebGLContext::BindVertexArray(WebGLVertexArrayJS
* const vao
) {
4349 const FuncScope
funcScope(*this, "bindVertexArray");
4350 if (IsContextLost()) return;
4351 if (vao
&& !vao
->ValidateUsable(*this, "vao")) return;
4352 auto& state
= State();
4355 vao
->mHasBeenBound
= true;
4356 state
.mBoundVao
= vao
;
4358 state
.mBoundVao
= state
.mDefaultVao
;
4361 Run
<RPROC(BindVertexArray
)>(vao
? vao
->mId
: 0);
4364 void ClientWebGLContext::EnableVertexAttribArray(GLuint index
) {
4365 Run
<RPROC(EnableVertexAttribArray
)>(index
);
4368 void ClientWebGLContext::DisableVertexAttribArray(GLuint index
) {
4369 Run
<RPROC(DisableVertexAttribArray
)>(index
);
4372 WebGLsizeiptr
ClientWebGLContext::GetVertexAttribOffset(GLuint index
,
4374 const FuncScope
funcScope(*this, "getVertexAttribOffset");
4375 if (IsContextLost()) return 0;
4377 if (pname
!= LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
) {
4378 EnqueueError_ArgEnum("pname", pname
);
4382 const auto maybe
= GetVertexAttribPriv(index
, pname
);
4383 if (!maybe
) return 0;
4387 void ClientWebGLContext::VertexAttrib4Tv(GLuint index
, webgl::AttribBaseType t
,
4388 const Range
<const uint8_t>& src
) {
4389 const FuncScope
funcScope(*this, "vertexAttrib[1234]u?[fi]{v}");
4390 if (IsContextLost()) return;
4391 auto& state
= State();
4393 if (src
.length() / sizeof(float) < 4) {
4394 EnqueueError(LOCAL_GL_INVALID_VALUE
, "Array must have >=4 elements.");
4398 auto& list
= state
.mGenericVertexAttribs
;
4399 if (index
>= list
.size()) {
4400 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4401 "`index` must be < MAX_VERTEX_ATTRIBS.");
4405 auto& attrib
= list
[index
];
4407 memcpy(attrib
.data
, src
.begin().get(), sizeof(attrib
.data
));
4409 Run
<RPROC(VertexAttrib4T
)>(index
, attrib
);
4414 void ClientWebGLContext::VertexAttribDivisor(GLuint index
, GLuint divisor
) {
4415 Run
<RPROC(VertexAttribDivisor
)>(index
, divisor
);
4420 void ClientWebGLContext::VertexAttribPointerImpl(bool isFuncInt
, GLuint index
,
4421 GLint rawChannels
, GLenum type
,
4423 GLsizei rawByteStrideOrZero
,
4424 WebGLintptr rawByteOffset
) {
4425 const FuncScope
funcScope(*this, "vertexAttribI?Pointer");
4426 if (IsContextLost()) return;
4427 auto& state
= State();
4429 const auto channels
= MaybeAs
<uint8_t>(rawChannels
);
4431 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4432 "Channel count `size` must be within [1,4].");
4436 const auto byteStrideOrZero
= MaybeAs
<uint8_t>(rawByteStrideOrZero
);
4437 if (!byteStrideOrZero
) {
4438 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`stride` must be within [0,255].");
4442 if (!ValidateNonNegative("byteOffset", rawByteOffset
)) return;
4443 const auto byteOffset
= static_cast<uint64_t>(rawByteOffset
);
4447 const webgl::VertAttribPointerDesc desc
{
4448 isFuncInt
, *channels
, normalized
, *byteStrideOrZero
, type
, byteOffset
};
4450 const auto res
= CheckVertexAttribPointer(mIsWebGL2
, desc
);
4452 const auto& err
= res
.inspectErr();
4453 EnqueueError(err
.type
, "%s", err
.info
.c_str());
4457 auto& list
= state
.mBoundVao
->mAttribBuffers
;
4458 if (index
>= list
.size()) {
4459 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4460 "`index` (%u) must be < MAX_VERTEX_ATTRIBS.", index
);
4464 const auto buffer
= state
.mBoundBufferByTarget
[LOCAL_GL_ARRAY_BUFFER
];
4465 if (!buffer
&& byteOffset
) {
4466 return EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4467 "If ARRAY_BUFFER is null, byteOffset must be zero.");
4470 Run
<RPROC(VertexAttribPointer
)>(index
, desc
);
4472 list
[index
] = buffer
;
4475 // -------------------------------- Drawing -------------------------------
4477 void ClientWebGLContext::DrawArraysInstanced(GLenum mode
, GLint first
,
4478 GLsizei count
, GLsizei primcount
,
4480 Run
<RPROC(DrawArraysInstanced
)>(mode
, first
, count
, primcount
);
4484 void ClientWebGLContext::DrawElementsInstanced(GLenum mode
, GLsizei count
,
4485 GLenum type
, WebGLintptr offset
,
4486 GLsizei primcount
, FuncScopeId
) {
4487 Run
<RPROC(DrawElementsInstanced
)>(mode
, count
, type
, offset
, primcount
);
4491 // ------------------------------ Readback -------------------------------
4492 void ClientWebGLContext::ReadPixels(GLint x
, GLint y
, GLsizei width
,
4493 GLsizei height
, GLenum format
, GLenum type
,
4494 WebGLsizeiptr offset
,
4495 dom::CallerType aCallerType
,
4496 ErrorResult
& out_error
) const {
4497 const FuncScope
funcScope(*this, "readPixels");
4498 if (!ReadPixels_SharedPrecheck(aCallerType
, out_error
)) return;
4499 const auto& state
= State();
4500 if (!ValidateNonNegative("width", width
)) return;
4501 if (!ValidateNonNegative("height", height
)) return;
4502 if (!ValidateNonNegative("offset", offset
)) return;
4504 const auto desc
= webgl::ReadPixelsDesc
{{x
, y
},
4505 *uvec2::From(width
, height
),
4507 state
.mPixelPackState
};
4508 Run
<RPROC(ReadPixelsPbo
)>(desc
, static_cast<uint64_t>(offset
));
4511 void ClientWebGLContext::ReadPixels(GLint x
, GLint y
, GLsizei width
,
4512 GLsizei height
, GLenum format
, GLenum type
,
4513 const dom::ArrayBufferView
& dstData
,
4514 GLuint dstElemOffset
,
4515 dom::CallerType aCallerType
,
4516 ErrorResult
& out_error
) const {
4517 const FuncScope
funcScope(*this, "readPixels");
4518 if (!ReadPixels_SharedPrecheck(aCallerType
, out_error
)) return;
4519 const auto& state
= State();
4520 if (!ValidateNonNegative("width", width
)) return;
4521 if (!ValidateNonNegative("height", height
)) return;
4525 js::Scalar::Type reqScalarType
;
4526 if (!GetJSScalarFromGLType(type
, &reqScalarType
)) {
4528 WebGLContext::EnumName(type
, &name
);
4529 EnqueueError(LOCAL_GL_INVALID_ENUM
, "type: invalid enum value %s",
4530 name
.BeginReading());
4534 auto viewElemType
= dstData
.Type();
4535 if (viewElemType
== js::Scalar::Uint8Clamped
) {
4536 viewElemType
= js::Scalar::Uint8
;
4538 if (viewElemType
!= reqScalarType
) {
4539 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4540 "`pixels` type does not match `type`.");
4546 if (!ValidateArrayBufferView(dstData
, dstElemOffset
, 0,
4547 LOCAL_GL_INVALID_VALUE
, &bytes
, &byteLen
)) {
4551 const auto desc
= webgl::ReadPixelsDesc
{{x
, y
},
4552 *uvec2::From(width
, height
),
4554 state
.mPixelPackState
};
4555 const auto range
= Range
<uint8_t>(bytes
, byteLen
);
4556 DoReadPixels(desc
, range
);
4559 void ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc
& desc
,
4560 const Range
<uint8_t> dest
) const {
4561 const auto notLost
=
4562 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
4563 if (!notLost
) return;
4564 const auto& inProcess
= notLost
->inProcess
;
4566 inProcess
->ReadPixelsInto(desc
, dest
);
4569 const auto& child
= notLost
->outOfProcess
;
4570 child
->FlushPendingCmds();
4571 webgl::ReadPixelsResultIpc res
= {};
4572 if (!child
->SendReadPixels(desc
, dest
.length(), &res
)) {
4575 if (!res
.byteStride
) return;
4576 const auto& byteStride
= res
.byteStride
;
4577 const auto& subrect
= res
.subrect
;
4578 const webgl::RaiiShmem shmem
{child
, res
.shmem
};
4579 const auto& shmemBytes
= shmem
.ByteRange();
4582 if (!GetBytesPerPixel(desc
.pi
, &bpp
)) {
4587 const auto& packing
= desc
.packState
;
4588 auto packRect
= *uvec2::From(subrect
.x
, subrect
.y
);
4589 packRect
.x
+= packing
.skipPixels
;
4590 packRect
.y
+= packing
.skipRows
;
4592 const auto xByteSize
= bpp
* static_cast<uint32_t>(subrect
.width
);
4593 const ptrdiff_t byteOffset
= packRect
.y
* byteStride
+ packRect
.x
* bpp
;
4595 auto srcItr
= shmemBytes
.begin() + byteOffset
;
4596 auto destItr
= dest
.begin() + byteOffset
;
4598 for (const auto i
: IntegerRange(subrect
.height
)) {
4600 // Don't trigger an assert on the last loop by pushing a RangedPtr past
4602 srcItr
+= byteStride
;
4603 destItr
+= byteStride
;
4604 MOZ_RELEASE_ASSERT(srcItr
+ xByteSize
<= shmemBytes
.end());
4605 MOZ_RELEASE_ASSERT(destItr
+ xByteSize
<= dest
.end());
4607 Memcpy(destItr
, srcItr
, xByteSize
);
4611 bool ClientWebGLContext::ReadPixels_SharedPrecheck(
4612 dom::CallerType aCallerType
, ErrorResult
& out_error
) const {
4613 if (IsContextLost()) return false;
4615 if (mCanvasElement
&& mCanvasElement
->IsWriteOnly() &&
4616 aCallerType
!= dom::CallerType::System
) {
4617 JsWarning("readPixels: Not allowed");
4618 out_error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
4625 // --------------------------------- GL Query ---------------------------------
4627 static inline GLenum
QuerySlotTarget(const GLenum specificTarget
) {
4628 if (specificTarget
== LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE
) {
4629 return LOCAL_GL_ANY_SAMPLES_PASSED
;
4631 return specificTarget
;
4634 void ClientWebGLContext::GetQuery(JSContext
* cx
, GLenum specificTarget
,
4636 JS::MutableHandle
<JS::Value
> retval
) const {
4637 retval
.set(JS::NullValue());
4638 const FuncScope
funcScope(*this, "getQuery");
4639 if (IsContextLost()) return;
4640 const auto& limits
= Limits();
4641 auto& state
= State();
4643 if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query
)) {
4644 if (pname
== LOCAL_GL_QUERY_COUNTER_BITS
) {
4645 switch (specificTarget
) {
4646 case LOCAL_GL_TIME_ELAPSED_EXT
:
4647 retval
.set(JS::NumberValue(limits
.queryCounterBitsTimeElapsed
));
4650 case LOCAL_GL_TIMESTAMP_EXT
:
4651 retval
.set(JS::NumberValue(limits
.queryCounterBitsTimestamp
));
4655 EnqueueError_ArgEnum("target", specificTarget
);
4661 if (pname
!= LOCAL_GL_CURRENT_QUERY
) {
4662 EnqueueError_ArgEnum("pname", pname
);
4666 const auto slotTarget
= QuerySlotTarget(specificTarget
);
4667 const auto& slot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
4669 EnqueueError_ArgEnum("target", specificTarget
);
4674 if (query
&& query
->mTarget
!= specificTarget
) {
4678 (void)ToJSValueOrNull(cx
, query
, retval
);
4681 void ClientWebGLContext::GetQueryParameter(
4682 JSContext
*, WebGLQueryJS
& query
, const GLenum pname
,
4683 JS::MutableHandle
<JS::Value
> retval
) const {
4684 retval
.set(JS::NullValue());
4685 const FuncScope
funcScope(*this, "getQueryParameter");
4686 if (IsContextLost()) return;
4687 if (!query
.ValidateUsable(*this, "query")) return;
4689 auto maybe
= [&]() {
4690 const auto& inProcess
= mNotLost
->inProcess
;
4692 return inProcess
->GetQueryParameter(query
.mId
, pname
);
4694 const auto& child
= mNotLost
->outOfProcess
;
4695 child
->FlushPendingCmds();
4697 if (!child
->SendGetQueryParameter(query
.mId
, pname
, &ret
)) {
4704 // We must usually wait for an event loop before the query can be available.
4705 const bool canBeAvailable
=
4706 (query
.mCanBeAvailable
|| StaticPrefs::webgl_allow_immediate_queries());
4707 if (!canBeAvailable
) {
4708 if (pname
!= LOCAL_GL_QUERY_RESULT_AVAILABLE
) {
4715 case LOCAL_GL_QUERY_RESULT_AVAILABLE
:
4716 retval
.set(JS::BooleanValue(*maybe
));
4720 retval
.set(JS::NumberValue(*maybe
));
4725 void ClientWebGLContext::BeginQuery(const GLenum specificTarget
,
4726 WebGLQueryJS
& query
) {
4727 const FuncScope
funcScope(*this, "beginQuery");
4728 if (IsContextLost()) return;
4729 if (!query
.ValidateUsable(*this, "query")) return;
4730 auto& state
= State();
4732 const auto slotTarget
= QuerySlotTarget(specificTarget
);
4733 const auto& slot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
4735 EnqueueError_ArgEnum("target", specificTarget
);
4740 auto enumStr
= EnumString(slotTarget
);
4741 if (slotTarget
== LOCAL_GL_ANY_SAMPLES_PASSED
) {
4742 enumStr
+= "/ANY_SAMPLES_PASSED_CONSERVATIVE";
4744 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4745 "A Query is already active for %s.", enumStr
.c_str());
4749 if (query
.mTarget
&& query
.mTarget
!= specificTarget
) {
4750 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4751 "`query` cannot be changed to a different target.");
4756 query
.mTarget
= specificTarget
;
4758 Run
<RPROC(BeginQuery
)>(specificTarget
, query
.mId
);
4761 void ClientWebGLContext::EndQuery(const GLenum specificTarget
) {
4762 const FuncScope
funcScope(*this, "endQuery");
4763 if (IsContextLost()) return;
4764 auto& state
= State();
4766 const auto slotTarget
= QuerySlotTarget(specificTarget
);
4767 const auto& maybeSlot
= MaybeFind(state
.mCurrentQueryByTarget
, slotTarget
);
4769 EnqueueError_ArgEnum("target", specificTarget
);
4772 auto& slot
= *maybeSlot
;
4773 if (!slot
|| slot
->mTarget
!= specificTarget
) {
4774 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No Query is active for %s.",
4775 EnumString(specificTarget
).c_str());
4778 const auto query
= slot
;
4781 Run
<RPROC(EndQuery
)>(specificTarget
);
4783 auto& availRunnable
= EnsureAvailabilityRunnable();
4784 availRunnable
.mQueries
.push_back(query
.get());
4785 query
->mCanBeAvailable
= false;
4788 void ClientWebGLContext::QueryCounter(WebGLQueryJS
& query
,
4789 const GLenum target
) const {
4790 const FuncScope
funcScope(*this, "queryCounter");
4791 if (IsContextLost()) return;
4792 if (!query
.ValidateUsable(*this, "query")) return;
4794 if (target
!= LOCAL_GL_TIMESTAMP
) {
4795 EnqueueError(LOCAL_GL_INVALID_ENUM
, "`target` must be TIMESTAMP.");
4799 if (query
.mTarget
&& query
.mTarget
!= target
) {
4800 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4801 "`query` cannot be changed to a different target.");
4804 query
.mTarget
= target
;
4806 Run
<RPROC(QueryCounter
)>(query
.mId
);
4808 auto& availRunnable
= EnsureAvailabilityRunnable();
4809 availRunnable
.mQueries
.push_back(&query
);
4810 query
.mCanBeAvailable
= false;
4813 // -------------------------------- Sampler -------------------------------
4814 void ClientWebGLContext::GetSamplerParameter(
4815 JSContext
* cx
, const WebGLSamplerJS
& sampler
, const GLenum pname
,
4816 JS::MutableHandle
<JS::Value
> retval
) const {
4817 retval
.set(JS::NullValue());
4818 const FuncScope
funcScope(*this, "getSamplerParameter");
4819 if (IsContextLost()) return;
4820 if (!sampler
.ValidateUsable(*this, "sampler")) return;
4822 const auto maybe
= [&]() {
4823 const auto& inProcess
= mNotLost
->inProcess
;
4825 return inProcess
->GetSamplerParameter(sampler
.mId
, pname
);
4827 const auto& child
= mNotLost
->outOfProcess
;
4828 child
->FlushPendingCmds();
4830 if (!child
->SendGetSamplerParameter(sampler
.mId
, pname
, &ret
)) {
4836 retval
.set(JS::NumberValue(*maybe
));
4840 void ClientWebGLContext::BindSampler(const GLuint unit
,
4841 WebGLSamplerJS
* const sampler
) {
4842 const FuncScope
funcScope(*this, "bindSampler");
4843 if (IsContextLost()) return;
4844 if (sampler
&& !sampler
->ValidateUsable(*this, "sampler")) return;
4845 auto& state
= State();
4847 auto& texUnits
= state
.mTexUnits
;
4848 if (unit
>= texUnits
.size()) {
4849 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`unit` (%u) larger than %zu.", unit
,
4856 texUnits
[unit
].sampler
= sampler
;
4858 Run
<RPROC(BindSampler
)>(unit
, sampler
? sampler
->mId
: 0);
4861 void ClientWebGLContext::SamplerParameteri(WebGLSamplerJS
& sampler
,
4863 const GLint param
) const {
4864 const FuncScope
funcScope(*this, "samplerParameteri");
4865 if (IsContextLost()) return;
4866 if (!sampler
.ValidateUsable(*this, "sampler")) return;
4868 Run
<RPROC(SamplerParameteri
)>(sampler
.mId
, pname
, param
);
4871 void ClientWebGLContext::SamplerParameterf(WebGLSamplerJS
& sampler
,
4873 const GLfloat param
) const {
4874 const FuncScope
funcScope(*this, "samplerParameterf");
4875 if (IsContextLost()) return;
4876 if (!sampler
.ValidateUsable(*this, "sampler")) return;
4878 Run
<RPROC(SamplerParameterf
)>(sampler
.mId
, pname
, param
);
4881 // ------------------------------- GL Sync ---------------------------------
4883 void ClientWebGLContext::GetSyncParameter(
4884 JSContext
* const cx
, WebGLSyncJS
& sync
, const GLenum pname
,
4885 JS::MutableHandle
<JS::Value
> retval
) const {
4886 retval
.set(JS::NullValue());
4887 const FuncScope
funcScope(*this, "getSyncParameter");
4888 if (IsContextLost()) return;
4889 if (!sync
.ValidateUsable(*this, "sync")) return;
4891 retval
.set([&]() -> JS::Value
{
4893 case LOCAL_GL_OBJECT_TYPE
:
4894 return JS::NumberValue(LOCAL_GL_SYNC_FENCE
);
4895 case LOCAL_GL_SYNC_CONDITION
:
4896 return JS::NumberValue(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE
);
4897 case LOCAL_GL_SYNC_FLAGS
:
4898 return JS::NumberValue(0);
4899 case LOCAL_GL_SYNC_STATUS
: {
4900 if (!sync
.mSignaled
) {
4901 const auto res
= ClientWaitSync(sync
, 0, 0);
4902 sync
.mSignaled
= (res
== LOCAL_GL_ALREADY_SIGNALED
||
4903 res
== LOCAL_GL_CONDITION_SATISFIED
);
4905 return JS::NumberValue(sync
.mSignaled
? LOCAL_GL_SIGNALED
4906 : LOCAL_GL_UNSIGNALED
);
4909 EnqueueError_ArgEnum("pname", pname
);
4910 return JS::NullValue();
4915 GLenum
ClientWebGLContext::ClientWaitSync(WebGLSyncJS
& sync
,
4916 const GLbitfield flags
,
4917 const GLuint64 timeout
) const {
4918 const FuncScope
funcScope(*this, "clientWaitSync");
4919 if (IsContextLost()) return LOCAL_GL_WAIT_FAILED
;
4920 if (!sync
.ValidateUsable(*this, "sync")) return LOCAL_GL_WAIT_FAILED
;
4922 if (flags
!= 0 && flags
!= LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT
) {
4923 EnqueueError(LOCAL_GL_INVALID_VALUE
,
4924 "`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.");
4925 return LOCAL_GL_WAIT_FAILED
;
4928 const auto ret
= [&]() {
4929 const auto& inProcess
= mNotLost
->inProcess
;
4931 return inProcess
->ClientWaitSync(sync
.mId
, flags
, timeout
);
4933 const auto& child
= mNotLost
->outOfProcess
;
4934 child
->FlushPendingCmds();
4936 if (!child
->SendClientWaitSync(sync
.mId
, flags
, timeout
, &ret
)) {
4943 case LOCAL_GL_CONDITION_SATISFIED
:
4944 case LOCAL_GL_ALREADY_SIGNALED
:
4945 sync
.mSignaled
= true;
4951 const bool canBeAvailable
=
4952 (sync
.mCanBeAvailable
|| StaticPrefs::webgl_allow_immediate_queries());
4953 if (!canBeAvailable
) {
4956 "Sync object not yet queryable. Please wait for the event"
4959 return LOCAL_GL_WAIT_FAILED
;
4965 void ClientWebGLContext::WaitSync(const WebGLSyncJS
& sync
,
4966 const GLbitfield flags
,
4967 const GLint64 timeout
) const {
4968 const FuncScope
funcScope(*this, "waitSync");
4969 if (IsContextLost()) return;
4970 if (!sync
.ValidateUsable(*this, "sync")) return;
4973 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`flags` must be 0.");
4976 if (timeout
!= -1) {
4977 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`timeout` must be TIMEOUT_IGNORED.");
4981 JsWarning("waitSync is a no-op.");
4984 // -------------------------- Transform Feedback ---------------------------
4986 void ClientWebGLContext::BindTransformFeedback(
4987 const GLenum target
, WebGLTransformFeedbackJS
* const tf
) {
4988 const FuncScope
funcScope(*this, "bindTransformFeedback");
4989 if (IsContextLost()) return;
4990 if (tf
&& !tf
->ValidateUsable(*this, "tf")) return;
4991 auto& state
= State();
4993 if (target
!= LOCAL_GL_TRANSFORM_FEEDBACK
) {
4994 EnqueueError(LOCAL_GL_INVALID_ENUM
, "`target` must be TRANSFORM_FEEDBACK.");
4997 if (state
.mTfActiveAndNotPaused
) {
4998 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
4999 "Current Transform Feedback object is active and not paused.");
5004 tf
->mHasBeenBound
= true;
5005 state
.mBoundTfo
= tf
;
5007 state
.mBoundTfo
= state
.mDefaultTfo
;
5010 Run
<RPROC(BindTransformFeedback
)>(tf
? tf
->mId
: 0);
5013 void ClientWebGLContext::BeginTransformFeedback(const GLenum primMode
) {
5014 const FuncScope
funcScope(*this, "beginTransformFeedback");
5015 if (IsContextLost()) return;
5016 auto& state
= State();
5017 auto& tfo
= *(state
.mBoundTfo
);
5019 if (tfo
.mActiveOrPaused
) {
5020 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5021 "Transform Feedback is already active or paused.");
5024 MOZ_ASSERT(!state
.mTfActiveAndNotPaused
);
5026 auto& prog
= state
.mCurrentProgram
;
5028 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "No program in use.");
5031 const auto& linkResult
= GetLinkResult(*prog
);
5032 if (!linkResult
.success
) {
5033 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5034 "Program is not successfully linked.");
5038 auto tfBufferCount
= linkResult
.active
.activeTfVaryings
.size();
5039 if (tfBufferCount
&&
5040 linkResult
.tfBufferMode
== LOCAL_GL_INTERLEAVED_ATTRIBS
) {
5043 if (!tfBufferCount
) {
5044 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5045 "Program does not use Transform Feedback.");
5049 const auto& buffers
= tfo
.mAttribBuffers
;
5050 for (const auto i
: IntegerRange(tfBufferCount
)) {
5052 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5053 "Transform Feedback buffer %u is null.", i
);
5059 case LOCAL_GL_POINTS
:
5060 case LOCAL_GL_LINES
:
5061 case LOCAL_GL_TRIANGLES
:
5064 EnqueueError(LOCAL_GL_INVALID_ENUM
,
5065 "`primitiveMode` must be POINTS, LINES< or TRIANGLES.");
5071 tfo
.mActiveOrPaused
= true;
5072 tfo
.mActiveProgram
= prog
;
5073 tfo
.mActiveProgramKeepAlive
= prog
->mKeepAliveWeak
.lock();
5074 prog
->mActiveTfos
.insert(&tfo
);
5075 state
.mTfActiveAndNotPaused
= true;
5077 Run
<RPROC(BeginTransformFeedback
)>(primMode
);
5080 void ClientWebGLContext::EndTransformFeedback() {
5081 const FuncScope
funcScope(*this, "endTransformFeedback");
5082 if (IsContextLost()) return;
5083 auto& state
= State();
5084 auto& tfo
= *(state
.mBoundTfo
);
5086 if (!tfo
.mActiveOrPaused
) {
5087 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5088 "Transform Feedback is not active or paused.");
5092 tfo
.mActiveOrPaused
= false;
5093 tfo
.mActiveProgram
->mActiveTfos
.erase(&tfo
);
5094 tfo
.mActiveProgram
= nullptr;
5095 tfo
.mActiveProgramKeepAlive
= nullptr;
5096 state
.mTfActiveAndNotPaused
= false;
5097 Run
<RPROC(EndTransformFeedback
)>();
5100 void ClientWebGLContext::PauseTransformFeedback() {
5101 const FuncScope
funcScope(*this, "pauseTransformFeedback");
5102 if (IsContextLost()) return;
5103 auto& state
= State();
5104 auto& tfo
= *(state
.mBoundTfo
);
5106 if (!tfo
.mActiveOrPaused
) {
5107 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5108 "Transform Feedback is not active.");
5111 if (!state
.mTfActiveAndNotPaused
) {
5112 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5113 "Transform Feedback is already paused.");
5117 state
.mTfActiveAndNotPaused
= false;
5118 Run
<RPROC(PauseTransformFeedback
)>();
5121 void ClientWebGLContext::ResumeTransformFeedback() {
5122 const FuncScope
funcScope(*this, "resumeTransformFeedback");
5123 if (IsContextLost()) return;
5124 auto& state
= State();
5125 auto& tfo
= *(state
.mBoundTfo
);
5127 if (!tfo
.mActiveOrPaused
) {
5128 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5129 "Transform Feedback is not active and paused.");
5132 if (state
.mTfActiveAndNotPaused
) {
5133 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5134 "Transform Feedback is not paused.");
5137 if (state
.mCurrentProgram
!= tfo
.mActiveProgram
) {
5139 LOCAL_GL_INVALID_OPERATION
,
5140 "Cannot Resume Transform Feedback with a program link result different"
5141 " from when Begin was called.");
5145 state
.mTfActiveAndNotPaused
= true;
5146 Run
<RPROC(ResumeTransformFeedback
)>();
5149 void ClientWebGLContext::SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS
* fb
,
5151 fb
->mInOpaqueRAF
= value
;
5152 Run
<RPROC(SetFramebufferIsInOpaqueRAF
)>(fb
->mId
, value
);
5155 // ---------------------------- Misc Extensions ----------------------------
5156 void ClientWebGLContext::DrawBuffers(const dom::Sequence
<GLenum
>& buffers
) {
5157 const auto range
= MakeRange(buffers
);
5158 const auto vec
= std::vector
<GLenum
>(range
.begin().get(), range
.end().get());
5159 Run
<RPROC(DrawBuffers
)>(vec
);
5162 void ClientWebGLContext::EnqueueErrorImpl(const GLenum error
,
5163 const nsACString
& text
) const {
5164 if (!mNotLost
) return; // Ignored if context is lost.
5165 Run
<RPROC(GenerateError
)>(error
, ToString(text
));
5168 void ClientWebGLContext::RequestExtension(const WebGLExtensionID ext
) const {
5169 Run
<RPROC(RequestExtension
)>(ext
);
5174 static bool IsExtensionForbiddenForCaller(const WebGLExtensionID ext
,
5175 const dom::CallerType callerType
) {
5176 if (callerType
== dom::CallerType::System
) return false;
5178 if (StaticPrefs::webgl_enable_privileged_extensions()) return false;
5180 const bool resistFingerprinting
=
5181 nsContentUtils::ShouldResistFingerprinting();
5183 case WebGLExtensionID::MOZ_debug
:
5186 case WebGLExtensionID::WEBGL_debug_renderer_info
:
5187 return resistFingerprinting
||
5188 !StaticPrefs::webgl_enable_debug_renderer_info();
5190 case WebGLExtensionID::WEBGL_debug_shaders
:
5191 return resistFingerprinting
;
5198 bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext
,
5199 const dom::CallerType callerType
) const {
5200 if (IsExtensionForbiddenForCaller(ext
, callerType
)) return false;
5201 const auto& limits
= Limits();
5202 return limits
.supportedExtensions
[ext
];
5205 void ClientWebGLContext::GetSupportedExtensions(
5206 dom::Nullable
<nsTArray
<nsString
>>& retval
,
5207 const dom::CallerType callerType
) const {
5209 if (!mNotLost
) return;
5211 auto& retarr
= retval
.SetValue();
5212 for (const auto i
: MakeEnumeratedRange(WebGLExtensionID::Max
)) {
5213 if (!IsSupported(i
, callerType
)) continue;
5215 const auto& extStr
= GetExtensionName(i
);
5216 retarr
.AppendElement(NS_ConvertUTF8toUTF16(extStr
));
5222 void ClientWebGLContext::GetSupportedProfilesASTC(
5223 dom::Nullable
<nsTArray
<nsString
>>& retval
) const {
5225 if (!mNotLost
) return;
5226 const auto& limits
= Limits();
5228 auto& retarr
= retval
.SetValue();
5229 retarr
.AppendElement(u
"ldr"_ns
);
5230 if (limits
.astcHdr
) {
5231 retarr
.AppendElement(u
"hdr"_ns
);
5237 bool ClientWebGLContext::ShouldResistFingerprinting() const {
5238 if (NS_IsMainThread()) {
5239 if (mCanvasElement
) {
5240 // If we're constructed from a canvas element
5241 return nsContentUtils::ShouldResistFingerprinting(GetOwnerDoc());
5243 // if (mOffscreenCanvas->GetOwnerGlobal()) {
5244 // // If we're constructed from an offscreen canvas
5245 // return nsContentUtils::ShouldResistFingerprinting(
5246 // mOffscreenCanvas->GetOwnerGlobal()->PrincipalOrNull());
5248 // Last resort, just check the global preference
5249 return nsContentUtils::ShouldResistFingerprinting();
5251 dom::WorkerPrivate
* workerPrivate
= dom::GetCurrentThreadWorkerPrivate();
5252 MOZ_ASSERT(workerPrivate
);
5253 return nsContentUtils::ShouldResistFingerprinting(workerPrivate
);
5256 // ---------------------------
5258 void ClientWebGLContext::EnqueueError_ArgEnum(const char* const argName
,
5259 const GLenum val
) const {
5260 EnqueueError(LOCAL_GL_INVALID_ENUM
, "Bad `%s`: 0x%04x", argName
, val
);
5266 void ClientWebGLContext::AttachShader(WebGLProgramJS
& prog
,
5267 WebGLShaderJS
& shader
) const {
5268 const FuncScope
funcScope(*this, "attachShader");
5269 if (IsContextLost()) return;
5270 if (!prog
.ValidateUsable(*this, "program")) return;
5271 if (!shader
.ValidateUsable(*this, "shader")) return;
5273 auto& slot
= *MaybeFind(prog
.mNextLink_Shaders
, shader
.mType
);
5275 if (&shader
== slot
.shader
) {
5276 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`shader` is already attached.");
5278 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5279 "Only one of each type of"
5280 " shader may be attached to a program.");
5284 slot
= {&shader
, shader
.mKeepAliveWeak
.lock()};
5286 Run
<RPROC(AttachShader
)>(prog
.mId
, shader
.mId
);
5289 void ClientWebGLContext::BindAttribLocation(WebGLProgramJS
& prog
,
5290 const GLuint location
,
5291 const nsAString
& name
) const {
5292 const FuncScope
funcScope(*this, "detachShader");
5293 if (IsContextLost()) return;
5294 if (!prog
.ValidateUsable(*this, "program")) return;
5296 const auto& nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5297 Run
<RPROC(BindAttribLocation
)>(prog
.mId
, location
, nameU8
);
5300 void ClientWebGLContext::DetachShader(WebGLProgramJS
& prog
,
5301 const WebGLShaderJS
& shader
) const {
5302 const FuncScope
funcScope(*this, "detachShader");
5303 if (IsContextLost()) return;
5304 if (!prog
.ValidateUsable(*this, "program")) return;
5305 if (!shader
.ValidateUsable(*this, "shader")) return;
5307 auto& slot
= *MaybeFind(prog
.mNextLink_Shaders
, shader
.mType
);
5309 if (slot
.shader
!= &shader
) {
5310 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "`shader` is not attached.");
5315 Run
<RPROC(DetachShader
)>(prog
.mId
, shader
.mId
);
5318 void ClientWebGLContext::GetAttachedShaders(
5319 const WebGLProgramJS
& prog
,
5320 dom::Nullable
<nsTArray
<RefPtr
<WebGLShaderJS
>>>& retval
) const {
5321 const FuncScope
funcScope(*this, "getAttachedShaders");
5322 if (IsContextLost()) return;
5323 if (!prog
.ValidateUsable(*this, "program")) return;
5325 auto& arr
= retval
.SetValue();
5326 for (const auto& pair
: prog
.mNextLink_Shaders
) {
5327 const auto& attachment
= pair
.second
;
5328 if (!attachment
.shader
) continue;
5329 arr
.AppendElement(attachment
.shader
);
5333 void ClientWebGLContext::LinkProgram(WebGLProgramJS
& prog
) const {
5334 const FuncScope
funcScope(*this, "linkProgram");
5335 if (IsContextLost()) return;
5336 if (!prog
.ValidateUsable(*this, "program")) return;
5338 if (!prog
.mActiveTfos
.empty()) {
5339 EnqueueError(LOCAL_GL_INVALID_OPERATION
,
5340 "Program still in use by active or paused"
5341 " Transform Feedback objects.");
5345 prog
.mResult
= std::make_shared
<webgl::LinkResult
>();
5346 prog
.mUniformLocByName
= Nothing();
5347 prog
.mUniformBlockBindings
= {};
5348 Run
<RPROC(LinkProgram
)>(prog
.mId
);
5351 void ClientWebGLContext::TransformFeedbackVaryings(
5352 WebGLProgramJS
& prog
, const dom::Sequence
<nsString
>& varyings
,
5353 const GLenum bufferMode
) const {
5354 const FuncScope
funcScope(*this, "transformFeedbackVaryings");
5355 if (IsContextLost()) return;
5356 if (!prog
.ValidateUsable(*this, "program")) return;
5358 std::vector
<std::string
> varyingsU8
;
5359 varyingsU8
.reserve(varyings
.Length());
5360 for (const auto& cur
: varyings
) {
5361 const auto curU8
= ToString(NS_ConvertUTF16toUTF8(cur
));
5362 varyingsU8
.push_back(curU8
);
5365 Run
<RPROC(TransformFeedbackVaryings
)>(prog
.mId
, varyingsU8
, bufferMode
);
5368 void ClientWebGLContext::UniformBlockBinding(WebGLProgramJS
& prog
,
5369 const GLuint blockIndex
,
5370 const GLuint blockBinding
) const {
5371 const FuncScope
funcScope(*this, "uniformBlockBinding");
5372 if (IsContextLost()) return;
5373 if (!prog
.ValidateUsable(*this, "program")) return;
5374 const auto& state
= State();
5376 (void)GetLinkResult(prog
);
5377 auto& list
= prog
.mUniformBlockBindings
;
5378 if (blockIndex
>= list
.size()) {
5380 LOCAL_GL_INVALID_VALUE
,
5381 "`blockIndex` (%u) must be less than ACTIVE_UNIFORM_BLOCKS (%zu).",
5382 blockIndex
, list
.size());
5385 if (blockBinding
>= state
.mBoundUbos
.size()) {
5386 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5387 "`blockBinding` (%u) must be less than "
5388 "MAX_UNIFORM_BUFFER_BINDINGS (%zu).",
5389 blockBinding
, state
.mBoundUbos
.size());
5393 list
[blockIndex
] = blockBinding
;
5394 Run
<RPROC(UniformBlockBinding
)>(prog
.mId
, blockIndex
, blockBinding
);
5397 // WebGLProgramJS link result reflection
5399 already_AddRefed
<WebGLActiveInfoJS
> ClientWebGLContext::GetActiveAttrib(
5400 const WebGLProgramJS
& prog
, const GLuint index
) {
5401 const FuncScope
funcScope(*this, "getActiveAttrib");
5402 if (IsContextLost()) return nullptr;
5403 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5405 const auto& res
= GetLinkResult(prog
);
5406 const auto& list
= res
.active
.activeAttribs
;
5407 if (index
>= list
.size()) {
5408 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5412 const auto& info
= list
[index
];
5413 return AsAddRefed(new WebGLActiveInfoJS(info
));
5416 already_AddRefed
<WebGLActiveInfoJS
> ClientWebGLContext::GetActiveUniform(
5417 const WebGLProgramJS
& prog
, const GLuint index
) {
5418 const FuncScope
funcScope(*this, "getActiveUniform");
5419 if (IsContextLost()) return nullptr;
5420 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5422 const auto& res
= GetLinkResult(prog
);
5423 const auto& list
= res
.active
.activeUniforms
;
5424 if (index
>= list
.size()) {
5425 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5429 const auto& info
= list
[index
];
5430 return AsAddRefed(new WebGLActiveInfoJS(info
));
5433 void ClientWebGLContext::GetActiveUniformBlockName(const WebGLProgramJS
& prog
,
5435 nsAString
& retval
) const {
5436 retval
.SetIsVoid(true);
5437 const FuncScope
funcScope(*this, "getActiveUniformBlockName");
5438 if (IsContextLost()) return;
5439 if (!prog
.ValidateUsable(*this, "program")) return;
5441 const auto& res
= GetLinkResult(prog
);
5443 EnqueueError(LOCAL_GL_INVALID_OPERATION
, "Program has not been linked.");
5447 const auto& list
= res
.active
.activeUniformBlocks
;
5448 if (index
>= list
.size()) {
5449 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5453 const auto& block
= list
[index
];
5454 CopyUTF8toUTF16(block
.name
, retval
);
5457 void ClientWebGLContext::GetActiveUniformBlockParameter(
5458 JSContext
* const cx
, const WebGLProgramJS
& prog
, const GLuint index
,
5459 const GLenum pname
, JS::MutableHandle
<JS::Value
> retval
, ErrorResult
& rv
) {
5460 retval
.set(JS::NullValue());
5461 const FuncScope
funcScope(*this, "getActiveUniformBlockParameter");
5462 if (IsContextLost()) return;
5463 if (!prog
.ValidateUsable(*this, "program")) return;
5465 const auto& res
= GetLinkResult(prog
);
5466 const auto& list
= res
.active
.activeUniformBlocks
;
5467 if (index
>= list
.size()) {
5468 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5471 const auto& block
= list
[index
];
5473 retval
.set([&]() -> JS::Value
{
5475 case LOCAL_GL_UNIFORM_BLOCK_BINDING
:
5476 return JS::NumberValue(prog
.mUniformBlockBindings
[index
]);
5478 case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE
:
5479 return JS::NumberValue(block
.dataSize
);
5481 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
:
5482 return JS::NumberValue(block
.activeUniformIndices
.size());
5484 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
: {
5485 const auto& indices
= block
.activeUniformIndices
;
5486 JS::RootedObject
obj(cx
, dom::Uint32Array::Create(
5487 cx
, this, indices
.size(), indices
.data()));
5489 rv
= NS_ERROR_OUT_OF_MEMORY
;
5491 return JS::ObjectOrNullValue(obj
);
5494 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
:
5495 return JS::BooleanValue(block
.referencedByVertexShader
);
5497 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER
:
5498 return JS::BooleanValue(block
.referencedByFragmentShader
);
5501 EnqueueError_ArgEnum("pname", pname
);
5502 return JS::NullValue();
5507 void ClientWebGLContext::GetActiveUniforms(
5508 JSContext
* const cx
, const WebGLProgramJS
& prog
,
5509 const dom::Sequence
<GLuint
>& uniformIndices
, const GLenum pname
,
5510 JS::MutableHandle
<JS::Value
> retval
) const {
5511 retval
.set(JS::NullValue());
5512 const FuncScope
funcScope(*this, "getActiveUniforms");
5513 if (IsContextLost()) return;
5514 if (!prog
.ValidateUsable(*this, "program")) return;
5516 const auto& res
= GetLinkResult(prog
);
5517 const auto& list
= res
.active
.activeUniforms
;
5519 const auto count
= uniformIndices
.Length();
5520 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, count
));
5521 if (!array
) return; // Just bail.
5523 for (const auto i
: IntegerRange(count
)) {
5524 const auto index
= uniformIndices
[i
];
5525 if (index
>= list
.size()) {
5526 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5527 "`uniformIndices[%u]`: `%u` too large.", i
, index
);
5530 const auto& uniform
= list
[index
];
5532 JS::RootedValue
value(cx
);
5534 case LOCAL_GL_UNIFORM_TYPE
:
5535 value
= JS::NumberValue(uniform
.elemType
);
5538 case LOCAL_GL_UNIFORM_SIZE
:
5539 value
= JS::NumberValue(uniform
.elemCount
);
5542 case LOCAL_GL_UNIFORM_BLOCK_INDEX
:
5543 value
= JS::NumberValue(uniform
.block_index
);
5546 case LOCAL_GL_UNIFORM_OFFSET
:
5547 value
= JS::NumberValue(uniform
.block_offset
);
5550 case LOCAL_GL_UNIFORM_ARRAY_STRIDE
:
5551 value
= JS::NumberValue(uniform
.block_arrayStride
);
5554 case LOCAL_GL_UNIFORM_MATRIX_STRIDE
:
5555 value
= JS::NumberValue(uniform
.block_matrixStride
);
5558 case LOCAL_GL_UNIFORM_IS_ROW_MAJOR
:
5559 value
= JS::BooleanValue(uniform
.block_isRowMajor
);
5563 EnqueueError_ArgEnum("pname", pname
);
5566 if (!JS_DefineElement(cx
, array
, i
, value
, JSPROP_ENUMERATE
)) return;
5569 retval
.setObject(*array
);
5572 already_AddRefed
<WebGLActiveInfoJS
>
5573 ClientWebGLContext::GetTransformFeedbackVarying(const WebGLProgramJS
& prog
,
5574 const GLuint index
) {
5575 const FuncScope
funcScope(*this, "getTransformFeedbackVarying");
5576 if (IsContextLost()) return nullptr;
5577 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5579 const auto& res
= GetLinkResult(prog
);
5580 const auto& list
= res
.active
.activeTfVaryings
;
5581 if (index
>= list
.size()) {
5582 EnqueueError(LOCAL_GL_INVALID_VALUE
, "`index` too large.");
5586 const auto& info
= list
[index
];
5587 return AsAddRefed(new WebGLActiveInfoJS(info
));
5590 GLint
ClientWebGLContext::GetAttribLocation(const WebGLProgramJS
& prog
,
5591 const nsAString
& name
) const {
5592 const FuncScope
funcScope(*this, "getAttribLocation");
5593 if (IsContextLost()) return -1;
5594 if (!prog
.ValidateUsable(*this, "program")) return -1;
5596 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5597 const auto& res
= GetLinkResult(prog
);
5598 for (const auto& cur
: res
.active
.activeAttribs
) {
5599 if (cur
.name
== nameU8
) return cur
.location
;
5602 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
5604 EnqueueError(err
->type
, "%s", err
->info
.c_str());
5609 GLint
ClientWebGLContext::GetFragDataLocation(const WebGLProgramJS
& prog
,
5610 const nsAString
& name
) const {
5611 const FuncScope
funcScope(*this, "getFragDataLocation");
5612 if (IsContextLost()) return -1;
5613 if (!prog
.ValidateUsable(*this, "program")) return -1;
5615 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5617 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
5624 const auto& inProcess
= mNotLost
->inProcess
;
5626 return inProcess
->GetFragDataLocation(prog
.mId
, nameU8
);
5628 const auto& child
= mNotLost
->outOfProcess
;
5629 child
->FlushPendingCmds();
5631 if (!child
->SendGetFragDataLocation(prog
.mId
, nameU8
, &ret
)) {
5638 GLuint
ClientWebGLContext::GetUniformBlockIndex(
5639 const WebGLProgramJS
& prog
, const nsAString
& blockName
) const {
5640 const FuncScope
funcScope(*this, "getUniformBlockIndex");
5641 if (IsContextLost()) return LOCAL_GL_INVALID_INDEX
;
5642 if (!prog
.ValidateUsable(*this, "program")) return LOCAL_GL_INVALID_INDEX
;
5644 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(blockName
));
5646 const auto& res
= GetLinkResult(prog
);
5647 const auto& list
= res
.active
.activeUniformBlocks
;
5648 for (const auto i
: IntegerRange(list
.size())) {
5649 const auto& cur
= list
[i
];
5650 if (cur
.name
== nameU8
) {
5654 return LOCAL_GL_INVALID_INDEX
;
5657 void ClientWebGLContext::GetUniformIndices(
5658 const WebGLProgramJS
& prog
, const dom::Sequence
<nsString
>& uniformNames
,
5659 dom::Nullable
<nsTArray
<GLuint
>>& retval
) const {
5660 const FuncScope
funcScope(*this, "getUniformIndices");
5661 if (IsContextLost()) return;
5662 if (!prog
.ValidateUsable(*this, "program")) return;
5664 const auto& res
= GetLinkResult(prog
);
5665 auto ret
= nsTArray
<GLuint
>(uniformNames
.Length());
5667 std::unordered_map
<std::string
, size_t> retIdByName
;
5668 retIdByName
.reserve(ret
.Length());
5670 for (const auto i
: IntegerRange(uniformNames
.Length())) {
5671 const auto& name
= uniformNames
[i
];
5672 auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5673 retIdByName
.insert({std::move(nameU8
), i
});
5674 ret
.AppendElement(LOCAL_GL_INVALID_INDEX
);
5678 for (const auto& cur
: res
.active
.activeUniforms
) {
5679 const auto maybeRetId
= MaybeFind(retIdByName
, cur
.name
);
5681 ret
[*maybeRetId
] = i
;
5686 retval
.SetValue(std::move(ret
));
5689 already_AddRefed
<WebGLUniformLocationJS
> ClientWebGLContext::GetUniformLocation(
5690 const WebGLProgramJS
& prog
, const nsAString
& name
) const {
5691 const FuncScope
funcScope(*this, "getUniformLocation");
5692 if (IsContextLost()) return nullptr;
5693 if (!prog
.ValidateUsable(*this, "program")) return nullptr;
5695 const auto& res
= GetLinkResult(prog
);
5697 if (!prog
.mUniformLocByName
) {
5698 // Cache a map from name->location.
5699 // Since the only way to set uniforms requires calling GetUniformLocation,
5700 // we expect apps to query most active uniforms once for each scalar or
5701 // array. NB: Uniform array setters do have overflow semantics, even though
5702 // uniform locations aren't guaranteed contiguous, but GetUniformLocation
5703 // must still be called once per array.
5704 prog
.mUniformLocByName
.emplace();
5706 for (const auto& activeUniform
: res
.active
.activeUniforms
) {
5707 if (activeUniform
.block_index
!= -1) continue;
5709 auto locName
= activeUniform
.name
;
5710 const auto indexed
= webgl::ParseIndexed(locName
);
5712 locName
= indexed
->name
;
5715 const auto err
= CheckGLSLVariableName(mIsWebGL2
, locName
);
5718 const auto baseLength
= locName
.size();
5719 for (const auto& pair
: activeUniform
.locByIndex
) {
5721 locName
.erase(baseLength
); // Erase previous "[N]".
5723 locName
+= std::to_string(pair
.first
);
5726 const auto locInfo
=
5727 WebGLProgramJS::UniformLocInfo
{pair
.second
, activeUniform
.elemType
};
5728 prog
.mUniformLocByName
->insert({locName
, locInfo
});
5732 const auto& locByName
= *(prog
.mUniformLocByName
);
5734 const auto nameU8
= ToString(NS_ConvertUTF16toUTF8(name
));
5735 auto loc
= MaybeFind(locByName
, nameU8
);
5737 loc
= MaybeFind(locByName
, nameU8
+ "[0]");
5740 const auto err
= CheckGLSLVariableName(mIsWebGL2
, nameU8
);
5742 EnqueueError(err
->type
, "%s", err
->info
.c_str());
5747 return AsAddRefed(new WebGLUniformLocationJS(*this, prog
.mResult
,
5748 loc
->location
, loc
->elemType
));
5751 std::array
<uint16_t, 3> ValidUploadElemTypes(const GLenum elemType
) {
5752 std::vector
<GLenum
> ret
;
5755 ret
= {LOCAL_GL_FLOAT
, LOCAL_GL_INT
, LOCAL_GL_UNSIGNED_INT
};
5757 case LOCAL_GL_BOOL_VEC2
:
5758 ret
= {LOCAL_GL_FLOAT_VEC2
, LOCAL_GL_INT_VEC2
,
5759 LOCAL_GL_UNSIGNED_INT_VEC2
};
5761 case LOCAL_GL_BOOL_VEC3
:
5762 ret
= {LOCAL_GL_FLOAT_VEC3
, LOCAL_GL_INT_VEC3
,
5763 LOCAL_GL_UNSIGNED_INT_VEC3
};
5765 case LOCAL_GL_BOOL_VEC4
:
5766 ret
= {LOCAL_GL_FLOAT_VEC4
, LOCAL_GL_INT_VEC4
,
5767 LOCAL_GL_UNSIGNED_INT_VEC4
};
5770 case LOCAL_GL_SAMPLER_2D
:
5771 case LOCAL_GL_SAMPLER_3D
:
5772 case LOCAL_GL_SAMPLER_CUBE
:
5773 case LOCAL_GL_SAMPLER_2D_SHADOW
:
5774 case LOCAL_GL_SAMPLER_2D_ARRAY
:
5775 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
5776 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
5777 case LOCAL_GL_INT_SAMPLER_2D
:
5778 case LOCAL_GL_INT_SAMPLER_3D
:
5779 case LOCAL_GL_INT_SAMPLER_CUBE
:
5780 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
5781 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
5782 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
5783 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
5784 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
:
5785 ret
= {LOCAL_GL_INT
};
5793 std::array
<uint16_t, 3> arr
= {};
5794 MOZ_ASSERT(arr
[2] == 0);
5795 for (const auto i
: IntegerRange(ret
.size())) {
5796 arr
[i
] = AssertedCast
<uint16_t>(ret
[i
]);
5801 void ClientWebGLContext::GetProgramInfoLog(const WebGLProgramJS
& prog
,
5802 nsAString
& retval
) const {
5803 retval
.SetIsVoid(true);
5804 const FuncScope
funcScope(*this, "getProgramInfoLog");
5805 if (IsContextLost()) return;
5806 if (!prog
.ValidateUsable(*this, "program")) return;
5808 const auto& res
= GetLinkResult(prog
);
5809 CopyUTF8toUTF16(res
.log
, retval
);
5812 void ClientWebGLContext::GetProgramParameter(
5813 JSContext
* const js
, const WebGLProgramJS
& prog
, const GLenum pname
,
5814 JS::MutableHandle
<JS::Value
> retval
) const {
5815 retval
.set(JS::NullValue());
5816 const FuncScope
funcScope(*this, "getProgramParameter");
5817 if (IsContextLost()) return;
5818 if (!prog
.ValidateUsable(*this, "program")) return;
5820 retval
.set([&]() -> JS::Value
{
5822 case LOCAL_GL_DELETE_STATUS
:
5823 // "Is flagged for deletion?"
5824 return JS::BooleanValue(!prog
.mKeepAlive
);
5825 case LOCAL_GL_VALIDATE_STATUS
:
5826 return JS::BooleanValue(prog
.mLastValidate
);
5827 case LOCAL_GL_ATTACHED_SHADERS
: {
5829 for (const auto& pair
: prog
.mNextLink_Shaders
) {
5830 const auto& slot
= pair
.second
;
5835 return JS::NumberValue(shaders
);
5841 const auto& res
= GetLinkResult(prog
);
5844 case LOCAL_GL_LINK_STATUS
:
5845 return JS::BooleanValue(res
.success
);
5847 case LOCAL_GL_ACTIVE_ATTRIBUTES
:
5848 return JS::NumberValue(res
.active
.activeAttribs
.size());
5850 case LOCAL_GL_ACTIVE_UNIFORMS
:
5851 return JS::NumberValue(res
.active
.activeUniforms
.size());
5853 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE
:
5854 if (!mIsWebGL2
) break;
5855 return JS::NumberValue(res
.tfBufferMode
);
5857 case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS
:
5858 if (!mIsWebGL2
) break;
5859 return JS::NumberValue(res
.active
.activeTfVaryings
.size());
5861 case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS
:
5862 if (!mIsWebGL2
) break;
5863 return JS::NumberValue(res
.active
.activeUniformBlocks
.size());
5868 EnqueueError_ArgEnum("pname", pname
);
5869 return JS::NullValue();
5876 void ClientWebGLContext::CompileShader(WebGLShaderJS
& shader
) const {
5877 const FuncScope
funcScope(*this, "compileShader");
5878 if (IsContextLost()) return;
5879 if (!shader
.ValidateUsable(*this, "shader")) return;
5881 shader
.mResult
= {};
5882 Run
<RPROC(CompileShader
)>(shader
.mId
);
5885 void ClientWebGLContext::GetShaderInfoLog(const WebGLShaderJS
& shader
,
5886 nsAString
& retval
) const {
5887 retval
.SetIsVoid(true);
5888 const FuncScope
funcScope(*this, "getShaderInfoLog");
5889 if (IsContextLost()) return;
5890 if (!shader
.ValidateUsable(*this, "shader")) return;
5892 const auto& result
= GetCompileResult(shader
);
5893 CopyUTF8toUTF16(result
.log
, retval
);
5896 void ClientWebGLContext::GetShaderParameter(
5897 JSContext
* const cx
, const WebGLShaderJS
& shader
, const GLenum pname
,
5898 JS::MutableHandle
<JS::Value
> retval
) const {
5899 retval
.set(JS::NullValue());
5900 const FuncScope
funcScope(*this, "getShaderParameter");
5901 if (IsContextLost()) return;
5902 if (!shader
.ValidateUsable(*this, "shader")) return;
5904 retval
.set([&]() -> JS::Value
{
5906 case LOCAL_GL_SHADER_TYPE
:
5907 return JS::NumberValue(shader
.mType
);
5909 case LOCAL_GL_DELETE_STATUS
: // "Is flagged for deletion?"
5910 return JS::BooleanValue(!shader
.mKeepAlive
);
5912 case LOCAL_GL_COMPILE_STATUS
: {
5913 const auto& result
= GetCompileResult(shader
);
5914 return JS::BooleanValue(result
.success
);
5918 EnqueueError_ArgEnum("pname", pname
);
5919 return JS::NullValue();
5924 void ClientWebGLContext::GetShaderSource(const WebGLShaderJS
& shader
,
5925 nsAString
& retval
) const {
5926 retval
.SetIsVoid(true);
5927 const FuncScope
funcScope(*this, "getShaderSource");
5928 if (IsContextLost()) return;
5929 if (!shader
.ValidateUsable(*this, "shader")) return;
5931 CopyUTF8toUTF16(shader
.mSource
, retval
);
5934 void ClientWebGLContext::GetTranslatedShaderSource(const WebGLShaderJS
& shader
,
5935 nsAString
& retval
) const {
5936 retval
.SetIsVoid(true);
5937 const FuncScope
funcScope(*this, "getTranslatedShaderSource");
5938 if (IsContextLost()) return;
5939 if (!shader
.ValidateUsable(*this, "shader")) return;
5941 const auto& result
= GetCompileResult(shader
);
5942 CopyUTF8toUTF16(result
.translatedSource
, retval
);
5945 void ClientWebGLContext::ShaderSource(WebGLShaderJS
& shader
,
5946 const nsAString
& sourceU16
) const {
5947 const FuncScope
funcScope(*this, "shaderSource");
5948 if (IsContextLost()) return;
5949 if (!shader
.ValidateUsable(*this, "shader")) return;
5951 auto source
= ToString(NS_ConvertUTF16toUTF8(sourceU16
));
5952 const auto cleanSource
= CommentsToSpaces(source
);
5954 const auto badChar
= CheckGLSLPreprocString(mIsWebGL2
, cleanSource
);
5956 EnqueueError(LOCAL_GL_INVALID_VALUE
,
5957 "`source` contains illegal character 0x%x.", *badChar
);
5961 shader
.mSource
= std::move(source
);
5962 Run
<RPROC(ShaderSource
)>(shader
.mId
, cleanSource
);
5967 const webgl::CompileResult
& ClientWebGLContext::GetCompileResult(
5968 const WebGLShaderJS
& shader
) const {
5969 if (shader
.mResult
.pending
) {
5970 shader
.mResult
= [&]() {
5971 const auto& inProcess
= mNotLost
->inProcess
;
5973 return inProcess
->GetCompileResult(shader
.mId
);
5975 const auto& child
= mNotLost
->outOfProcess
;
5976 child
->FlushPendingCmds();
5977 webgl::CompileResult ret
= {};
5978 if (!child
->SendGetCompileResult(shader
.mId
, &ret
)) {
5984 return shader
.mResult
;
5987 const webgl::LinkResult
& ClientWebGLContext::GetLinkResult(
5988 const WebGLProgramJS
& prog
) const {
5989 if (prog
.mResult
->pending
) {
5990 const auto notLost
=
5991 mNotLost
; // Hold a strong-ref to prevent LoseContext=>UAF.
5992 if (!notLost
) return *(prog
.mResult
);
5994 *(prog
.mResult
) = [&]() {
5995 const auto& inProcess
= mNotLost
->inProcess
;
5997 return inProcess
->GetLinkResult(prog
.mId
);
5999 const auto& child
= mNotLost
->outOfProcess
;
6000 child
->FlushPendingCmds();
6001 webgl::LinkResult ret
;
6002 if (!child
->SendGetLinkResult(prog
.mId
, &ret
)) {
6008 prog
.mUniformBlockBindings
.resize(
6009 prog
.mResult
->active
.activeUniformBlocks
.size());
6011 auto& state
= State();
6012 if (state
.mCurrentProgram
== &prog
&& prog
.mResult
->success
) {
6013 state
.mActiveLinkResult
= prog
.mResult
;
6016 return *(prog
.mResult
);
6021 // ---------------------------
6023 bool ClientWebGLContext::ValidateArrayBufferView(
6024 const dom::ArrayBufferView
& view
, GLuint elemOffset
,
6025 GLuint elemCountOverride
, const GLenum errorEnum
, uint8_t** const out_bytes
,
6026 size_t* const out_byteLen
) const {
6027 view
.ComputeState();
6028 uint8_t* const bytes
= view
.Data();
6029 const size_t byteLen
= view
.Length();
6031 const auto& elemSize
= SizeOfViewElem(view
);
6033 size_t elemCount
= byteLen
/ elemSize
;
6034 if (elemOffset
> elemCount
) {
6035 EnqueueError(errorEnum
, "Invalid offset into ArrayBufferView.");
6038 elemCount
-= elemOffset
;
6040 if (elemCountOverride
) {
6041 if (elemCountOverride
> elemCount
) {
6042 EnqueueError(errorEnum
, "Invalid sub-length for ArrayBufferView.");
6045 elemCount
= elemCountOverride
;
6048 *out_bytes
= bytes
+ (elemOffset
* elemSize
);
6049 *out_byteLen
= elemCount
* elemSize
;
6053 // ---------------------------
6055 webgl::ObjectJS::ObjectJS(const ClientWebGLContext
& webgl
)
6056 : mGeneration(webgl
.mNotLost
), mId(webgl
.mNotLost
->state
.NextId()) {}
6060 WebGLFramebufferJS::WebGLFramebufferJS(const ClientWebGLContext
& webgl
,
6062 : webgl::ObjectJS(webgl
), mOpaque(opaque
) {
6063 (void)mAttachments
[LOCAL_GL_DEPTH_ATTACHMENT
];
6064 (void)mAttachments
[LOCAL_GL_STENCIL_ATTACHMENT
];
6065 if (!webgl
.mIsWebGL2
) {
6066 (void)mAttachments
[LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
];
6069 EnsureColorAttachments();
6072 void WebGLFramebufferJS::EnsureColorAttachments() {
6073 const auto& webgl
= Context();
6074 const auto& limits
= webgl
->Limits();
6075 auto maxColorDrawBuffers
= limits
.maxColorDrawBuffers
;
6076 if (!webgl
->mIsWebGL2
&&
6077 !webgl
->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
)) {
6078 maxColorDrawBuffers
= 1;
6080 for (const auto i
: IntegerRange(maxColorDrawBuffers
)) {
6081 (void)mAttachments
[LOCAL_GL_COLOR_ATTACHMENT0
+ i
];
6085 WebGLProgramJS::WebGLProgramJS(const ClientWebGLContext
& webgl
)
6086 : webgl::ObjectJS(webgl
),
6087 mKeepAlive(std::make_shared
<webgl::ProgramKeepAlive
>(*this)),
6088 mKeepAliveWeak(mKeepAlive
) {
6089 (void)mNextLink_Shaders
[LOCAL_GL_VERTEX_SHADER
];
6090 (void)mNextLink_Shaders
[LOCAL_GL_FRAGMENT_SHADER
];
6092 mResult
= std::make_shared
<webgl::LinkResult
>();
6095 WebGLShaderJS::WebGLShaderJS(const ClientWebGLContext
& webgl
, const GLenum type
)
6096 : webgl::ObjectJS(webgl
),
6098 mKeepAlive(std::make_shared
<webgl::ShaderKeepAlive
>(*this)),
6099 mKeepAliveWeak(mKeepAlive
) {}
6101 WebGLTransformFeedbackJS::WebGLTransformFeedbackJS(
6102 const ClientWebGLContext
& webgl
)
6103 : webgl::ObjectJS(webgl
),
6104 mAttribBuffers(webgl
.Limits().maxTransformFeedbackSeparateAttribs
) {}
6106 WebGLVertexArrayJS::WebGLVertexArrayJS(const ClientWebGLContext
& webgl
)
6107 : webgl::ObjectJS(webgl
), mAttribBuffers(webgl
.Limits().maxVertexAttribs
) {}
6111 #define _(WebGLType) \
6112 JSObject* WebGLType##JS::WrapObject(JSContext* const cx, \
6113 JS::Handle<JSObject*> givenProto) { \
6114 return dom::WebGLType##_Binding::Wrap(cx, this, givenProto); \
6121 _(WebGLRenderbuffer
)
6126 _(WebGLTransformFeedback
)
6127 _(WebGLUniformLocation
)
6128 //_(WebGLVertexArray) // The webidl is `WebGLVertexArrayObject` :(
6132 JSObject
* WebGLVertexArrayJS::WrapObject(JSContext
* const cx
,
6133 JS::Handle
<JSObject
*> givenProto
) {
6134 return dom::WebGLVertexArrayObject_Binding::Wrap(cx
, this, givenProto
);
6137 bool WebGLActiveInfoJS::WrapObject(JSContext
* const cx
,
6138 JS::Handle
<JSObject
*> givenProto
,
6139 JS::MutableHandle
<JSObject
*> reflector
) {
6140 return dom::WebGLActiveInfo_Binding::Wrap(cx
, this, givenProto
, reflector
);
6143 bool WebGLShaderPrecisionFormatJS::WrapObject(
6144 JSContext
* const cx
, JS::Handle
<JSObject
*> givenProto
,
6145 JS::MutableHandle
<JSObject
*> reflector
) {
6146 return dom::WebGLShaderPrecisionFormat_Binding::Wrap(cx
, this, givenProto
,
6150 // ---------------------
6152 // Todo: Move this to RefPtr.h.
6153 template <typename T
>
6154 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6155 const RefPtr
<T
>& field
, const char* name
,
6157 ImplCycleCollectionTraverse(callback
, const_cast<RefPtr
<T
>&>(field
), name
,
6163 template <typename T
>
6164 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6165 const std::vector
<RefPtr
<T
>>& field
,
6166 const char* name
, uint32_t flags
) {
6167 for (const auto& cur
: field
) {
6168 ImplCycleCollectionTraverse(callback
, cur
, name
, flags
);
6172 template <typename T
>
6173 void ImplCycleCollectionUnlink(std::vector
<RefPtr
<T
>>& field
) {
6179 template <typename T
, size_t N
>
6180 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& callback
,
6181 const std::array
<RefPtr
<T
>, N
>& field
,
6182 const char* name
, uint32_t flags
) {
6183 for (const auto& cur
: field
) {
6184 ImplCycleCollectionTraverse(callback
, cur
, name
, flags
);
6188 template <typename T
, size_t N
>
6189 void ImplCycleCollectionUnlink(std::array
<RefPtr
<T
>, N
>& field
) {
6195 template <typename T
>
6196 void ImplCycleCollectionTraverse(
6197 nsCycleCollectionTraversalCallback
& callback
,
6198 const std::unordered_map
<GLenum
, RefPtr
<T
>>& field
, const char* name
,
6200 for (const auto& pair
: field
) {
6201 ImplCycleCollectionTraverse(callback
, pair
.second
, name
, flags
);
6205 template <typename T
>
6206 void ImplCycleCollectionUnlink(std::unordered_map
<GLenum
, RefPtr
<T
>>& field
) {
6212 void ImplCycleCollectionTraverse(
6213 nsCycleCollectionTraversalCallback
& callback
,
6214 const std::unordered_map
<GLenum
, WebGLFramebufferJS::Attachment
>& field
,
6215 const char* name
, uint32_t flags
) {
6216 for (const auto& pair
: field
) {
6217 const auto& attach
= pair
.second
;
6218 ImplCycleCollectionTraverse(callback
, attach
.rb
, name
, flags
);
6219 ImplCycleCollectionTraverse(callback
, attach
.tex
, name
, flags
);
6223 void ImplCycleCollectionUnlink(
6224 std::unordered_map
<GLenum
, WebGLFramebufferJS::Attachment
>& field
) {
6230 void ImplCycleCollectionTraverse(
6231 nsCycleCollectionTraversalCallback
& callback
,
6232 const std::unordered_map
<GLenum
, WebGLProgramJS::Attachment
>& field
,
6233 const char* name
, uint32_t flags
) {
6234 for (const auto& pair
: field
) {
6235 const auto& attach
= pair
.second
;
6236 ImplCycleCollectionTraverse(callback
, attach
.shader
, name
, flags
);
6240 void ImplCycleCollectionUnlink(
6241 std::unordered_map
<GLenum
, WebGLProgramJS::Attachment
>& field
) {
6247 void ImplCycleCollectionUnlink(
6248 const RefPtr
<ClientWebGLExtensionLoseContext
>& field
) {
6249 const_cast<RefPtr
<ClientWebGLExtensionLoseContext
>&>(field
) = nullptr;
6251 void ImplCycleCollectionUnlink(const RefPtr
<WebGLProgramJS
>& field
) {
6252 const_cast<RefPtr
<WebGLProgramJS
>&>(field
) = nullptr;
6254 void ImplCycleCollectionUnlink(const RefPtr
<WebGLShaderJS
>& field
) {
6255 const_cast<RefPtr
<WebGLShaderJS
>&>(field
) = nullptr;
6258 // ----------------------
6260 void ImplCycleCollectionTraverse(
6261 nsCycleCollectionTraversalCallback
& callback
,
6262 const std::shared_ptr
<webgl::NotLostData
>& field
, const char* name
,
6266 ImplCycleCollectionTraverse(callback
, field
->extensions
,
6267 "NotLostData.extensions", flags
);
6269 const auto& state
= field
->state
;
6271 ImplCycleCollectionTraverse(callback
, state
.mDefaultTfo
, "state.mDefaultTfo",
6273 ImplCycleCollectionTraverse(callback
, state
.mDefaultVao
, "state.mDefaultVao",
6276 ImplCycleCollectionTraverse(callback
, state
.mCurrentProgram
,
6277 "state.mCurrentProgram", flags
);
6279 ImplCycleCollectionTraverse(callback
, state
.mBoundBufferByTarget
,
6280 "state.mBoundBufferByTarget", flags
);
6281 ImplCycleCollectionTraverse(callback
, state
.mBoundUbos
, "state.mBoundUbos",
6283 ImplCycleCollectionTraverse(callback
, state
.mBoundDrawFb
,
6284 "state.mBoundDrawFb", flags
);
6285 ImplCycleCollectionTraverse(callback
, state
.mBoundReadFb
,
6286 "state.mBoundReadFb", flags
);
6287 ImplCycleCollectionTraverse(callback
, state
.mBoundRb
, "state.mBoundRb",
6289 ImplCycleCollectionTraverse(callback
, state
.mBoundTfo
, "state.mBoundTfo",
6291 ImplCycleCollectionTraverse(callback
, state
.mBoundVao
, "state.mBoundVao",
6293 ImplCycleCollectionTraverse(callback
, state
.mCurrentQueryByTarget
,
6294 "state.state.mCurrentQueryByTarget", flags
);
6296 for (const auto& texUnit
: state
.mTexUnits
) {
6297 ImplCycleCollectionTraverse(callback
, texUnit
.sampler
,
6298 "state.mTexUnits[].sampler", flags
);
6299 ImplCycleCollectionTraverse(callback
, texUnit
.texByTarget
,
6300 "state.mTexUnits[].texByTarget", flags
);
6304 void ImplCycleCollectionUnlink(std::shared_ptr
<webgl::NotLostData
>& field
) {
6306 const auto keepAlive
= field
;
6307 keepAlive
->extensions
= {};
6308 keepAlive
->state
= {};
6312 // -----------------------------------------------------
6314 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBufferJS
)
6315 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebufferJS
, mAttachments
)
6316 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgramJS
, mNextLink_Shaders
)
6317 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQueryJS
)
6318 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbufferJS
)
6319 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSamplerJS
)
6320 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShaderJS
)
6321 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSyncJS
)
6322 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTextureJS
)
6323 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLTransformFeedbackJS
, mAttribBuffers
,
6325 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocationJS
)
6326 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArrayJS
, mIndexBuffer
,
6331 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClientWebGLContext
)
6332 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
6333 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
6334 NS_INTERFACE_MAP_ENTRY(nsISupports
)
6335 NS_INTERFACE_MAP_END
6337 NS_IMPL_CYCLE_COLLECTING_ADDREF(ClientWebGLContext
)
6338 NS_IMPL_CYCLE_COLLECTING_RELEASE(ClientWebGLContext
)
6340 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(
6341 ClientWebGLContext
, mExtLoseContext
, mNotLost
,
6342 // Don't forget nsICanvasRenderingContextInternal:
6343 mCanvasElement
, mOffscreenCanvas
)
6345 // -----------------------------
6348 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGL##X##JS, AddRef) \
6349 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGL##X##JS, Release)
6360 _(TransformFeedback
)
6366 } // namespace mozilla