summaryrefslogtreecommitdiffstats
path: root/dom/canvas/ClientWebGLContext.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/canvas/ClientWebGLContext.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/ClientWebGLContext.h')
-rw-r--r--dom/canvas/ClientWebGLContext.h2401
1 files changed, 2401 insertions, 0 deletions
diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h
new file mode 100644
index 0000000000..47e03d29c5
--- /dev/null
+++ b/dom/canvas/ClientWebGLContext.h
@@ -0,0 +1,2401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CLIENTWEBGLCONTEXT_H_
+#define CLIENTWEBGLCONTEXT_H_
+
+#include "GLConsts.h"
+#include "js/GCAPI.h"
+#include "mozilla/dom/ImageData.h"
+#include "mozilla/Range.h"
+#include "mozilla/RefCounted.h"
+#include "mozilla/dom/TypedArray.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/StaticPrefs_webgl.h"
+#include "WebGLFormats.h"
+#include "WebGLStrongTypes.h"
+#include "WebGLTypes.h"
+
+#include "mozilla/Logging.h"
+#include "WebGLCommandQueue.h"
+
+#include <memory>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+namespace mozilla {
+
+class ClientWebGLExtensionBase;
+class HostWebGLContext;
+
+template <typename MethodT, MethodT Method>
+size_t IdByMethod();
+
+namespace dom {
+class OwningHTMLCanvasElementOrOffscreenCanvas;
+class WebGLChild;
+} // namespace dom
+
+namespace webgl {
+class AvailabilityRunnable;
+class TexUnpackBlob;
+class TexUnpackBytes;
+} // namespace webgl
+
+////////////////////////////////////
+
+class WebGLActiveInfoJS final : public RefCounted<WebGLActiveInfoJS> {
+ public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(WebGLActiveInfoJS)
+
+ const webgl::ActiveInfo mInfo;
+
+ explicit WebGLActiveInfoJS(const webgl::ActiveInfo& info) : mInfo(info) {}
+
+ virtual ~WebGLActiveInfoJS() = default;
+
+ // -
+ // WebIDL attributes
+
+ GLint Size() const { return static_cast<GLint>(mInfo.elemCount); }
+ GLenum Type() const { return mInfo.elemType; }
+
+ void GetName(nsString& retval) const { CopyUTF8toUTF16(mInfo.name, retval); }
+
+ bool WrapObject(JSContext*, JS::Handle<JSObject*>,
+ JS::MutableHandle<JSObject*>);
+};
+
+class WebGLShaderPrecisionFormatJS final
+ : public RefCounted<WebGLShaderPrecisionFormatJS> {
+ public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(WebGLShaderPrecisionFormatJS)
+
+ const webgl::ShaderPrecisionFormat mInfo;
+
+ explicit WebGLShaderPrecisionFormatJS(
+ const webgl::ShaderPrecisionFormat& info)
+ : mInfo(info) {}
+
+ virtual ~WebGLShaderPrecisionFormatJS() = default;
+
+ GLint RangeMin() const { return mInfo.rangeMin; }
+ GLint RangeMax() const { return mInfo.rangeMax; }
+ GLint Precision() const { return mInfo.precision; }
+
+ bool WrapObject(JSContext*, JS::Handle<JSObject*>,
+ JS::MutableHandle<JSObject*>);
+};
+
+// -----------------------
+
+class ClientWebGLContext;
+class WebGLBufferJS;
+class WebGLFramebufferJS;
+class WebGLProgramJS;
+class WebGLQueryJS;
+class WebGLRenderbufferJS;
+class WebGLSamplerJS;
+class WebGLShaderJS;
+class WebGLTextureJS;
+class WebGLTransformFeedbackJS;
+class WebGLVertexArrayJS;
+
+namespace webgl {
+
+struct LinkResult;
+
+class ProgramKeepAlive final {
+ friend class mozilla::WebGLProgramJS;
+
+ WebGLProgramJS* mParent;
+
+ public:
+ explicit ProgramKeepAlive(WebGLProgramJS& parent) : mParent(&parent) {}
+ ~ProgramKeepAlive();
+};
+
+class ShaderKeepAlive final {
+ friend class mozilla::WebGLShaderJS;
+
+ const WebGLShaderJS* mParent;
+
+ public:
+ explicit ShaderKeepAlive(const WebGLShaderJS& parent) : mParent(&parent) {}
+ ~ShaderKeepAlive();
+};
+
+class ContextGenerationInfo final {
+ private:
+ ObjectId mLastId = 0;
+
+ public:
+ webgl::ExtensionBits mEnabledExtensions;
+ RefPtr<WebGLProgramJS> mCurrentProgram;
+ std::shared_ptr<webgl::ProgramKeepAlive> mProgramKeepAlive;
+ mutable std::shared_ptr<webgl::LinkResult> mActiveLinkResult;
+
+ RefPtr<WebGLTransformFeedbackJS> mDefaultTfo;
+ RefPtr<WebGLVertexArrayJS> mDefaultVao;
+
+ std::unordered_map<GLenum, RefPtr<WebGLBufferJS>> mBoundBufferByTarget;
+ std::vector<RefPtr<WebGLBufferJS>> mBoundUbos;
+ RefPtr<WebGLFramebufferJS> mBoundDrawFb;
+ RefPtr<WebGLFramebufferJS> mBoundReadFb;
+ RefPtr<WebGLRenderbufferJS> mBoundRb;
+ RefPtr<WebGLTransformFeedbackJS> mBoundTfo;
+ RefPtr<WebGLVertexArrayJS> mBoundVao;
+ std::unordered_map<GLenum, RefPtr<WebGLQueryJS>> mCurrentQueryByTarget;
+
+ struct TexUnit final {
+ RefPtr<WebGLSamplerJS> sampler;
+ std::unordered_map<GLenum, RefPtr<WebGLTextureJS>> texByTarget;
+ };
+ uint32_t mActiveTexUnit = 0;
+ std::vector<TexUnit> mTexUnits;
+
+ bool mTfActiveAndNotPaused = false;
+
+ std::vector<TypedQuad> mGenericVertexAttribs;
+
+ std::array<int32_t, 4> mScissor = {};
+ std::array<int32_t, 4> mViewport = {};
+ std::array<float, 4> mClearColor = {{0, 0, 0, 0}};
+ std::array<float, 4> mBlendColor = {{0, 0, 0, 0}};
+ std::array<float, 2> mDepthRange = {{0, 1}};
+ webgl::PixelPackingState mPixelPackState;
+ webgl::PixelUnpackStateWebgl mPixelUnpackState;
+
+ std::vector<GLenum> mCompressedTextureFormats;
+
+ Maybe<uvec2> mDrawingBufferSize;
+
+ webgl::ProvokingVertex mProvokingVertex = webgl::ProvokingVertex::LastVertex;
+
+ ObjectId NextId() { return mLastId += 1; }
+};
+
+// -
+
+// In the cross process case, the WebGL actor's ownership relationship looks
+// like this:
+// ---------------------------------------------------------------------
+// | ClientWebGLContext -> WebGLChild -> WebGLParent -> HostWebGLContext
+// ---------------------------------------------------------------------
+//
+// where 'A -> B' means "A owns B"
+
+struct NotLostData final {
+ ClientWebGLContext& context;
+ webgl::InitContextResult info;
+
+ RefPtr<mozilla::dom::WebGLChild> outOfProcess;
+ UniquePtr<HostWebGLContext> inProcess;
+
+ webgl::ContextGenerationInfo state;
+ std::array<RefPtr<ClientWebGLExtensionBase>,
+ UnderlyingValue(WebGLExtensionID::Max)>
+ extensions;
+
+ RefPtr<layers::CanvasRenderer> mCanvasRenderer;
+
+ explicit NotLostData(ClientWebGLContext& context);
+ ~NotLostData();
+};
+
+// -
+
+class ObjectJS {
+ friend ClientWebGLContext;
+
+ public:
+ const std::weak_ptr<NotLostData> mGeneration;
+ const ObjectId mId;
+
+ protected:
+ bool mDeleteRequested = false;
+
+ explicit ObjectJS(const ClientWebGLContext&);
+ virtual ~ObjectJS() = default;
+
+ public:
+ ClientWebGLContext* Context() const {
+ const auto locked = mGeneration.lock();
+ if (!locked) return nullptr;
+ return &(locked->context);
+ }
+
+ ClientWebGLContext* GetParentObject() const { return Context(); }
+
+ // A la carte:
+ bool IsForContext(const ClientWebGLContext&) const;
+ virtual bool IsDeleted() const { return mDeleteRequested; }
+
+ bool IsUsable(const ClientWebGLContext& context) const {
+ return IsForContext(context) && !IsDeleted();
+ }
+
+ // The workhorse:
+ bool ValidateUsable(const ClientWebGLContext& context,
+ const char* const argName) const {
+ if (MOZ_LIKELY(IsUsable(context))) return true;
+ WarnInvalidUse(context, argName);
+ return false;
+ }
+
+ // Use by DeleteFoo:
+ bool ValidateForContext(const ClientWebGLContext& context,
+ const char* const argName) const;
+
+ private:
+ void WarnInvalidUse(const ClientWebGLContext&, const char* argName) const;
+
+ // The enum is INVALID_VALUE for Program/Shader :(
+ virtual GLenum ErrorOnDeleted() const { return LOCAL_GL_INVALID_OPERATION; }
+};
+
+} // namespace webgl
+
+// -------------------------
+
+class WebGLBufferJS final : public nsWrapperCache, public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ webgl::BufferKind mKind =
+ webgl::BufferKind::Undefined; // !IsBuffer until Bind
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBufferJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLBufferJS)
+
+ explicit WebGLBufferJS(const ClientWebGLContext& webgl)
+ : webgl::ObjectJS(webgl) {}
+
+ private:
+ ~WebGLBufferJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLFramebufferJS final : public nsWrapperCache, public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ public:
+ struct Attachment final {
+ RefPtr<WebGLRenderbufferJS> rb;
+ RefPtr<WebGLTextureJS> tex;
+ };
+
+ private:
+ bool mHasBeenBound = false; // !IsFramebuffer until Bind
+ std::unordered_map<GLenum, Attachment> mAttachments;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebufferJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLFramebufferJS)
+
+ explicit WebGLFramebufferJS(const ClientWebGLContext&, bool opaque = false);
+
+ const bool mOpaque;
+ bool mInOpaqueRAF = false;
+
+ private:
+ ~WebGLFramebufferJS();
+
+ void EnsureColorAttachments();
+
+ public:
+ Attachment* GetAttachment(const GLenum slotEnum) {
+ auto ret = MaybeFind(mAttachments, slotEnum);
+ if (!ret) {
+ EnsureColorAttachments();
+ ret = MaybeFind(mAttachments, slotEnum);
+ }
+ return ret;
+ }
+
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLProgramJS final : public nsWrapperCache, public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgramJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLProgramJS)
+ // Must come first!
+ // If the REFCOUNTING macro isn't declared first, the AddRef at
+ // mInnerRef->js will panic when REFCOUNTING's "owning thread" var is still
+ // uninitialized.
+
+ struct Attachment final {
+ RefPtr<WebGLShaderJS> shader;
+ std::shared_ptr<webgl::ShaderKeepAlive> keepAlive;
+ };
+
+ private:
+ std::shared_ptr<webgl::ProgramKeepAlive> mKeepAlive;
+ const std::weak_ptr<webgl::ProgramKeepAlive> mKeepAliveWeak;
+
+ std::unordered_map<GLenum, Attachment> mNextLink_Shaders;
+ bool mLastValidate = false;
+ mutable std::shared_ptr<webgl::LinkResult>
+ mResult; // Never null, often defaulted.
+
+ struct UniformLocInfo final {
+ const uint32_t location;
+ const GLenum elemType;
+ };
+
+ mutable Maybe<std::unordered_map<std::string, UniformLocInfo>>
+ mUniformLocByName;
+ mutable std::vector<uint32_t> mUniformBlockBindings;
+
+ std::unordered_set<const WebGLTransformFeedbackJS*> mActiveTfos;
+
+ explicit WebGLProgramJS(const ClientWebGLContext&);
+
+ ~WebGLProgramJS() {
+ mKeepAlive = nullptr; // Try to delete.
+
+ const auto& maybe = mKeepAliveWeak.lock();
+ if (maybe) {
+ maybe->mParent = nullptr;
+ }
+ }
+
+ public:
+ bool IsDeleted() const override { return !mKeepAliveWeak.lock(); }
+ GLenum ErrorOnDeleted() const override { return LOCAL_GL_INVALID_VALUE; }
+
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLQueryJS final : public nsWrapperCache,
+ public webgl::ObjectJS,
+ public SupportsWeakPtr {
+ friend class ClientWebGLContext;
+ friend class webgl::AvailabilityRunnable;
+
+ GLenum mTarget = 0; // !IsQuery until Bind
+ bool mCanBeAvailable = false;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLQueryJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLQueryJS)
+
+ explicit WebGLQueryJS(const ClientWebGLContext& webgl)
+ : webgl::ObjectJS(webgl) {}
+
+ private:
+ ~WebGLQueryJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLRenderbufferJS final : public nsWrapperCache,
+ public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLRenderbufferJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLRenderbufferJS)
+
+ private:
+ bool mHasBeenBound = false; // !IsRenderbuffer until Bind
+
+ explicit WebGLRenderbufferJS(const ClientWebGLContext& webgl)
+ : webgl::ObjectJS(webgl) {}
+ ~WebGLRenderbufferJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLSamplerJS final : public nsWrapperCache, public webgl::ObjectJS {
+ // IsSampler without Bind
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSamplerJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLSamplerJS)
+
+ explicit WebGLSamplerJS(const ClientWebGLContext& webgl)
+ : webgl::ObjectJS(webgl) {}
+
+ private:
+ ~WebGLSamplerJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLShaderJS final : public nsWrapperCache, public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLShaderJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLShaderJS)
+
+ private:
+ const GLenum mType;
+ std::string mSource;
+ std::shared_ptr<webgl::ShaderKeepAlive> mKeepAlive;
+ const std::weak_ptr<webgl::ShaderKeepAlive> mKeepAliveWeak;
+
+ mutable webgl::CompileResult mResult;
+
+ WebGLShaderJS(const ClientWebGLContext&, GLenum type);
+
+ ~WebGLShaderJS() {
+ mKeepAlive = nullptr; // Try to delete.
+
+ const auto& maybe = mKeepAliveWeak.lock();
+ if (maybe) {
+ maybe->mParent = nullptr;
+ }
+ }
+
+ public:
+ bool IsDeleted() const override { return !mKeepAliveWeak.lock(); }
+ GLenum ErrorOnDeleted() const override { return LOCAL_GL_INVALID_VALUE; }
+
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLSyncJS final : public nsWrapperCache,
+ public webgl::ObjectJS,
+ public SupportsWeakPtr {
+ friend class ClientWebGLContext;
+ friend class webgl::AvailabilityRunnable;
+
+ bool mCanBeAvailable = false;
+ uint8_t mNumQueriesBeforeFirstFrameBoundary = 0;
+ bool mSignaled = false;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSyncJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLSyncJS)
+
+ explicit WebGLSyncJS(const ClientWebGLContext& webgl)
+ : webgl::ObjectJS(webgl) {}
+
+ private:
+ ~WebGLSyncJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLTextureJS final : public nsWrapperCache, public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ GLenum mTarget = 0; // !IsTexture until Bind
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTextureJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLTextureJS)
+
+ explicit WebGLTextureJS(const ClientWebGLContext& webgl)
+ : webgl::ObjectJS(webgl) {}
+
+ private:
+ ~WebGLTextureJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLTransformFeedbackJS final : public nsWrapperCache,
+ public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ bool mHasBeenBound = false; // !IsTransformFeedback until Bind
+ bool mActiveOrPaused = false;
+ std::vector<RefPtr<WebGLBufferJS>> mAttribBuffers;
+ RefPtr<WebGLProgramJS> mActiveProgram;
+ std::shared_ptr<webgl::ProgramKeepAlive> mActiveProgramKeepAlive;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedbackJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLTransformFeedbackJS)
+
+ explicit WebGLTransformFeedbackJS(const ClientWebGLContext&);
+
+ private:
+ ~WebGLTransformFeedbackJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+std::array<uint16_t, 3> ValidUploadElemTypes(GLenum);
+
+class WebGLUniformLocationJS final : public nsWrapperCache,
+ public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ const std::weak_ptr<webgl::LinkResult> mParent;
+ const uint32_t mLocation;
+ const std::array<uint16_t, 3> mValidUploadElemTypes;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocationJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLUniformLocationJS)
+
+ WebGLUniformLocationJS(const ClientWebGLContext& webgl,
+ std::weak_ptr<webgl::LinkResult> parent, uint32_t loc,
+ GLenum elemType)
+ : webgl::ObjectJS(webgl),
+ mParent(parent),
+ mLocation(loc),
+ mValidUploadElemTypes(ValidUploadElemTypes(elemType)) {}
+
+ private:
+ ~WebGLUniformLocationJS() = default;
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+// -
+
+class WebGLVertexArrayJS final : public nsWrapperCache, public webgl::ObjectJS {
+ friend class ClientWebGLContext;
+
+ bool mHasBeenBound = false; // !IsVertexArray until Bind
+ RefPtr<WebGLBufferJS> mIndexBuffer;
+ std::vector<RefPtr<WebGLBufferJS>> mAttribBuffers;
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArrayJS)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebGLVertexArrayJS)
+
+ explicit WebGLVertexArrayJS(const ClientWebGLContext&);
+
+ private:
+ ~WebGLVertexArrayJS();
+
+ public:
+ JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+};
+
+////////////////////////////////////
+
+using Float32ListU = dom::MaybeSharedFloat32ArrayOrUnrestrictedFloatSequence;
+using Int32ListU = dom::MaybeSharedInt32ArrayOrLongSequence;
+using Uint32ListU = dom::MaybeSharedUint32ArrayOrUnsignedLongSequence;
+
+template <typename Converter, typename T>
+inline bool ConvertSequence(const dom::Sequence<T>& sequence,
+ Converter&& converter) {
+ // It's ok to GC here, but we need a common parameter list for
+ // Converter with the typed array version.
+ JS::AutoCheckCannotGC nogc;
+ nogc.reset();
+ return std::forward<Converter>(converter)(Span(sequence), std::move(nogc));
+}
+
+template <typename Converter>
+inline bool Convert(const Float32ListU& list, Converter&& converter) {
+ if (list.IsFloat32Array()) {
+ return list.GetAsFloat32Array().ProcessData(
+ std::forward<Converter>(converter));
+ }
+
+ return ConvertSequence(list.GetAsUnrestrictedFloatSequence(),
+ std::forward<Converter>(converter));
+}
+
+template <typename Converter>
+inline bool Convert(const Int32ListU& list, Converter&& converter) {
+ if (list.IsInt32Array()) {
+ return list.GetAsInt32Array().ProcessData(
+ std::forward<Converter>(converter));
+ }
+
+ return ConvertSequence(list.GetAsLongSequence(),
+ std::forward<Converter>(converter));
+}
+
+template <typename Converter>
+inline bool Convert(const Uint32ListU& list, Converter&& converter) {
+ if (list.IsUint32Array()) {
+ return list.GetAsUint32Array().ProcessData(
+ std::forward<Converter>(converter));
+ }
+
+ return ConvertSequence(list.GetAsUnsignedLongSequence(),
+ std::forward<Converter>(converter));
+}
+
+template <typename T>
+inline Range<const uint8_t> MakeByteRange(const T& x) {
+ const auto typed = MakeRange(x);
+ return Range<const uint8_t>(
+ reinterpret_cast<const uint8_t*>(typed.begin().get()),
+ typed.length() * sizeof(typed[0]));
+}
+
+template <typename T>
+inline Range<const uint8_t> MakeByteRange(const Span<T>& x) {
+ return AsBytes(x);
+}
+
+// -
+
+struct TexImageSourceAdapter final : public TexImageSource {
+ TexImageSourceAdapter(const dom::Nullable<dom::ArrayBufferView>* maybeView,
+ ErrorResult*) {
+ if (!maybeView->IsNull()) {
+ mView = &(maybeView->Value());
+ }
+ }
+
+ TexImageSourceAdapter(const dom::Nullable<dom::ArrayBufferView>* maybeView,
+ GLuint viewElemOffset) {
+ if (!maybeView->IsNull()) {
+ mView = &(maybeView->Value());
+ }
+ mViewElemOffset = viewElemOffset;
+ }
+
+ TexImageSourceAdapter(const dom::ArrayBufferView* view, ErrorResult*) {
+ mView = view;
+ }
+
+ TexImageSourceAdapter(const dom::ArrayBufferView* view, GLuint viewElemOffset,
+ GLuint viewElemLengthOverride = 0) {
+ mView = view;
+ mViewElemOffset = viewElemOffset;
+ mViewElemLengthOverride = viewElemLengthOverride;
+ }
+
+ explicit TexImageSourceAdapter(const WebGLintptr* pboOffset,
+ GLuint ignored1 = 0, GLuint ignored2 = 0) {
+ mPboOffset = pboOffset;
+ }
+
+ TexImageSourceAdapter(const WebGLintptr* pboOffset, ErrorResult* ignored) {
+ mPboOffset = pboOffset;
+ }
+
+ TexImageSourceAdapter(const dom::ImageBitmap* imageBitmap,
+ ErrorResult* out_error) {
+ mImageBitmap = imageBitmap;
+ mOut_error = out_error;
+ }
+
+ TexImageSourceAdapter(const dom::ImageData* imageData, ErrorResult*) {
+ mImageData = imageData;
+ }
+
+ TexImageSourceAdapter(const dom::OffscreenCanvas* offscreenCanvas,
+ ErrorResult* const out_error) {
+ mOffscreenCanvas = offscreenCanvas;
+ mOut_error = out_error;
+ }
+
+ TexImageSourceAdapter(const dom::VideoFrame* videoFrame,
+ ErrorResult* const out_error) {
+ mVideoFrame = videoFrame;
+ mOut_error = out_error;
+ }
+
+ TexImageSourceAdapter(const dom::Element* domElem,
+ ErrorResult* const out_error) {
+ mDomElem = domElem;
+ mOut_error = out_error;
+ }
+};
+
+/**
+ * Base class for all IDL implementations of WebGLContext
+ */
+class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
+ public nsWrapperCache {
+ friend class webgl::AvailabilityRunnable;
+ friend class webgl::ObjectJS;
+ friend class webgl::ProgramKeepAlive;
+ friend class webgl::ShaderKeepAlive;
+
+ // ----------------------------- Lifetime and DOM ---------------------------
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ClientWebGLContext)
+
+ JSObject* WrapObject(JSContext* cx,
+ JS::Handle<JSObject*> givenProto) override {
+ if (mIsWebGL2) {
+ return dom::WebGL2RenderingContext_Binding::Wrap(cx, this, givenProto);
+ }
+ return dom::WebGLRenderingContext_Binding::Wrap(cx, this, givenProto);
+ }
+
+ // -
+
+ public:
+ const bool mIsWebGL2;
+
+ private:
+ bool mIsCanvasDirty = false;
+ uvec2 mRequestedSize = {};
+
+ public:
+ explicit ClientWebGLContext(bool webgl2);
+
+ private:
+ virtual ~ClientWebGLContext();
+
+ const RefPtr<ClientWebGLExtensionLoseContext> mExtLoseContext;
+
+ mutable std::shared_ptr<webgl::NotLostData> mNotLost;
+ mutable GLenum mNextError = 0;
+ mutable webgl::LossStatus mLossStatus = webgl::LossStatus::Ready;
+ mutable bool mAwaitingRestore = false;
+
+ public:
+ // Holds Some Id if async present is used
+ mutable Maybe<layers::RemoteTextureId> mLastRemoteTextureId;
+ mutable Maybe<layers::RemoteTextureOwnerId> mRemoteTextureOwnerId;
+ mutable RefPtr<layers::FwdTransactionTracker> mFwdTransactionTracker;
+
+ // -
+
+ public:
+ const auto& Limits() const { return mNotLost->info.limits; }
+ const auto& Vendor() const { return mNotLost->info.vendor; }
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#actual-context-parameters
+ const WebGLContextOptions& ActualContextParameters() const {
+ MOZ_ASSERT(mNotLost != nullptr);
+ return mNotLost->info.options;
+ }
+
+ auto& State() { return mNotLost->state; }
+ const auto& State() const {
+ return const_cast<ClientWebGLContext*>(this)->State();
+ }
+
+ // -
+
+ private:
+ mutable RefPtr<webgl::AvailabilityRunnable> mAvailabilityRunnable;
+
+ public:
+ webgl::AvailabilityRunnable& EnsureAvailabilityRunnable() const;
+
+ // -
+
+ public:
+ void EmulateLoseContext() const;
+ void OnContextLoss(webgl::ContextLossReason) const;
+ void RestoreContext(webgl::LossStatus requiredStatus) const;
+
+ private:
+ bool DispatchEvent(const nsAString&) const;
+ void Event_webglcontextlost() const;
+ void Event_webglcontextrestored() const;
+
+ bool CreateHostContext(const uvec2& requestedSize);
+ void ThrowEvent_WebGLContextCreationError(const std::string&) const;
+
+ void UpdateCanvasParameters();
+
+ public:
+ void MarkCanvasDirty();
+
+ void MarkContextClean() override {}
+
+ void OnBeforePaintTransaction() override;
+
+ mozilla::dom::WebGLChild* GetChild() const {
+ if (!mNotLost) return nullptr;
+ if (!mNotLost->outOfProcess) return nullptr;
+ return mNotLost->outOfProcess.get();
+ }
+
+ // -------------------------------------------------------------------------
+ // Client WebGL API call tracking and error message reporting
+ // -------------------------------------------------------------------------
+ public:
+ // Remembers the WebGL function that is lowest on the stack for client-side
+ // error generation.
+ class FuncScope final {
+ public:
+ const ClientWebGLContext& mWebGL;
+ const std::shared_ptr<webgl::NotLostData> mKeepNotLostOrNull;
+ const char* const mFuncName;
+
+ FuncScope(const ClientWebGLContext& webgl, const char* funcName)
+ : mWebGL(webgl),
+ mKeepNotLostOrNull(webgl.mNotLost),
+ mFuncName(funcName) {
+ // Only set if an "outer" scope hasn't already been set.
+ if (!mWebGL.mFuncScope) {
+ mWebGL.mFuncScope = this;
+ }
+ }
+
+ ~FuncScope() {
+ if (this == mWebGL.mFuncScope) {
+ mWebGL.mFuncScope = nullptr;
+ }
+ }
+
+ FuncScope(const FuncScope&) = delete;
+ FuncScope(FuncScope&&) = delete;
+ };
+
+ protected:
+ // The scope of the function at the top of the current WebGL function call
+ // stack
+ mutable FuncScope* mFuncScope = nullptr;
+
+ const char* FuncName() const {
+ return mFuncScope ? mFuncScope->mFuncName : nullptr;
+ }
+
+ public:
+ template <typename... Args>
+ void EnqueueError(const GLenum error, const char* const format,
+ const Args&... args) const {
+ MOZ_ASSERT(FuncName());
+ nsCString text;
+ text.AppendPrintf("WebGL warning: %s: ", FuncName());
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wformat-security"
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-security"
+#endif
+ text.AppendPrintf(format, args...);
+#ifdef __clang__
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+ EnqueueErrorImpl(error, text);
+ }
+
+ void EnqueueError(const webgl::ErrorInfo& info) const {
+ EnqueueError(info.type, "%s", info.info.c_str());
+ }
+
+ template <typename... Args>
+ void EnqueueWarning(const char* const format, const Args&... args) const {
+ EnqueueError(0, format, args...);
+ }
+
+ template <typename... Args>
+ void EnqueuePerfWarning(const char* const format, const Args&... args) const {
+ EnqueueError(webgl::kErrorPerfWarning, format, args...);
+ }
+
+ void EnqueueError_ArgEnum(const char* argName,
+ GLenum val) const; // Cold code.
+
+ private:
+ void EnqueueErrorImpl(GLenum errorOrZero, const nsACString&) const;
+
+ public:
+ Maybe<Range<uint8_t>> ValidateArrayBufferView(const Span<uint8_t>& bytes,
+ size_t elemSize,
+ GLuint elemOffset,
+ GLuint elemCountOverride,
+ const GLenum errorEnum) const;
+
+ protected:
+ template <typename T>
+ bool ValidateNonNull(const char* const argName,
+ const dom::Nullable<T>& maybe) const {
+ if (maybe.IsNull()) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "%s: Cannot be null.", argName);
+ return false;
+ }
+ return true;
+ }
+
+ bool ValidateNonNegative(const char* argName, int64_t val) const {
+ if (MOZ_UNLIKELY(val < 0)) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "`%s` must be non-negative.",
+ argName);
+ return false;
+ }
+ return true;
+ }
+
+ bool ValidateViewType(GLenum unpackType, const TexImageSource& src) const;
+
+ Maybe<uvec3> ValidateExtents(GLsizei width, GLsizei height, GLsizei depth,
+ GLint border) const;
+
+ // -------------------------------------------------------------------------
+ // nsICanvasRenderingContextInternal / nsAPostRefreshObserver
+ // -------------------------------------------------------------------------
+ public:
+ bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
+ layers::CanvasRenderer* aRenderer) override;
+
+ void MarkContextCleanForFrameCapture() override {
+ mFrameCaptureState = FrameCaptureState::CLEAN;
+ }
+ // Note that 'clean' here refers to its invalidation state, not the
+ // contents of the buffer.
+ Watchable<FrameCaptureState>* GetFrameCaptureState() override {
+ return &mFrameCaptureState;
+ }
+
+ void OnMemoryPressure() override;
+ void SetContextOptions(const WebGLContextOptions& aOptions) {
+ mInitialOptions.emplace(aOptions);
+ }
+ const WebGLContextOptions& GetContextOptions() const {
+ return mInitialOptions.ref();
+ }
+ NS_IMETHOD
+ SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options,
+ ErrorResult& aRvForDictionaryInit) override;
+ NS_IMETHOD
+ SetDimensions(int32_t width, int32_t height) override;
+ bool UpdateWebRenderCanvasData(
+ nsDisplayListBuilder* aBuilder,
+ layers::WebRenderCanvasData* aCanvasData) override;
+
+ // ------
+
+ int32_t GetWidth() override { return AutoAssertCast(DrawingBufferSize().x); }
+ int32_t GetHeight() override { return AutoAssertCast(DrawingBufferSize().y); }
+
+ NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
+ NotNull<gfx::DrawTarget*>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ void ResetBitmap() override;
+
+ UniquePtr<uint8_t[]> GetImageBuffer(int32_t* out_format,
+ gfx::IntSize* out_imageSize) override;
+ NS_IMETHOD GetInputStream(const char* mimeType,
+ const nsAString& encoderOptions,
+ nsIInputStream** out_stream) override;
+
+ already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(
+ gfxAlphaType* out_alphaType) override;
+
+ void SetOpaqueValueFromOpaqueAttr(bool) override{};
+ bool GetIsOpaque() override { return !mInitialOptions->alpha; }
+
+ /**
+ * An abstract base class to be implemented by callers wanting to be notified
+ * that a refresh has occurred. Callers must ensure an observer is removed
+ * before it is destroyed.
+ */
+ void DidRefresh() override;
+
+ NS_IMETHOD Redraw(const gfxRect&) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ // ------
+
+ protected:
+ layers::LayersBackend GetCompositorBackendType() const;
+
+ Watchable<FrameCaptureState> mFrameCaptureState = {
+ FrameCaptureState::CLEAN, "ClientWebGLContext::mFrameCaptureState"};
+
+ // -------------------------------------------------------------------------
+ // WebGLRenderingContext Basic Properties and Methods
+ // -------------------------------------------------------------------------
+ public:
+ dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
+ void Commit();
+ void GetCanvas(
+ dom::Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
+
+ GLsizei DrawingBufferWidth() {
+ const FuncScope funcScope(*this, "drawingBufferWidth");
+ return AutoAssertCast(DrawingBufferSize().x);
+ }
+ GLsizei DrawingBufferHeight() {
+ const FuncScope funcScope(*this, "drawingBufferHeight");
+ return AutoAssertCast(DrawingBufferSize().y);
+ }
+ void GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
+
+ private:
+ webgl::SwapChainOptions PrepareAsyncSwapChainOptions(
+ WebGLFramebufferJS* fb, bool webvr,
+ const webgl::SwapChainOptions& options = webgl::SwapChainOptions());
+
+ public:
+ layers::TextureType GetTexTypeForSwapChain() const;
+ void Present(
+ WebGLFramebufferJS*, const bool webvr = false,
+ const webgl::SwapChainOptions& options = webgl::SwapChainOptions());
+ void Present(
+ WebGLFramebufferJS*, layers::TextureType, const bool webvr = false,
+ const webgl::SwapChainOptions& options = webgl::SwapChainOptions());
+ void CopyToSwapChain(
+ WebGLFramebufferJS*,
+ const webgl::SwapChainOptions& options = webgl::SwapChainOptions());
+ void EndOfFrame();
+ Maybe<layers::SurfaceDescriptor> GetFrontBuffer(
+ WebGLFramebufferJS*, const bool webvr = false) override;
+ Maybe<layers::SurfaceDescriptor> PresentFrontBuffer(
+ WebGLFramebufferJS*, layers::TextureType,
+ const bool webvr = false) override;
+ RefPtr<gfx::SourceSurface> GetFrontBufferSnapshot(
+ bool requireAlphaPremult = true) override;
+ already_AddRefed<layers::FwdTransactionTracker> UseCompositableForwarder(
+ layers::CompositableForwarder* aForwarder) override;
+ void OnDestroyChild(dom::WebGLChild* aChild);
+
+ void ClearVRSwapChain();
+
+ private:
+ RefPtr<gfx::DataSourceSurface> BackBufferSnapshot();
+ [[nodiscard]] bool DoReadPixels(const webgl::ReadPixelsDesc&,
+ Range<uint8_t>) const;
+ uvec2 DrawingBufferSize();
+
+ // -
+
+ bool mAutoFlushPending = false;
+
+ void AutoEnqueueFlush() {
+ if (MOZ_LIKELY(mAutoFlushPending)) return;
+ mAutoFlushPending = true;
+
+ const auto weak = WeakPtr<ClientWebGLContext>(this);
+ const auto DeferredFlush = [weak]() {
+ const auto strong = RefPtr<ClientWebGLContext>(weak);
+ if (!strong) return;
+ if (!strong->mAutoFlushPending) return;
+ strong->mAutoFlushPending = false;
+
+ if (!StaticPrefs::webgl_auto_flush()) return;
+ const bool flushGl = StaticPrefs::webgl_auto_flush_gl();
+ strong->Flush(flushGl);
+ };
+
+ already_AddRefed<mozilla::CancelableRunnable> runnable =
+ NS_NewCancelableRunnableFunction("enqueue Event_webglcontextrestored",
+ DeferredFlush);
+ NS_DispatchToCurrentThread(std::move(runnable));
+ }
+
+ void CancelAutoFlush() { mAutoFlushPending = false; }
+
+ // -
+
+ void AfterDrawCall() {
+ if (!mNotLost) return;
+ const auto& state = State();
+ if (!state.mBoundDrawFb) {
+ MarkCanvasDirty();
+ }
+
+ AutoEnqueueFlush();
+ }
+
+ // -------------------------------------------------------------------------
+ // Client-side helper methods. Dispatch to a Host method.
+ // -------------------------------------------------------------------------
+
+ // ------------------------- GL State -------------------------
+ public:
+ bool IsContextLost() const { return !mNotLost; }
+
+ void Disable(GLenum cap) const { SetEnabledI(cap, {}, false); }
+ void Enable(GLenum cap) const { SetEnabledI(cap, {}, true); }
+ void SetEnabledI(GLenum cap, Maybe<GLuint> i, bool val) const;
+ bool IsEnabled(GLenum cap) const;
+
+ private:
+ Maybe<double> GetNumber(GLenum pname);
+ Maybe<std::string> GetString(GLenum pname);
+
+ public:
+ void GetParameter(JSContext* cx, GLenum pname,
+ JS::MutableHandle<JS::Value> retval, ErrorResult& rv,
+ bool debug = false);
+
+ void GetBufferParameter(JSContext* cx, GLenum target, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+
+ void GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
+ GLenum attachment, GLenum pname,
+ JS::MutableHandle<JS::Value> retval,
+ ErrorResult& rv) const;
+
+ void GetRenderbufferParameter(JSContext* cx, GLenum target, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+
+ void GetIndexedParameter(JSContext* cx, GLenum target, GLuint index,
+ JS::MutableHandle<JS::Value> retval,
+ ErrorResult& rv) const;
+
+ already_AddRefed<WebGLShaderPrecisionFormatJS> GetShaderPrecisionFormat(
+ GLenum shadertype, GLenum precisiontype);
+
+ void UseProgram(WebGLProgramJS*);
+ void ValidateProgram(WebGLProgramJS&) const;
+
+ // -
+
+ already_AddRefed<WebGLBufferJS> CreateBuffer() const;
+ already_AddRefed<WebGLFramebufferJS> CreateFramebuffer() const;
+ already_AddRefed<WebGLFramebufferJS> CreateOpaqueFramebuffer(
+ const webgl::OpaqueFramebufferOptions&) const;
+ already_AddRefed<WebGLProgramJS> CreateProgram() const;
+ already_AddRefed<WebGLQueryJS> CreateQuery() const;
+ already_AddRefed<WebGLRenderbufferJS> CreateRenderbuffer() const;
+ already_AddRefed<WebGLSamplerJS> CreateSampler() const;
+ already_AddRefed<WebGLShaderJS> CreateShader(GLenum type) const;
+ already_AddRefed<WebGLSyncJS> FenceSync(GLenum condition,
+ GLbitfield flags) const;
+ already_AddRefed<WebGLTextureJS> CreateTexture() const;
+ already_AddRefed<WebGLTransformFeedbackJS> CreateTransformFeedback() const;
+ already_AddRefed<WebGLVertexArrayJS> CreateVertexArray() const;
+
+ void DeleteBuffer(WebGLBufferJS*);
+ void DeleteFramebuffer(WebGLFramebufferJS*, bool canDeleteOpaque = false);
+ void DeleteProgram(WebGLProgramJS*) const;
+ void DeleteQuery(WebGLQueryJS*);
+ void DeleteRenderbuffer(WebGLRenderbufferJS*);
+ void DeleteSampler(WebGLSamplerJS*);
+ void DeleteShader(WebGLShaderJS*) const;
+ void DeleteSync(WebGLSyncJS*) const;
+ void DeleteTexture(WebGLTextureJS*);
+ void DeleteTransformFeedback(WebGLTransformFeedbackJS*);
+ void DeleteVertexArray(WebGLVertexArrayJS*);
+
+ private:
+ void DoDeleteProgram(WebGLProgramJS&) const;
+ void DoDeleteShader(const WebGLShaderJS&) const;
+
+ public:
+ // -
+
+ bool IsBuffer(const WebGLBufferJS*) const;
+ bool IsFramebuffer(const WebGLFramebufferJS*) const;
+ bool IsProgram(const WebGLProgramJS*) const;
+ bool IsQuery(const WebGLQueryJS*) const;
+ bool IsRenderbuffer(const WebGLRenderbufferJS*) const;
+ bool IsSampler(const WebGLSamplerJS*) const;
+ bool IsShader(const WebGLShaderJS*) const;
+ bool IsSync(const WebGLSyncJS*) const;
+ bool IsTexture(const WebGLTextureJS*) const;
+ bool IsTransformFeedback(const WebGLTransformFeedbackJS*) const;
+ bool IsVertexArray(const WebGLVertexArrayJS*) const;
+
+ // -
+ // WebGLProgramJS
+
+ private:
+ const webgl::LinkResult& GetLinkResult(const WebGLProgramJS&) const;
+
+ public:
+ void AttachShader(WebGLProgramJS&, WebGLShaderJS&) const;
+ void BindAttribLocation(WebGLProgramJS&, GLuint location,
+ const nsAString& name) const;
+ void DetachShader(WebGLProgramJS&, const WebGLShaderJS&) const;
+ void GetAttachedShaders(
+ const WebGLProgramJS&,
+ dom::Nullable<nsTArray<RefPtr<WebGLShaderJS>>>& retval) const;
+ void LinkProgram(WebGLProgramJS&) const;
+ void TransformFeedbackVaryings(WebGLProgramJS&,
+ const dom::Sequence<nsString>& varyings,
+ GLenum bufferMode) const;
+ void UniformBlockBinding(WebGLProgramJS&, GLuint blockIndex,
+ GLuint blockBinding) const;
+
+ // Link result reflection
+ already_AddRefed<WebGLActiveInfoJS> GetActiveAttrib(const WebGLProgramJS&,
+ GLuint index);
+ already_AddRefed<WebGLActiveInfoJS> GetActiveUniform(const WebGLProgramJS&,
+ GLuint index);
+ void GetActiveUniformBlockName(const WebGLProgramJS&,
+ GLuint uniformBlockIndex,
+ nsAString& retval) const;
+ void GetActiveUniformBlockParameter(JSContext* cx, const WebGLProgramJS&,
+ GLuint uniformBlockIndex, GLenum pname,
+ JS::MutableHandle<JS::Value> retval,
+ ErrorResult& rv);
+ void GetActiveUniforms(JSContext*, const WebGLProgramJS&,
+ const dom::Sequence<GLuint>& uniformIndices,
+ GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+ GLint GetAttribLocation(const WebGLProgramJS&, const nsAString& name) const;
+ GLint GetFragDataLocation(const WebGLProgramJS&, const nsAString& name) const;
+ void GetProgramInfoLog(const WebGLProgramJS& prog, nsAString& retval) const;
+ void GetProgramParameter(JSContext*, const WebGLProgramJS&, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+ already_AddRefed<WebGLActiveInfoJS> GetTransformFeedbackVarying(
+ const WebGLProgramJS&, GLuint index);
+ GLuint GetUniformBlockIndex(const WebGLProgramJS&,
+ const nsAString& uniformBlockName) const;
+ void GetUniformIndices(const WebGLProgramJS&,
+ const dom::Sequence<nsString>& uniformNames,
+ dom::Nullable<nsTArray<GLuint>>& retval) const;
+
+ // WebGLUniformLocationJS
+ already_AddRefed<WebGLUniformLocationJS> GetUniformLocation(
+ const WebGLProgramJS&, const nsAString& name) const;
+ void GetUniform(JSContext*, const WebGLProgramJS&,
+ const WebGLUniformLocationJS&,
+ JS::MutableHandle<JS::Value> retval);
+
+ // -
+ // WebGLShaderJS
+
+ private:
+ const webgl::CompileResult& GetCompileResult(const WebGLShaderJS&) const;
+
+ public:
+ void CompileShader(WebGLShaderJS&) const;
+ void GetShaderInfoLog(const WebGLShaderJS&, nsAString& retval) const;
+ void GetShaderParameter(JSContext*, const WebGLShaderJS&, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+ void GetShaderSource(const WebGLShaderJS&, nsAString& retval) const;
+ void GetTranslatedShaderSource(const WebGLShaderJS& shader,
+ nsAString& retval) const;
+ void ShaderSource(WebGLShaderJS&, const nsAString&) const;
+
+ // -
+
+ void BindFramebuffer(GLenum target, WebGLFramebufferJS*);
+
+ void BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
+
+ // -
+
+ void BlendEquation(GLenum mode) { BlendEquationSeparate(mode, mode); }
+ void BlendFunc(GLenum sfactor, GLenum dfactor) {
+ BlendFuncSeparate(sfactor, dfactor, sfactor, dfactor);
+ }
+
+ void BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {
+ BlendEquationSeparateI({}, modeRGB, modeAlpha);
+ }
+ void BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha,
+ GLenum dstAlpha) {
+ BlendFuncSeparateI({}, srcRGB, dstRGB, srcAlpha, dstAlpha);
+ }
+
+ void BlendEquationSeparateI(Maybe<GLuint> buf, GLenum modeRGB,
+ GLenum modeAlpha);
+ void BlendFuncSeparateI(Maybe<GLuint> buf, GLenum srcRGB, GLenum dstRGB,
+ GLenum srcAlpha, GLenum dstAlpha);
+
+ // -
+
+ GLenum CheckFramebufferStatus(GLenum target);
+
+ void Clear(GLbitfield mask);
+
+ // -
+
+ private:
+ void ClearBufferTv(const GLenum buffer, const GLint drawBuffer,
+ const webgl::AttribBaseType type,
+ JS::AutoCheckCannotGC&& nogc,
+ const Span<const uint8_t>& view,
+ const GLuint srcElemOffset);
+
+ public:
+ void ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32ListU& list,
+ GLuint srcElemOffset) {
+ const FuncScope funcScope(*this, "clearBufferfv");
+ if (!Convert(list, [&](const Span<const float>& aData,
+ JS::AutoCheckCannotGC&& nogc) {
+ ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Float,
+ std::move(nogc), AsBytes(aData), srcElemOffset);
+ return true;
+ })) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
+ }
+ }
+ void ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32ListU& list,
+ GLuint srcElemOffset) {
+ const FuncScope funcScope(*this, "clearBufferiv");
+ if (!Convert(list, [&](const Span<const int32_t>& aData,
+ JS::AutoCheckCannotGC&& nogc) {
+ ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Int,
+ std::move(nogc), AsBytes(aData), srcElemOffset);
+ return true;
+ })) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
+ }
+ }
+ void ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32ListU& list,
+ GLuint srcElemOffset) {
+ const FuncScope funcScope(*this, "clearBufferuiv");
+ if (!Convert(list, [&](const Span<const uint32_t>& aData,
+ JS::AutoCheckCannotGC&& nogc) {
+ ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Uint,
+ std::move(nogc), AsBytes(aData), srcElemOffset);
+ return true;
+ })) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
+ }
+ }
+
+ // -
+
+ void ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth,
+ GLint stencil);
+
+ void ClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
+
+ void ClearDepth(GLclampf v);
+
+ void ClearStencil(GLint v);
+
+ void ColorMask(bool r, bool g, bool b, bool a) const {
+ ColorMaskI({}, r, g, b, a);
+ }
+ void ColorMaskI(Maybe<GLuint> buf, bool r, bool g, bool b, bool a) const;
+
+ void CullFace(GLenum face);
+
+ void DepthFunc(GLenum func);
+
+ void DepthMask(WebGLboolean b);
+
+ void DepthRange(GLclampf zNear, GLclampf zFar);
+
+ void Flush(bool flushGl = true);
+
+ void Finish();
+
+ void FrontFace(GLenum mode);
+
+ GLenum GetError();
+
+ void Hint(GLenum target, GLenum mode);
+
+ void LineWidth(GLfloat width);
+
+ void PixelStorei(GLenum pname, GLint param);
+
+ void PolygonOffset(GLfloat factor, GLfloat units);
+
+ void SampleCoverage(GLclampf value, WebGLboolean invert);
+
+ void Scissor(GLint x, GLint y, GLsizei width, GLsizei height);
+
+ // -
+
+ void StencilFunc(GLenum func, GLint ref, GLuint mask) {
+ StencilFuncSeparate(LOCAL_GL_FRONT_AND_BACK, func, ref, mask);
+ }
+ void StencilMask(GLuint mask) {
+ StencilMaskSeparate(LOCAL_GL_FRONT_AND_BACK, mask);
+ }
+ void StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass) {
+ StencilOpSeparate(LOCAL_GL_FRONT_AND_BACK, sfail, dpfail, dppass);
+ }
+
+ void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
+ void StencilMaskSeparate(GLenum face, GLuint mask);
+ void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail,
+ GLenum dppass);
+
+ // -
+
+ void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
+
+ // ------------------------- Buffer Objects -------------------------
+ public:
+ void BindBuffer(GLenum target, WebGLBufferJS*);
+
+ // -
+
+ private:
+ void BindBufferRangeImpl(const GLenum target, const GLuint index,
+ WebGLBufferJS* const buffer, const uint64_t offset,
+ const uint64_t size);
+
+ public:
+ void BindBufferBase(const GLenum target, const GLuint index,
+ WebGLBufferJS* const buffer) {
+ const FuncScope funcScope(*this, "bindBufferBase");
+ if (IsContextLost()) return;
+
+ BindBufferRangeImpl(target, index, buffer, 0, 0);
+ }
+
+ void BindBufferRange(const GLenum target, const GLuint index,
+ WebGLBufferJS* const buffer, const WebGLintptr offset,
+ const WebGLsizeiptr size) {
+ const FuncScope funcScope(*this, "bindBufferRange");
+ if (IsContextLost()) return;
+
+ if (buffer) {
+ if (!ValidateNonNegative("offset", offset)) return;
+
+ if (size < 1) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE,
+ "`size` must be positive for non-null `buffer`.");
+ return;
+ }
+ }
+
+ BindBufferRangeImpl(target, index, buffer, static_cast<uint64_t>(offset),
+ static_cast<uint64_t>(size));
+ }
+
+ // -
+
+ void CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
+ GLintptr readOffset, GLintptr writeOffset,
+ GLsizeiptr size);
+
+ void BufferData(GLenum target, WebGLsizeiptr size, GLenum usage);
+ void BufferData(GLenum target,
+ const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
+ GLenum usage);
+ void BufferData(GLenum target, const dom::ArrayBufferView& srcData,
+ GLenum usage, GLuint srcElemOffset = 0,
+ GLuint srcElemCountOverride = 0);
+
+ void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
+ const dom::ArrayBufferView& src, GLuint srcElemOffset = 0,
+ GLuint srcElemCountOverride = 0);
+ void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
+ const dom::ArrayBuffer& src);
+
+ void GetBufferSubData(GLenum target, GLintptr srcByteOffset,
+ const dom::ArrayBufferView& dstData,
+ GLuint dstElemOffset, GLuint dstElemCountOverride);
+
+ // -------------------------- Framebuffer Objects --------------------------
+
+ void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+ GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter);
+
+ // -
+
+ private:
+ // `bindTarget` if non-zero allows initializing the rb/tex with that target.
+ void FramebufferAttach(GLenum target, GLenum attachEnum, GLenum bindTarget,
+ WebGLRenderbufferJS*, WebGLTextureJS*,
+ uint32_t mipLevel, uint32_t zLayer,
+ uint32_t numViewLayers) const;
+
+ public:
+ void FramebufferRenderbuffer(GLenum target, GLenum attachSlot,
+ GLenum rbTarget, WebGLRenderbufferJS* rb) const {
+ const FuncScope funcScope(*this, "framebufferRenderbuffer");
+ if (IsContextLost()) return;
+ if (rbTarget != LOCAL_GL_RENDERBUFFER) {
+ EnqueueError_ArgEnum("rbTarget", rbTarget);
+ return;
+ }
+ FramebufferAttach(target, attachSlot, rbTarget, rb, nullptr, 0, 0, 0);
+ }
+
+ void FramebufferTexture2D(GLenum target, GLenum attachSlot,
+ GLenum texImageTarget, WebGLTextureJS*,
+ GLint mipLevel) const;
+
+ void FramebufferTextureLayer(GLenum target, GLenum attachSlot,
+ WebGLTextureJS* tex, GLint mipLevel,
+ GLint zLayer) const {
+ const FuncScope funcScope(*this, "framebufferTextureLayer");
+ if (IsContextLost()) return;
+ FramebufferAttach(target, attachSlot, 0, nullptr, tex,
+ static_cast<uint32_t>(mipLevel),
+ static_cast<uint32_t>(zLayer), 0);
+ }
+
+ void FramebufferTextureMultiview(GLenum target, GLenum attachSlot,
+ WebGLTextureJS* tex, GLint mipLevel,
+ GLint zLayerBase,
+ GLsizei numViewLayers) const {
+ const FuncScope funcScope(*this, "framebufferTextureMultiview");
+ if (IsContextLost()) return;
+ if (tex && numViewLayers < 1) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "`numViewLayers` must be >=1.");
+ return;
+ }
+ FramebufferAttach(target, attachSlot, 0, nullptr, tex,
+ static_cast<uint32_t>(mipLevel),
+ static_cast<uint32_t>(zLayerBase),
+ static_cast<uint32_t>(numViewLayers));
+ }
+
+ // -
+
+ void InvalidateFramebuffer(GLenum target,
+ const dom::Sequence<GLenum>& attachments,
+ ErrorResult& unused);
+ void InvalidateSubFramebuffer(GLenum target,
+ const dom::Sequence<GLenum>& attachments,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ ErrorResult& unused);
+
+ void ReadBuffer(GLenum mode);
+
+ // ----------------------- Renderbuffer objects -----------------------
+ void GetInternalformatParameter(JSContext* cx, GLenum target,
+ GLenum internalformat, GLenum pname,
+ JS::MutableHandle<JS::Value> retval,
+ ErrorResult& rv);
+
+ void BindRenderbuffer(GLenum target, WebGLRenderbufferJS*);
+
+ void RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width,
+ GLsizei height) const {
+ RenderbufferStorageMultisample(target, 0, internalFormat, width, height);
+ }
+
+ void RenderbufferStorageMultisample(GLenum target, GLsizei samples,
+ GLenum internalFormat, GLsizei width,
+ GLsizei height) const;
+
+ // --------------------------- Texture objects ---------------------------
+
+ void ActiveTexture(GLenum texUnit);
+
+ void BindTexture(GLenum texTarget, WebGLTextureJS*);
+
+ void GenerateMipmap(GLenum texTarget) const;
+
+ void GetTexParameter(JSContext* cx, GLenum texTarget, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+
+ void TexParameterf(GLenum texTarget, GLenum pname, GLfloat param);
+ void TexParameteri(GLenum texTarget, GLenum pname, GLint param);
+
+ // -
+
+ private:
+ void TexStorage(uint8_t funcDims, GLenum target, GLsizei levels,
+ GLenum internalFormat, const ivec3& size) const;
+
+ // Primitive tex upload functions
+ void TexImage(uint8_t funcDims, GLenum target, GLint level,
+ GLenum respecFormat, const ivec3& offset,
+ const Maybe<ivec3>& size, GLint border,
+ const webgl::PackingInfo& pi, const TexImageSource& src) const;
+ void CompressedTexImage(bool sub, uint8_t funcDims, GLenum target,
+ GLint level, GLenum format, const ivec3& offset,
+ const ivec3& size, GLint border,
+ const TexImageSource& src,
+ GLsizei pboImageSize) const;
+ void CopyTexImage(uint8_t funcDims, GLenum target, GLint level,
+ GLenum respecFormat, const ivec3& dstOffset,
+ const ivec2& srcOffset, const ivec2& size,
+ GLint border) const;
+
+ public:
+ void TexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat,
+ GLsizei width, GLsizei height) const {
+ TexStorage(2, target, levels, internalFormat, {width, height, 1});
+ }
+
+ void TexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLsizei depth) const {
+ TexStorage(3, target, levels, internalFormat, {width, height, depth});
+ }
+
+ ////////////////////////////////////
+
+ template <typename T> // TexImageSource or WebGLintptr
+ void TexImage2D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum unpackFormat, GLenum unpackType, const T& anySrc,
+ ErrorResult& out_error) const {
+ const TexImageSourceAdapter src(&anySrc, &out_error);
+ TexImage(2, target, level, internalFormat, {0, 0, 0},
+ Some(ivec3{width, height, 1}), border, {unpackFormat, unpackType},
+ src);
+ }
+
+ void TexImage2D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum unpackFormat, GLenum unpackType,
+ const dom::ArrayBufferView& view, GLuint viewElemOffset,
+ ErrorResult&) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset);
+ TexImage(2, target, level, internalFormat, {0, 0, 0},
+ Some(ivec3{width, height, 1}), border, {unpackFormat, unpackType},
+ src);
+ }
+
+ // -
+
+ template <typename T> // TexImageSource or WebGLintptr
+ void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
+ GLsizei width, GLsizei height, GLenum unpackFormat,
+ GLenum unpackType, const T& anySrc,
+ ErrorResult& out_error) const {
+ const TexImageSourceAdapter src(&anySrc, &out_error);
+ TexImage(2, target, level, 0, {xOffset, yOffset, 0},
+ Some(ivec3{width, height, 1}), 0, {unpackFormat, unpackType}, src);
+ }
+
+ void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
+ GLsizei width, GLsizei height, GLenum unpackFormat,
+ GLenum unpackType, const dom::ArrayBufferView& view,
+ GLuint viewElemOffset, ErrorResult&) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset);
+ TexImage(2, target, level, 0, {xOffset, yOffset, 0},
+ Some(ivec3{width, height, 1}), 0, {unpackFormat, unpackType}, src);
+ }
+
+ // -
+
+ template <typename T> // TexImageSource or WebGLintptr
+ void TexImage3D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLsizei depth, GLint border,
+ GLenum unpackFormat, GLenum unpackType, const T& anySrc,
+ ErrorResult& out_error) const {
+ const TexImageSourceAdapter src(&anySrc, &out_error);
+ TexImage(3, target, level, internalFormat, {0, 0, 0},
+ Some(ivec3{width, height, depth}), border,
+ {unpackFormat, unpackType}, src);
+ }
+
+ void TexImage3D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLsizei depth, GLint border,
+ GLenum unpackFormat, GLenum unpackType,
+ const dom::ArrayBufferView& view, GLuint viewElemOffset,
+ ErrorResult&) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset);
+ TexImage(3, target, level, internalFormat, {0, 0, 0},
+ Some(ivec3{width, height, depth}), border,
+ {unpackFormat, unpackType}, src);
+ }
+
+ // -
+
+ template <typename T> // TexImageSource or WebGLintptr
+ void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
+ GLint zOffset, GLsizei width, GLsizei height,
+ GLsizei depth, GLenum unpackFormat, GLenum unpackType,
+ const T& anySrc, ErrorResult& out_error) const {
+ const TexImageSourceAdapter src(&anySrc, &out_error);
+ TexImage(3, target, level, 0, {xOffset, yOffset, zOffset},
+ Some(ivec3{width, height, depth}), 0, {unpackFormat, unpackType},
+ src);
+ }
+
+ void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
+ GLint zOffset, GLsizei width, GLsizei height,
+ GLsizei depth, GLenum unpackFormat, GLenum unpackType,
+ const dom::Nullable<dom::ArrayBufferView>& maybeSrcView,
+ GLuint srcElemOffset, ErrorResult&) const {
+ const TexImageSourceAdapter src(&maybeSrcView, srcElemOffset);
+ TexImage(3, target, level, 0, {xOffset, yOffset, zOffset},
+ Some(ivec3{width, height, depth}), 0, {unpackFormat, unpackType},
+ src);
+ }
+
+ ////////////////////////////////////
+
+ public:
+ void CompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLint border,
+ GLsizei imageSize, WebGLintptr offset) const {
+ const TexImageSourceAdapter src(&offset);
+ CompressedTexImage(false, 2, target, level, internalFormat, {0, 0, 0},
+ {width, height, 1}, border, src, imageSize);
+ }
+
+ void CompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLint border,
+ const dom::ArrayBufferView& view,
+ GLuint viewElemOffset = 0,
+ GLuint viewElemLengthOverride = 0) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset,
+ viewElemLengthOverride);
+ CompressedTexImage(false, 2, target, level, internalFormat, {0, 0, 0},
+ {width, height, 1}, border, src, 0);
+ }
+
+ // -
+
+ void CompressedTexSubImage2D(GLenum target, GLint level, GLint xOffset,
+ GLint yOffset, GLsizei width, GLsizei height,
+ GLenum unpackFormat, GLsizei imageSize,
+ WebGLintptr offset) const {
+ const TexImageSourceAdapter src(&offset);
+ CompressedTexImage(true, 2, target, level, unpackFormat,
+ {xOffset, yOffset, 0}, {width, height, 1}, 0, src,
+ imageSize);
+ }
+
+ void CompressedTexSubImage2D(GLenum target, GLint level, GLint xOffset,
+ GLint yOffset, GLsizei width, GLsizei height,
+ GLenum unpackFormat,
+ const dom::ArrayBufferView& view,
+ GLuint viewElemOffset = 0,
+ GLuint viewElemLengthOverride = 0) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset,
+ viewElemLengthOverride);
+ CompressedTexImage(true, 2, target, level, unpackFormat,
+ {xOffset, yOffset, 0}, {width, height, 1}, 0, src, 0);
+ }
+
+ // -
+
+ void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLint border, GLsizei imageSize,
+ WebGLintptr offset) const {
+ const TexImageSourceAdapter src(&offset);
+ CompressedTexImage(false, 3, target, level, internalFormat, {0, 0, 0},
+ {width, height, depth}, border, src, imageSize);
+ }
+
+ void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLint border, const dom::ArrayBufferView& view,
+ GLuint viewElemOffset = 0,
+ GLuint viewElemLengthOverride = 0) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset,
+ viewElemLengthOverride);
+ CompressedTexImage(false, 3, target, level, internalFormat, {0, 0, 0},
+ {width, height, depth}, border, src, 0);
+ }
+
+ // -
+
+ void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset,
+ GLint yOffset, GLint zOffset, GLsizei width,
+ GLsizei height, GLsizei depth,
+ GLenum unpackFormat, GLsizei imageSize,
+ WebGLintptr offset) const {
+ const TexImageSourceAdapter src(&offset);
+ CompressedTexImage(true, 3, target, level, unpackFormat,
+ {xOffset, yOffset, zOffset}, {width, height, depth}, 0,
+ src, imageSize);
+ }
+
+ void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset,
+ GLint yOffset, GLint zOffset, GLsizei width,
+ GLsizei height, GLsizei depth,
+ GLenum unpackFormat,
+ const dom::ArrayBufferView& view,
+ GLuint viewElemOffset = 0,
+ GLuint viewElemLengthOverride = 0) const {
+ const TexImageSourceAdapter src(&view, viewElemOffset,
+ viewElemLengthOverride);
+ CompressedTexImage(true, 3, target, level, unpackFormat,
+ {xOffset, yOffset, zOffset}, {width, height, depth}, 0,
+ src, 0);
+ }
+
+ // --------------------
+
+ void CopyTexImage2D(GLenum target, GLint level, GLenum internalFormat,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ GLint border) const {
+ CopyTexImage(2, target, level, internalFormat, {0, 0, 0}, {x, y},
+ {width, height}, border);
+ }
+
+ void CopyTexSubImage2D(GLenum target, GLint level, GLint xOffset,
+ GLint yOffset, GLint x, GLint y, GLsizei width,
+ GLsizei height) const {
+ CopyTexImage(2, target, level, 0, {xOffset, yOffset, 0}, {x, y},
+ {width, height}, 0);
+ }
+
+ void CopyTexSubImage3D(GLenum target, GLint level, GLint xOffset,
+ GLint yOffset, GLint zOffset, GLint x, GLint y,
+ GLsizei width, GLsizei height) const {
+ CopyTexImage(3, target, level, 0, {xOffset, yOffset, zOffset}, {x, y},
+ {width, height}, 0);
+ }
+
+ // -------------------
+ // legacy TexImageSource uploads without width/height.
+ // The width/height params are webgl2 only, and let you do subrect
+ // selection with e.g. width < UNPACK_ROW_LENGTH.
+
+ template <typename TexImageSourceT>
+ void TexImage2D(GLenum target, GLint level, GLenum internalFormat,
+ GLenum unpackFormat, GLenum unpackType,
+ const TexImageSourceT& anySrc, ErrorResult& out_error) const {
+ const TexImageSourceAdapter src(&anySrc, &out_error);
+ TexImage(2, target, level, internalFormat, {}, {}, 0,
+ {unpackFormat, unpackType}, src);
+ }
+
+ template <typename TexImageSourceT>
+ void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
+ GLenum unpackFormat, GLenum unpackType,
+ const TexImageSourceT& anySrc,
+ ErrorResult& out_error) const {
+ const TexImageSourceAdapter src(&anySrc, &out_error);
+ TexImage(2, target, level, 0, {xOffset, yOffset, 0}, {}, 0,
+ {unpackFormat, unpackType}, src);
+ }
+
+ // ------------------------ Uniforms and attributes ------------------------
+
+ private:
+ Maybe<double> GetVertexAttribPriv(GLuint index, GLenum pname);
+
+ public:
+ void GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
+ JS::MutableHandle<JS::Value> retval, ErrorResult& rv);
+
+ private:
+ const webgl::LinkResult* GetActiveLinkResult() const {
+ const auto& state = State();
+ if (state.mCurrentProgram) {
+ (void)GetLinkResult(*state.mCurrentProgram);
+ }
+ return state.mActiveLinkResult.get();
+ }
+
+ // Used by callers that have a `nogc` token that ensures that no GC will
+ // happen while `bytes` is alive. Internally, the nogc range will be ended
+ // after `bytes` is used (either successfully but where the data are possibly
+ // sent over IPC which has a tendency to GC, or unsuccesfully in which case
+ // error handling can GC.)
+ void UniformData(GLenum funcElemType, const WebGLUniformLocationJS* const loc,
+ bool transpose, const Range<const uint8_t>& bytes,
+ JS::AutoCheckCannotGC&& nogc, GLuint elemOffset = 0,
+ GLuint elemCountOverride = 0) const;
+
+ // Used by callers that are not passing `bytes` that might be GC-controlled.
+ // This will create an artificial and unnecessary nogc region that should
+ // get optimized away to nothing.
+ void UniformData(GLenum funcElemType, const WebGLUniformLocationJS* const loc,
+ bool transpose, const Range<const uint8_t>& bytes,
+ GLuint elemOffset = 0, GLuint elemCountOverride = 0) const {
+ JS::AutoCheckCannotGC nogc;
+ UniformData(funcElemType, loc, transpose, bytes, std::move(nogc),
+ elemOffset, elemCountOverride);
+ }
+
+ // -
+
+ template <typename T>
+ Maybe<Range<T>> ValidateSubrange(const Range<T>& data, size_t elemOffset,
+ size_t elemLengthOverride = 0) const {
+ auto ret = data;
+ if (elemOffset > ret.length()) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE,
+ "`elemOffset` too large for `data`.");
+ return {};
+ }
+ ret = {ret.begin() + elemOffset, ret.end()};
+ if (elemLengthOverride) {
+ if (elemLengthOverride > ret.length()) {
+ EnqueueError(
+ LOCAL_GL_INVALID_VALUE,
+ "`elemLengthOverride` too large for `data` and `elemOffset`.");
+ return {};
+ }
+ ret = {ret.begin().get(), elemLengthOverride};
+ }
+ return Some(ret);
+ }
+
+ public:
+#define _(T, type_t, TYPE) \
+ void Uniform1##T(const WebGLUniformLocationJS* const loc, type_t x) const { \
+ const type_t arr[] = {x}; \
+ UniformData(TYPE, loc, false, MakeByteRange(arr)); \
+ } \
+ void Uniform2##T(const WebGLUniformLocationJS* const loc, type_t x, \
+ type_t y) const { \
+ const type_t arr[] = {x, y}; \
+ UniformData(TYPE##_VEC2, loc, false, MakeByteRange(arr)); \
+ } \
+ void Uniform3##T(const WebGLUniformLocationJS* const loc, type_t x, \
+ type_t y, type_t z) const { \
+ const type_t arr[] = {x, y, z}; \
+ UniformData(TYPE##_VEC3, loc, false, MakeByteRange(arr)); \
+ } \
+ void Uniform4##T(const WebGLUniformLocationJS* const loc, type_t x, \
+ type_t y, type_t z, type_t w) const { \
+ const type_t arr[] = {x, y, z, w}; \
+ UniformData(TYPE##_VEC4, loc, false, MakeByteRange(arr)); \
+ }
+
+ _(f, float, LOCAL_GL_FLOAT)
+ _(i, int32_t, LOCAL_GL_INT)
+ _(ui, uint32_t, LOCAL_GL_UNSIGNED_INT)
+
+#undef _
+
+ // -
+
+#define _(NT, TypeListU, TYPE) \
+ void Uniform##NT##v(const WebGLUniformLocationJS* const loc, \
+ const TypeListU& list, GLuint elemOffset = 0, \
+ GLuint elemCountOverride = 0) const { \
+ Convert(list, [&](const auto& aData, JS::AutoCheckCannotGC&& nogc) { \
+ UniformData(TYPE, loc, false, MakeByteRange(aData), std::move(nogc), \
+ elemOffset, elemCountOverride); \
+ return true; \
+ }); \
+ }
+
+ _(1f, Float32ListU, LOCAL_GL_FLOAT)
+ _(2f, Float32ListU, LOCAL_GL_FLOAT_VEC2)
+ _(3f, Float32ListU, LOCAL_GL_FLOAT_VEC3)
+ _(4f, Float32ListU, LOCAL_GL_FLOAT_VEC4)
+ _(1i, Int32ListU, LOCAL_GL_INT)
+ _(2i, Int32ListU, LOCAL_GL_INT_VEC2)
+ _(3i, Int32ListU, LOCAL_GL_INT_VEC3)
+ _(4i, Int32ListU, LOCAL_GL_INT_VEC4)
+ _(1ui, Uint32ListU, LOCAL_GL_UNSIGNED_INT)
+ _(2ui, Uint32ListU, LOCAL_GL_UNSIGNED_INT_VEC2)
+ _(3ui, Uint32ListU, LOCAL_GL_UNSIGNED_INT_VEC3)
+ _(4ui, Uint32ListU, LOCAL_GL_UNSIGNED_INT_VEC4)
+
+#undef _
+
+ // -
+
+#define _(X) \
+ void UniformMatrix##X##fv(const WebGLUniformLocationJS* loc, bool transpose, \
+ const Float32ListU& list, GLuint elemOffset = 0, \
+ GLuint elemCountOverride = 0) const { \
+ Convert(list, [&](const Span<const float>& aData, \
+ JS::AutoCheckCannotGC&& nogc) { \
+ UniformData(LOCAL_GL_FLOAT_MAT##X, loc, transpose, MakeByteRange(aData), \
+ std::move(nogc), elemOffset, elemCountOverride); \
+ return true; \
+ }); \
+ }
+
+ _(2)
+ _(2x3)
+ _(2x4)
+
+ _(3x2)
+ _(3)
+ _(3x4)
+
+ _(4x2)
+ _(4x3)
+ _(4)
+
+#undef _
+
+ // -
+
+ void EnableVertexAttribArray(GLuint index);
+
+ void DisableVertexAttribArray(GLuint index);
+
+ WebGLsizeiptr GetVertexAttribOffset(GLuint index, GLenum pname);
+
+ // -
+
+ private:
+ void VertexAttrib4Tv(GLuint index, webgl::AttribBaseType,
+ const Range<const uint8_t>&);
+
+ public:
+ void VertexAttrib1f(GLuint index, GLfloat x) {
+ VertexAttrib4f(index, x, 0, 0, 1);
+ }
+ void VertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {
+ VertexAttrib4f(index, x, y, 0, 1);
+ }
+ void VertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {
+ VertexAttrib4f(index, x, y, z, 1);
+ }
+
+ void VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z,
+ GLfloat w) {
+ const float arr[4] = {x, y, z, w};
+ VertexAttrib4Tv(index, webgl::AttribBaseType::Float, MakeByteRange(arr));
+ }
+
+ // -
+
+ template <typename List, typename T, size_t N>
+ bool MakeArrayFromList(const List& list, T (&array)[N]) {
+ bool badLength = false;
+ if (!Convert(list, [&](const auto& aData, JS::AutoCheckCannotGC&&) {
+ static_assert(
+ std::is_same_v<std::remove_const_t<
+ std::remove_reference_t<decltype(aData[0])>>,
+ T>);
+ if (N > aData.Length()) {
+ badLength = true;
+ return false;
+ }
+
+ std::copy_n(aData.begin(), N, array);
+ return true;
+ })) {
+ EnqueueError(
+ LOCAL_GL_INVALID_VALUE,
+ badLength
+ ? nsPrintfCString("Length of `list` must be >=%zu.", N).get()
+ : "Conversion of `list` failed.");
+ return false;
+ }
+ return true;
+ }
+
+ void VertexAttrib1fv(const GLuint index, const Float32ListU& list) {
+ const FuncScope funcScope(*this, "vertexAttrib1fv");
+ if (IsContextLost()) return;
+
+ float arr[1];
+ if (!MakeArrayFromList(list, arr)) {
+ return;
+ }
+ VertexAttrib1f(index, arr[0]);
+ }
+
+ void VertexAttrib2fv(const GLuint index, const Float32ListU& list) {
+ const FuncScope funcScope(*this, "vertexAttrib1fv");
+ if (IsContextLost()) return;
+
+ float arr[2];
+ if (!MakeArrayFromList(list, arr)) {
+ return;
+ }
+ VertexAttrib2f(index, arr[0], arr[1]);
+ }
+
+ void VertexAttrib3fv(const GLuint index, const Float32ListU& list) {
+ const FuncScope funcScope(*this, "vertexAttrib1fv");
+ if (IsContextLost()) return;
+
+ float arr[3];
+ if (!MakeArrayFromList(list, arr)) {
+ return;
+ }
+ VertexAttrib3f(index, arr[0], arr[1], arr[2]);
+ }
+
+ void VertexAttrib4fv(GLuint index, const Float32ListU& list) {
+ const FuncScope funcScope(*this, "vertexAttrib4fv");
+ float arr[4];
+ if (!MakeArrayFromList(list, arr)) {
+ return;
+ }
+ VertexAttrib4Tv(index, webgl::AttribBaseType::Float, MakeByteRange(arr));
+ }
+ void VertexAttribI4iv(GLuint index, const Int32ListU& list) {
+ const FuncScope funcScope(*this, "vertexAttribI4iv");
+ int32_t arr[4];
+ if (!MakeArrayFromList(list, arr)) {
+ return;
+ }
+ VertexAttrib4Tv(index, webgl::AttribBaseType::Int, MakeByteRange(arr));
+ }
+ void VertexAttribI4uiv(GLuint index, const Uint32ListU& list) {
+ const FuncScope funcScope(*this, "vertexAttribI4uiv");
+ uint32_t arr[4];
+ if (!MakeArrayFromList(list, arr)) {
+ return;
+ }
+ VertexAttrib4Tv(index, webgl::AttribBaseType::Uint, MakeByteRange(arr));
+ }
+
+ void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) {
+ const int32_t arr[4] = {x, y, z, w};
+ VertexAttrib4Tv(index, webgl::AttribBaseType::Int, MakeByteRange(arr));
+ }
+ void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) {
+ const uint32_t arr[4] = {x, y, z, w};
+ VertexAttrib4Tv(index, webgl::AttribBaseType::Uint, MakeByteRange(arr));
+ }
+
+ private:
+ void VertexAttribPointerImpl(bool isFuncInt, GLuint index, GLint size,
+ GLenum type, WebGLboolean normalized,
+ GLsizei iStride, WebGLintptr iByteOffset);
+
+ public:
+ void VertexAttribIPointer(GLuint index, GLint size, GLenum type,
+ GLsizei stride, WebGLintptr byteOffset) {
+ VertexAttribPointerImpl(true, index, size, type, false, stride, byteOffset);
+ }
+
+ void VertexAttribPointer(GLuint index, GLint size, GLenum type,
+ WebGLboolean normalized, GLsizei stride,
+ WebGLintptr byteOffset) {
+ VertexAttribPointerImpl(false, index, size, type, normalized, stride,
+ byteOffset);
+ }
+
+ // -------------------------------- Drawing -------------------------------
+ public:
+ void DrawArrays(GLenum mode, GLint first, GLsizei count) {
+ DrawArraysInstanced(mode, first, count, 1);
+ }
+
+ void DrawElements(GLenum mode, GLsizei count, GLenum type,
+ WebGLintptr byteOffset) {
+ DrawElementsInstanced(mode, count, type, byteOffset, 1);
+ }
+
+ void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
+ GLenum type, WebGLintptr byteOffset) {
+ const FuncScope funcScope(*this, "drawRangeElements");
+ if (end < start) {
+ EnqueueError(LOCAL_GL_INVALID_VALUE, "end must be >= start.");
+ return;
+ }
+ DrawElementsInstanced(mode, count, type, byteOffset, 1);
+ }
+
+ // ------------------------------ Readback -------------------------------
+ public:
+ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ const dom::Nullable<dom::ArrayBufferView>& maybeView,
+ dom::CallerType aCallerType, ErrorResult& out_error) const {
+ const FuncScope funcScope(*this, "readPixels");
+ if (!ValidateNonNull("pixels", maybeView)) return;
+ ReadPixels(x, y, width, height, format, type, maybeView.Value(), 0,
+ aCallerType, out_error);
+ }
+
+ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, WebGLsizeiptr offset,
+ dom::CallerType aCallerType, ErrorResult& out_error) const;
+
+ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ const dom::ArrayBufferView& dstData, GLuint dstElemOffset,
+ dom::CallerType aCallerType, ErrorResult& out_error) const;
+
+ protected:
+ bool ReadPixels_SharedPrecheck(dom::CallerType aCallerType,
+ ErrorResult& out_error) const;
+
+ // ------------------------------ Vertex Array ------------------------------
+ public:
+ void BindVertexArray(WebGLVertexArrayJS*);
+
+ void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
+ GLsizei primcount);
+
+ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
+ WebGLintptr offset, GLsizei primcount);
+
+ void VertexAttribDivisor(GLuint index, GLuint divisor);
+
+ // --------------------------------- GL Query
+ // ---------------------------------
+ public:
+ void GetQuery(JSContext*, GLenum target, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+ void GetQueryParameter(JSContext*, WebGLQueryJS&, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+ void BeginQuery(GLenum target, WebGLQueryJS&);
+ void EndQuery(GLenum target);
+ void QueryCounter(WebGLQueryJS&, GLenum target) const;
+
+ // -------------------------------- Sampler -------------------------------
+
+ void GetSamplerParameter(JSContext*, const WebGLSamplerJS&, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+
+ void BindSampler(GLuint unit, WebGLSamplerJS*);
+ void SamplerParameteri(WebGLSamplerJS&, GLenum pname, GLint param) const;
+ void SamplerParameterf(WebGLSamplerJS&, GLenum pname, GLfloat param) const;
+
+ // ------------------------------- GL Sync ---------------------------------
+
+ GLenum ClientWaitSync(WebGLSyncJS&, GLbitfield flags, GLuint64 timeout) const;
+ void GetSyncParameter(JSContext*, WebGLSyncJS&, GLenum pname,
+ JS::MutableHandle<JS::Value> retval) const;
+ void WaitSync(const WebGLSyncJS&, GLbitfield flags, GLint64 timeout) const;
+
+ // -------------------------- Transform Feedback ---------------------------
+
+ void BindTransformFeedback(GLenum target, WebGLTransformFeedbackJS*);
+ void BeginTransformFeedback(GLenum primitiveMode);
+ void EndTransformFeedback();
+ void PauseTransformFeedback();
+ void ResumeTransformFeedback();
+
+ // -------------------------- Opaque Framebuffers ---------------------------
+
+ void SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS*, bool);
+
+ // ------------------------------ Extensions ------------------------------
+ public:
+ void GetSupportedExtensions(dom::Nullable<nsTArray<nsString>>& retval,
+ dom::CallerType callerType) const;
+
+ bool IsSupported(WebGLExtensionID, dom::CallerType callerType =
+ dom::CallerType::NonSystem) const;
+
+ void GetExtension(JSContext* cx, const nsAString& name,
+ JS::MutableHandle<JSObject*> retval,
+ dom::CallerType callerType, ErrorResult& rv);
+
+ protected:
+ bool IsExtensionForbiddenForCaller(const WebGLExtensionID ext,
+ const dom::CallerType callerType) const;
+
+ RefPtr<ClientWebGLExtensionBase> GetExtension(WebGLExtensionID ext,
+ dom::CallerType callerType);
+ void RequestExtension(WebGLExtensionID) const;
+
+ public:
+ bool IsExtensionEnabled(const WebGLExtensionID id) const {
+ return bool(mNotLost->extensions[UnderlyingValue(id)]);
+ }
+
+ void AddCompressedFormat(GLenum);
+
+ // ---------------------------- Misc Extensions ----------------------------
+ public:
+ void DrawBuffers(const dom::Sequence<GLenum>& buffers);
+
+ void GetSupportedProfilesASTC(
+ dom::Nullable<nsTArray<nsString>>& retval) const;
+
+ void MOZDebugGetParameter(JSContext* cx, GLenum pname,
+ JS::MutableHandle<JS::Value> retval,
+ ErrorResult& rv) {
+ GetParameter(cx, pname, retval, rv, true);
+ }
+
+ void ProvokingVertex(GLenum rawMode) const;
+
+ // -------------------------------------------------------------------------
+ // Client-side methods. Calls in the Host are forwarded to the client.
+ // -------------------------------------------------------------------------
+ public:
+ void JsWarning(const std::string&) const;
+
+ // -------------------------------------------------------------------------
+ // The cross-process communication mechanism
+ // -------------------------------------------------------------------------
+ protected:
+ // If we are running WebGL in this process then call the HostWebGLContext
+ // method directly. Otherwise, dispatch over IPC.
+ template <typename MethodType, MethodType method, typename... CallerArgs>
+ void Run(const CallerArgs&... args) const {
+ const auto id = IdByMethod<MethodType, method>();
+ auto noNoGc = std::optional<JS::AutoCheckCannotGC>{};
+ Run_WithDestArgTypes_ConstnessHelper(std::move(noNoGc), method, id,
+ args...);
+ }
+
+ // Same as above for use when using potentially GC-controlled data. The scope
+ // of `aNoGC` will be ended after the data is no longer needed.
+ template <typename MethodType, MethodType method, typename... CallerArgs>
+ void RunWithGCData(JS::AutoCheckCannotGC&& aNoGC,
+ const CallerArgs&... aArgs) const {
+ const auto id = IdByMethod<MethodType, method>();
+ auto noGc = std::optional<JS::AutoCheckCannotGC>{std::move(aNoGC)};
+ Run_WithDestArgTypes_ConstnessHelper(std::move(noGc), method, id, aArgs...);
+ }
+
+ // Because we're trying to explicitly pull `DestArgs` via `method`, we have
+ // one overload for mut-methods and one for const-methods.
+ template <typename... DestArgs>
+ void Run_WithDestArgTypes_ConstnessHelper(
+ std::optional<JS::AutoCheckCannotGC>&& noGc,
+ void (HostWebGLContext::*method)(DestArgs...), const size_t id,
+ const std::remove_reference_t<std::remove_const_t<DestArgs>>&... args)
+ const {
+ Run_WithDestArgTypes(std::move(noGc), method, id, args...);
+ }
+ template <typename... DestArgs>
+ void Run_WithDestArgTypes_ConstnessHelper(
+ std::optional<JS::AutoCheckCannotGC>&& noGc,
+ void (HostWebGLContext::*method)(DestArgs...) const, const size_t id,
+ const std::remove_reference_t<std::remove_const_t<DestArgs>>&... args)
+ const {
+ Run_WithDestArgTypes(std::move(noGc), method, id, args...);
+ }
+
+ template <typename MethodT, typename... DestArgs>
+ void Run_WithDestArgTypes(std::optional<JS::AutoCheckCannotGC>&&, MethodT,
+ const size_t id, const DestArgs&...) const;
+
+ // -------------------------------------------------------------------------
+ // Helpers for DOM operations, composition, actors, etc
+ // -------------------------------------------------------------------------
+
+ public:
+ // https://immersive-web.github.io/webxr/#xr-compatible
+ bool IsXRCompatible() const;
+ already_AddRefed<dom::Promise> MakeXRCompatible(ErrorResult& aRv);
+
+ protected:
+ uint32_t GetPrincipalHashValue() const;
+
+ // Prepare the context for capture before compositing
+ void BeginComposition();
+
+ // Clean up the context after captured for compositing
+ void EndComposition();
+
+ mozilla::dom::Document* GetOwnerDoc() const;
+
+ mutable bool mResetLayer = true;
+ Maybe<const WebGLContextOptions> mInitialOptions;
+ bool mXRCompatible = false;
+};
+
+// used by DOM bindings in conjunction with GetParentObject
+inline nsISupports* ToSupports(ClientWebGLContext* webgl) {
+ return static_cast<nsICanvasRenderingContextInternal*>(webgl);
+}
+
+const char* GetExtensionName(WebGLExtensionID);
+
+// -
+
+inline bool webgl::ObjectJS::IsForContext(
+ const ClientWebGLContext& context) const {
+ const auto& notLost = context.mNotLost;
+ if (!notLost) return false;
+ if (notLost.get() != mGeneration.lock().get()) return false;
+ return true;
+}
+
+void AutoJsWarning(const std::string& utf8);
+
+} // namespace mozilla
+
+#endif // CLIENTWEBGLCONTEXT_H_