/* -*- 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 WEBGLCONTEXT_H_ #define WEBGLCONTEXT_H_ #include #include #include #include "GLContextTypes.h" #include "GLDefs.h" #include "GLScreenBuffer.h" #include "js/ScalarType.h" // js::Scalar::Type #include "mozilla/Attributes.h" #include "mozilla/Atomics.h" #include "mozilla/CheckedInt.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/Nullable.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/gfx/2D.h" #include "mozilla/Mutex.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPrefs_webgl.h" #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "nsICanvasRenderingContextInternal.h" #include "nsTArray.h" #include "SurfaceTypes.h" #include "ScopedGLHelpers.h" #include "TexUnpackBlob.h" // Local #include "CacheInvalidator.h" #include "WebGLContextLossHandler.h" #include "WebGLExtensions.h" #include "WebGLObjectModel.h" #include "WebGLStrongTypes.h" #include "WebGLTypes.h" // Generated #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "mozilla/dom/WebGL2RenderingContextBinding.h" #include class nsIDocShell; // WebGL-only GLenums // clang-format off #define LOCAL_GL_BROWSER_DEFAULT_WEBGL 0x9244 #define LOCAL_GL_CONTEXT_LOST_WEBGL 0x9242 #define LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL 0x9247 #define LOCAL_GL_UNPACK_COLORSPACE_CONVERSION_WEBGL 0x9243 #define LOCAL_GL_UNPACK_FLIP_Y_WEBGL 0x9240 #define LOCAL_GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241 // clang-format on namespace mozilla { class HostWebGLContext; class ScopedCopyTexImageSource; class ScopedDrawCallWrapper; class ScopedResolveTexturesForDraw; class WebGLBuffer; class WebGLExtensionBase; class WebGLFramebuffer; class WebGLProgram; class WebGLQuery; class WebGLRenderbuffer; class WebGLSampler; class WebGLShader; class WebGLSync; class WebGLTexture; class WebGLTransformFeedback; class WebGLVertexArray; namespace dom { class Document; class Element; class ImageData; class OwningHTMLCanvasElementOrOffscreenCanvas; struct WebGLContextAttributes; } // namespace dom namespace gfx { class SourceSurface; class VRLayerChild; } // namespace gfx namespace gl { class GLScreenBuffer; class MozFramebuffer; class SharedSurface; class Texture; } // namespace gl namespace layers { class CompositableHost; class RemoteTextureOwnerClient; class SurfaceDescriptor; } // namespace layers namespace webgl { class AvailabilityRunnable; struct CachedDrawFetchLimits; struct FbAttachInfo; struct FormatInfo; class FormatUsageAuthority; struct FormatUsageInfo; struct ImageInfo; struct LinkedProgramInfo; struct SamplerUniformInfo; struct SamplingState; class ScopedPrepForResourceClear; class ShaderValidator; class TexUnpackBlob; struct UniformInfo; struct UniformBlockInfo; struct VertAttribPointerDesc; } // namespace webgl struct WebGLTexImageData { TexImageTarget mTarget; int32_t mRowLength; uint32_t mWidth; uint32_t mHeight; uint32_t mDepth; gfxAlphaType mSrcAlphaType; }; struct WebGLTexPboOffset { TexImageTarget mTarget; uint32_t mWidth; uint32_t mHeight; uint32_t mDepth; WebGLsizeiptr mPboOffset; bool mHasExpectedImageSize; GLsizei mExpectedImageSize; }; WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format); void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow); // From WebGLContextUtils TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget); struct WebGLIntOrFloat { const enum { Int, Float, Uint } mType; union { GLint i; GLfloat f; GLuint u; } mValue; explicit WebGLIntOrFloat(GLint i) : mType(Int) { mValue.i = i; } explicit WebGLIntOrFloat(GLfloat f) : mType(Float) { mValue.f = f; } GLint AsInt() const { return (mType == Int) ? mValue.i : NS_lroundf(mValue.f); } GLfloat AsFloat() const { return (mType == Float) ? mValue.f : GLfloat(mValue.i); } }; struct IndexedBufferBinding { RefPtr mBufferBinding; uint64_t mRangeStart; uint64_t mRangeSize; IndexedBufferBinding(); uint64_t ByteCount() const; }; //////////////////////////////////// namespace webgl { class AvailabilityRunnable final : public DiscardableRunnable { public: const WeakPtr mWebGL; std::vector> mQueries; std::vector> mSyncs; explicit AvailabilityRunnable(const ClientWebGLContext* webgl); ~AvailabilityRunnable(); NS_IMETHOD Run() override; }; struct BufferAndIndex final { const WebGLBuffer* buffer = nullptr; uint32_t id = -1; }; } // namespace webgl //////////////////////////////////////////////////////////////////////////////// class WebGLContext : public VRefCounted, public SupportsWeakPtr { friend class ScopedDrawCallWrapper; friend class ScopedDrawWithTransformFeedback; friend class ScopedFakeVertexAttrib0; friend class ScopedFBRebinder; friend class WebGL2Context; friend class WebGLContextUserData; friend class WebGLExtensionCompressedTextureASTC; friend class WebGLExtensionCompressedTextureBPTC; friend class WebGLExtensionCompressedTextureES3; friend class WebGLExtensionCompressedTextureETC1; friend class WebGLExtensionCompressedTexturePVRTC; friend class WebGLExtensionCompressedTextureRGTC; friend class WebGLExtensionCompressedTextureS3TC; friend class WebGLExtensionCompressedTextureS3TC_SRGB; friend class WebGLExtensionDepthTexture; friend class WebGLExtensionDisjointTimerQuery; friend class WebGLExtensionDrawBuffers; friend class WebGLExtensionLoseContext; friend class WebGLExtensionMOZDebug; friend class WebGLExtensionVertexArray; friend class WebGLMemoryTracker; friend class webgl::AvailabilityRunnable; friend struct webgl::LinkedProgramInfo; friend struct webgl::SamplerUniformInfo; friend class webgl::ScopedPrepForResourceClear; friend struct webgl::UniformBlockInfo; friend const webgl::CachedDrawFetchLimits* ValidateDraw(WebGLContext*, GLenum, uint32_t); friend RefPtr QueryProgramInfo( WebGLProgram* prog, gl::GLContext* gl); MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(WebGLContext, override) enum { UNPACK_FLIP_Y_WEBGL = 0x9240, UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241, // We throw InvalidOperation in TexImage if we fail to use GPU fast-path // for texture copy when it is set to true, only for debug purpose. UNPACK_REQUIRE_FASTPATH = 0x10001, CONTEXT_LOST_WEBGL = 0x9242, UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243, BROWSER_DEFAULT_WEBGL = 0x9244, UNMASKED_VENDOR_WEBGL = 0x9245, UNMASKED_RENDERER_WEBGL = 0x9246 }; private: class LruPosition final { std::list::iterator mItr; LruPosition(const LruPosition&) = delete; LruPosition(LruPosition&&) = delete; LruPosition& operator=(const LruPosition&) = delete; LruPosition& operator=(LruPosition&&) = delete; public: void AssignLocked(WebGLContext& aContext) MOZ_REQUIRES(sLruMutex); void Reset(); void ResetLocked() MOZ_REQUIRES(sLruMutex); bool IsInsertedLocked() const MOZ_REQUIRES(sLruMutex); LruPosition(); explicit LruPosition(WebGLContext&); ~LruPosition() { Reset(); } }; mutable LruPosition mLruPosition MOZ_GUARDED_BY(sLruMutex); void BumpLruLocked() MOZ_REQUIRES(sLruMutex); public: void BumpLru(); void LoseLruContextIfLimitExceeded(); // - // We've had issues in the past with nulling `gl` without actually releasing // all of our resources. This construction ensures that we are aware that we // should only null `gl` in DestroyResourcesAndContext. RefPtr mGL_OnlyClearInDestroyResourcesAndContext; public: // Grab a const reference so we can see changes, but can't make changes. const decltype(mGL_OnlyClearInDestroyResourcesAndContext)& gl; public: void CheckForInactivity(); protected: const WeakPtr mHost; const bool mResistFingerprinting; WebGLContextOptions mOptions; const uint32_t mPrincipalKey; Maybe mLimits; const uint32_t mMaxVertIdsPerDraw = StaticPrefs::webgl_max_vert_ids_per_draw(); bool mIsContextLost = false; Atomic mPendingContextLoss; webgl::ContextLossReason mPendingContextLossReason = webgl::ContextLossReason::None; const uint32_t mMaxPerfWarnings; mutable uint64_t mNumPerfWarnings = 0; const uint32_t mMaxAcceptableFBStatusInvals; bool mWarnOnce_DepthTexCompareFilterable = true; uint64_t mNextFenceId = 1; uint64_t mCompletedFenceId = 0; std::unique_ptr mIncompleteTexOverride; public: class FuncScope; private: mutable FuncScope* mFuncScope = nullptr; public: static RefPtr Create(HostWebGLContext&, const webgl::InitContextDesc&, webgl::InitContextResult* out); private: void FinishInit(); protected: WebGLContext(HostWebGLContext&, const webgl::InitContextDesc&); virtual ~WebGLContext(); RefPtr mCompositableHost; layers::LayersBackend mBackend = layers::LayersBackend::LAYERS_NONE; public: void Resize(uvec2 size); void SetCompositableHost(RefPtr& aCompositableHost); /** * 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. */ virtual void DidRefresh(); void OnMemoryPressure(); // - /* Here are the bind calls that are supposed to be fully-validated client side, so that client's binding state doesn't diverge: * AttachShader * DetachShader * BindFramebuffer * FramebufferAttach * BindBuffer * BindBufferRange * BindTexture * UseProgram * BindSampler * BindTransformFeedback * BindVertexArray * BeginQuery * EndQuery * ActiveTexture */ const auto& CurFuncScope() const { return *mFuncScope; } const char* FuncName() const; class FuncScope final { public: const WebGLContext& mWebGL; const char* const mFuncName; bool mBindFailureGuard = false; public: FuncScope(const WebGLContext& webgl, const char* funcName); ~FuncScope(); }; void GenerateErrorImpl(const GLenum err, const nsACString& text) const { GenerateErrorImpl(err, std::string(text.BeginReading())); } void GenerateErrorImpl(const GLenum err, const std::string& text) const; void GenerateError(const webgl::ErrorInfo& err) { GenerateError(err.type, "%s", err.info.c_str()); } template void GenerateError(const GLenum err, const char* const fmt, 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(fmt, args...); #ifdef __clang__ # pragma clang diagnostic pop #elif defined(__GNUC__) # pragma GCC diagnostic pop #endif GenerateErrorImpl(err, text); } template void ErrorInvalidEnum(const char* const fmt, const Args&... args) const { GenerateError(LOCAL_GL_INVALID_ENUM, fmt, args...); } template void ErrorInvalidOperation(const char* const fmt, const Args&... args) const { GenerateError(LOCAL_GL_INVALID_OPERATION, fmt, args...); } template void ErrorInvalidValue(const char* const fmt, const Args&... args) const { GenerateError(LOCAL_GL_INVALID_VALUE, fmt, args...); } template void ErrorInvalidFramebufferOperation(const char* const fmt, const Args&... args) const { GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION, fmt, args...); } template void ErrorOutOfMemory(const char* const fmt, const Args&... args) const { GenerateError(LOCAL_GL_OUT_OF_MEMORY, fmt, args...); } template void ErrorImplementationBug(const char* const fmt, const Args&... args) const { const nsPrintfCString newFmt( "Implementation bug, please file at %s! %s", "https://bugzilla.mozilla.org/" "enter_bug.cgi?product=Core&component=Canvas%3A+WebGL", fmt); GenerateError(LOCAL_GL_OUT_OF_MEMORY, newFmt.BeginReading(), args...); MOZ_ASSERT(false, "WebGLContext::ErrorImplementationBug"); NS_ERROR("WebGLContext::ErrorImplementationBug"); } void ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const; void ErrorInvalidEnumArg(const char* argName, GLenum val) const; static const char* ErrorName(GLenum error); /** * Return displayable name for GLenum. * This version is like gl::GLenumToStr but with out the GL_ prefix to * keep consistency with how errors are reported from WebGL. * Returns hex formatted version of glenum if glenum is unknown. */ static void EnumName(GLenum val, nsCString* out_name); void DummyReadFramebufferOperation(); WebGLTexture* GetActiveTex(const GLenum texTarget) const; gl::GLContext* GL() const { return gl; } bool IsPremultAlpha() const { return mOptions.premultipliedAlpha; } bool IsPreservingDrawingBuffer() const { return mOptions.preserveDrawingBuffer; } // Present to compositor private: bool PresentInto(gl::SwapChain& swapChain); bool PresentIntoXR(gl::SwapChain& swapChain, const gl::MozFramebuffer& xrFb); public: // Present swaps the front and back buffers of the swap chain for compositing. // This assumes the framebuffer may directly alias with the back buffer, // dependent on remoting state or other concerns. Framebuffer and swap chain // surface formats are assumed to be similar to enable this aliasing. As such, // the back buffer may be invalidated by this swap with the front buffer, // unless overriden by explicitly setting the preserveDrawingBuffer option, // which may incur a further copy to preserve the back buffer. void Present( WebGLFramebuffer*, layers::TextureType, const bool webvr, const webgl::SwapChainOptions& options = webgl::SwapChainOptions()); // CopyToSwapChain forces a copy from the supplied framebuffer into the back // buffer before swapping the front and back buffers of the swap chain for // compositing. The formats of the framebuffer and the swap chain buffers // may differ subject to available format conversion options. Since this // operation uses an explicit copy, it inherently preserves the framebuffer // without need to set the preserveDrawingBuffer option. void CopyToSwapChain( WebGLFramebuffer*, layers::TextureType, const webgl::SwapChainOptions& options = webgl::SwapChainOptions()); // In use cases where a framebuffer is used as an offscreen framebuffer and // does not need to be committed to the swap chain, it may still be useful // for the implementation to delineate distinct frames, such as when sharing // a single WebGLContext amongst many distinct users. EndOfFrame signals that // frame rendering is complete so that any implementation side-effects such // as resetting internal profile counters or resource queues may be handled // appropriately. void EndOfFrame(); RefPtr GetFrontBufferSnapshot(); Maybe FrontBufferSnapshotInto( const Maybe> dest, const Maybe destStride = Nothing()); Maybe FrontBufferSnapshotInto( const std::shared_ptr& front, const Maybe> dest, const Maybe destStride = Nothing()); Maybe SnapshotInto(GLuint srcFb, const gfx::IntSize& size, const Range& dest, const Maybe destStride = Nothing()); gl::SwapChain* GetSwapChain(WebGLFramebuffer*, const bool webvr); Maybe GetFrontBuffer(WebGLFramebuffer*, const bool webvr); void ClearVRSwapChain(); void RunContextLossTimer(); void CheckForContextLoss(); void HandlePendingContextLoss(); bool TryToRestoreContext(); void AssertCachedBindings() const; void AssertCachedGlobalState() const; // WebIDL WebGLRenderingContext API void Commit(); uvec2 DrawingBufferSize(); public: void GetContextAttributes(dom::Nullable& retval); // This is the entrypoint. Don't test against it directly. bool IsContextLost() const { auto* self = const_cast(this); if (self->mPendingContextLoss.exchange(false)) { self->HandlePendingContextLoss(); } return mIsContextLost; } // - RefPtr CreateBuffer(); RefPtr CreateFramebuffer(); RefPtr CreateOpaqueFramebuffer( const webgl::OpaqueFramebufferOptions& options); RefPtr CreateProgram(); RefPtr CreateQuery(); RefPtr CreateRenderbuffer(); RefPtr CreateShader(GLenum type); RefPtr CreateTexture(); RefPtr CreateVertexArray(); // - void AttachShader(WebGLProgram& prog, WebGLShader& shader); void BindAttribLocation(WebGLProgram& prog, GLuint location, const std::string& name) const; void BindFramebuffer(GLenum target, WebGLFramebuffer* fb); void BindRenderbuffer(GLenum target, WebGLRenderbuffer* fb); void BindVertexArray(WebGLVertexArray* vao); void BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a); void BlendEquationSeparate(Maybe i, GLenum modeRGB, GLenum modeAlpha); void BlendFuncSeparate(Maybe i, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); GLenum CheckFramebufferStatus(GLenum target); void Clear(GLbitfield mask); void ClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a); void ClearDepth(GLclampf v); void ClearStencil(GLint v); void ColorMask(Maybe i, uint8_t mask); void CompileShader(WebGLShader& shader); private: void CompileShaderANGLE(WebGLShader* shader); void CompileShaderBypass(WebGLShader* shader, const nsCString& shaderSource); public: void CullFace(GLenum face); void DepthFunc(GLenum func); void DepthMask(WebGLboolean b); void DepthRange(GLclampf zNear, GLclampf zFar); void DetachShader(WebGLProgram& prog, const WebGLShader& shader); void DrawBuffers(const std::vector& buffers); void Flush(); void Finish(); void FramebufferAttach(GLenum target, GLenum attachSlot, GLenum bindImageTarget, const webgl::FbAttachInfo& toAttach); void FrontFace(GLenum mode); Maybe GetBufferParameter(GLenum target, GLenum pname); webgl::CompileResult GetCompileResult(const WebGLShader&) const; GLenum GetError(); GLint GetFragDataLocation(const WebGLProgram&, const std::string& name) const; Maybe GetFramebufferAttachmentParameter(WebGLFramebuffer*, GLenum attachment, GLenum pname) const; Maybe GetRenderbufferParameter(const WebGLRenderbuffer&, GLenum pname) const; webgl::LinkResult GetLinkResult(const WebGLProgram&) const; Maybe GetShaderPrecisionFormat( GLenum shadertype, GLenum precisiontype) const; webgl::GetUniformData GetUniform(const WebGLProgram&, uint32_t loc) const; void Hint(GLenum target, GLenum mode); void LineWidth(GLfloat width); void LinkProgram(WebGLProgram& prog); void PolygonOffset(GLfloat factor, GLfloat units); void ProvokingVertex(webgl::ProvokingVertex) const; //// webgl::PackingInfo ValidImplementationColorReadPI( const webgl::FormatUsageInfo* usage) const; protected: webgl::ReadPixelsResult ReadPixelsImpl(const webgl::ReadPixelsDesc&, uintptr_t dest, uint64_t availBytes); bool DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, const webgl::ReadPixelsDesc&, uintptr_t dest, uint64_t dataLen, uint32_t rowStride); public: void ReadPixelsPbo(const webgl::ReadPixelsDesc&, uint64_t offset); webgl::ReadPixelsResult ReadPixelsInto(const webgl::ReadPixelsDesc&, const Range& dest); //// void RenderbufferStorageMultisample(WebGLRenderbuffer&, uint32_t samples, GLenum internalformat, uint32_t width, uint32_t height) const; public: void SampleCoverage(GLclampf value, WebGLboolean invert); void Scissor(GLint x, GLint y, GLsizei width, GLsizei height); void ShaderSource(WebGLShader& shader, const std::string& source) const; 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 UniformData(uint32_t loc, bool transpose, const Range& data) const; //////////////////////////////////// void UseProgram(WebGLProgram* prog); bool ValidateAttribArraySetter(uint32_t count, uint32_t arrayLength); bool ValidateProgram(const WebGLProgram& prog) const; void Viewport(GLint x, GLint y, GLsizei width, GLsizei height); // ----------------------------------------------------------------------------- // Buffer Objects (WebGLContextBuffers.cpp) void BindBuffer(GLenum target, WebGLBuffer* buffer); void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buf, uint64_t offset, uint64_t size); void BufferData(GLenum target, uint64_t dataLen, const uint8_t* data, GLenum usage) const; // The unsynchronized flag may allow for better performance when // interleaving buffer updates with draw calls. However, care must // be taken. This has similar semantics to glMapBufferRange's // GL_MAP_UNSYNCHRONIZED_BIT: the results of any pending operations // that reference the region of the buffer being updated are // undefined. void BufferSubData(GLenum target, uint64_t dstByteOffset, uint64_t srcDataLen, const uint8_t* srcData, bool unsynchronized = false) const; protected: // bound buffer state RefPtr mBoundArrayBuffer; RefPtr mBoundCopyReadBuffer; RefPtr mBoundCopyWriteBuffer; RefPtr mBoundPixelPackBuffer; RefPtr mBoundPixelUnpackBuffer; RefPtr mBoundTransformFeedbackBuffer; RefPtr mBoundUniformBuffer; std::vector mIndexedUniformBufferBindings; RefPtr& GetBufferSlotByTarget(GLenum target); RefPtr& GetBufferSlotByTargetIndexed(GLenum target, GLuint index); // - void GenErrorIllegalUse(GLenum useTarget, uint32_t useId, GLenum boundTarget, uint32_t boundId) const; bool ValidateBufferForNonTf(const WebGLBuffer&, GLenum nonTfTarget, uint32_t nonTfId) const; bool ValidateBufferForNonTf(const WebGLBuffer* const nonTfBuffer, const GLenum nonTfTarget, const uint32_t nonTfId = -1) const { if (!nonTfBuffer) return true; return ValidateBufferForNonTf(*nonTfBuffer, nonTfTarget, nonTfId); } bool ValidateBuffersForTf(const WebGLTransformFeedback&, const webgl::LinkedProgramInfo&) const; bool ValidateBuffersForTf( const std::vector& tfBuffers) const; // ----------------------------------------------------------------------------- // Queries (WebGL2ContextQueries.cpp) protected: RefPtr mQuerySlot_SamplesPassed; RefPtr mQuerySlot_TFPrimsWritten; RefPtr mQuerySlot_TimeElapsed; RefPtr* ValidateQuerySlotByTarget(GLenum target); public: void BeginQuery(GLenum target, WebGLQuery& query); void EndQuery(GLenum target); Maybe GetQueryParameter(const WebGLQuery& query, GLenum pname) const; void QueryCounter(WebGLQuery&) const; // ----------------------------------------------------------------------------- // State and State Requests (WebGLContextState.cpp) void SetEnabled(GLenum cap, Maybe i, bool enabled); bool GetStencilBits(GLint* const out_stencilBits) const; virtual Maybe GetParameter(GLenum pname); Maybe GetString(GLenum pname) const; bool IsEnabled(GLenum cap); private: static StaticMutex sLruMutex; static std::list sLru MOZ_GUARDED_BY(sLruMutex); // State tracking slots bool mDitherEnabled = true; bool mRasterizerDiscardEnabled = false; bool mScissorTestEnabled = false; bool mDepthTestEnabled = false; bool mStencilTestEnabled = false; GLenum mGenerateMipmapHint = LOCAL_GL_DONT_CARE; struct ScissorRect final { GLint x; GLint y; GLsizei w; GLsizei h; void Apply(gl::GLContext&) const; }; ScissorRect mScissorRect = {}; bool ValidateCapabilityEnum(GLenum cap); bool* GetStateTrackingSlot(GLenum cap, GLuint i); // Allocation debugging variables mutable uint64_t mDataAllocGLCallCount = 0; void OnDataAllocCall() const { mDataAllocGLCallCount++; } uint64_t GetNumGLDataAllocCalls() const { return mDataAllocGLCallCount; } void OnEndOfFrame(); // ----------------------------------------------------------------------------- // Texture funcions (WebGLContextTextures.cpp) public: void ActiveTexture(uint32_t texUnit); void BindTexture(GLenum texTarget, WebGLTexture* tex); void GenerateMipmap(GLenum texTarget); Maybe GetTexParameter(const WebGLTexture&, GLenum pname) const; void TexParameter_base(GLenum texTarget, GLenum pname, const FloatOrInt& param); virtual bool IsTexParamValid(GLenum pname) const; //////////////////////////////////// // Uploads // CompressedTexSubImage if `sub` void CompressedTexImage(bool sub, GLenum imageTarget, uint32_t level, GLenum format, uvec3 offset, uvec3 size, const Range& src, const uint32_t pboImageSize, const Maybe& pboOffset) const; // CopyTexSubImage if `!respectFormat` void CopyTexImage(GLenum imageTarget, uint32_t level, GLenum respecFormat, uvec3 dstOffset, const ivec2& srcOffset, const uvec2& size) const; // TexSubImage if `!respectFormat` void TexImage(uint32_t level, GLenum respecFormat, uvec3 offset, const webgl::PackingInfo& pi, const webgl::TexUnpackBlobDesc&) const; void TexStorage(GLenum texTarget, uint32_t levels, GLenum sizedFormat, uvec3 size) const; UniquePtr ToTexUnpackBytes( const WebGLTexImageData& imageData); UniquePtr ToTexUnpackBytes(WebGLTexPboOffset& aPbo); //////////////////////////////////// // WebGLTextureUpload.cpp protected: bool ValidateTexImageSpecification(uint8_t funcDims, GLenum texImageTarget, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLint border, TexImageTarget* const out_target, WebGLTexture** const out_texture, webgl::ImageInfo** const out_imageInfo); bool ValidateTexImageSelection(uint8_t funcDims, GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, TexImageTarget* const out_target, WebGLTexture** const out_texture, webgl::ImageInfo** const out_imageInfo); bool ValidateUnpackInfo(bool usePBOs, GLenum format, GLenum type, webgl::PackingInfo* const out); // ----------------------------------------------------------------------------- // Vertices Feature (WebGLContextVertices.cpp) GLenum mPrimRestartTypeBytes = 0; public: void DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertexCount, GLsizei instanceCount); void DrawElementsInstanced(GLenum mode, GLsizei vertexCount, GLenum type, WebGLintptr byteOffset, GLsizei instanceCount); void EnableVertexAttribArray(GLuint index); void DisableVertexAttribArray(GLuint index); Maybe GetVertexAttrib(GLuint index, GLenum pname); //// void VertexAttrib4T(GLuint index, const webgl::TypedQuad&); //// void VertexAttribPointer(uint32_t index, const webgl::VertAttribPointerDesc&); void VertexAttribDivisor(GLuint index, GLuint divisor); private: WebGLBuffer* DrawElements_check(GLsizei indexCount, GLenum type, WebGLintptr byteOffset, GLsizei instanceCount); void Draw_cleanup(); void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr); void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr); void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr); void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr); bool BindArrayAttribToLocation0(WebGLProgram* prog); // ----------------------------------------------------------------------------- // PROTECTED protected: WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const; bool DoFakeVertexAttrib0(uint64_t fakeVertexCount, WebGLVertexAttrib0Status whatDoesAttrib0Need); void UndoFakeVertexAttrib0(); bool mResetLayer = true; bool mOptionsFrozen = false; bool mIsMesa = false; bool mLoseContextOnMemoryPressure = false; bool mCanLoseContextInForeground = true; bool mShouldPresent = false; bool mDisableFragHighP = false; bool mForceResizeOnPresent = false; bool mVRReady = false; template void DeleteWebGLObjectsArray(nsTArray& array); GLuint mActiveTexture = 0; GLenum mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK; GLenum mDefaultFB_ReadBuffer = LOCAL_GL_BACK; mutable GLenum mWebGLError = 0; std::unique_ptr CreateShaderValidator( GLenum shaderType) const; // some GL constants uint32_t mGLMaxFragmentUniformVectors = 0; uint32_t mGLMaxVertexUniformVectors = 0; uint32_t mGLMaxVertexOutputVectors = 0; uint32_t mGLMaxFragmentInputVectors = 0; uint32_t mGLMaxVertexTextureImageUnits = 0; uint32_t mGLMaxFragmentTextureImageUnits = 0; uint32_t mGLMaxCombinedTextureImageUnits = 0; // ES3: uint32_t mGLMinProgramTexelOffset = 0; uint32_t mGLMaxProgramTexelOffset = 0; public: auto GLMaxDrawBuffers() const { return mLimits->maxColorDrawBuffers; } uint32_t MaxValidDrawBuffers() const { if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { return GLMaxDrawBuffers(); } return 1; } GLenum LastColorAttachmentEnum() const { return LOCAL_GL_COLOR_ATTACHMENT0 + MaxValidDrawBuffers() - 1; } const auto& Options() const { return mOptions; } protected: uint32_t mGLMaxRenderbufferSize = 0; public: const auto& Limits() const { return *mLimits; } auto MaxVertexAttribs() const { return mLimits->maxVertexAttribs; } auto GLMaxTextureUnits() const { return mLimits->maxTexUnits; } bool IsFormatValidForFB(TexInternalFormat format) const; protected: // ------------------------------------------------------------------------- // WebGL extensions (implemented in WebGLContextExtensions.cpp) EnumeratedArray> mExtensions; public: void RequestExtension(WebGLExtensionID, bool explicitly = true); // returns true if the extension has been enabled by calling getExtension. bool IsExtensionEnabled(const WebGLExtensionID id) const { return bool(mExtensions[id]); } bool IsExtensionExplicit(WebGLExtensionID) const; void WarnIfImplicit(WebGLExtensionID) const; bool IsExtensionSupported(WebGLExtensionID) const; // ------------------------------------------------------------------------- // WebGL 2 specifics (implemented in WebGL2Context.cpp) public: virtual bool IsWebGL2() const { return false; } struct FailureReason { nsCString key; // For reporting. nsCString info; FailureReason() = default; template FailureReason(const A& _key, const B& _info) : key(nsCString(_key)), info(nsCString(_info)) {} }; protected: bool InitWebGL2(FailureReason* const out_failReason); bool CreateAndInitGL(bool forceEnabled, std::vector* const out_failReasons); // ------------------------------------------------------------------------- // Validation functions (implemented in WebGLContextValidate.cpp) bool InitAndValidateGL(FailureReason* const out_failReason); bool ValidateBlendEquationEnum(GLenum cap, const char* info); bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char* info); bool ValidateStencilOpEnum(GLenum action, const char* info); bool ValidateFaceEnum(GLenum face); bool ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type, WebGLboolean normalized, GLsizei stride, WebGLintptr byteOffset, const char* info); bool ValidateStencilParamsForDrawCall() const; bool ValidateCopyTexImage(TexInternalFormat srcFormat, TexInternalFormat dstformat, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateTexImage(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, GLint xoffset, GLint yoffset, GLint zoffset, GLint width, GLint height, GLint depth, GLint border, GLenum format, GLenum type, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateTexImageFormat(GLenum internalFormat, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateTexImageType(GLenum type, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateTexImageFormatAndType(GLenum format, GLenum type, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateCompTexImageInternalFormat(GLenum format, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateCopyTexImageInternalFormat(GLenum format, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateTexImageSize(TexImageTarget texImageTarget, GLint level, GLint width, GLint height, GLint depth, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateTexSubImageSize(GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, GLsizei baseWidth, GLsizei baseHeight, GLsizei baseDepth, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateCompTexImageSize(GLint level, GLenum internalFormat, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLsizei levelWidth, GLsizei levelHeight, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateCompTexImageDataSize(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, uint32_t byteLength, WebGLTexImageFunc func, WebGLTexDimensions dims); bool HasDrawBuffers() const { return IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers); } RefPtr* ValidateBufferSlot(GLenum target); public: WebGLBuffer* ValidateBufferSelection(GLenum target) const; protected: IndexedBufferBinding* ValidateIndexedBufferSlot(GLenum target, GLuint index); bool ValidateIndexedBufferBinding( GLenum target, GLuint index, RefPtr** const out_genericBinding, IndexedBufferBinding** const out_indexedBinding); public: bool ValidateNonNegative(const char* argName, int64_t val) const { if (MOZ_UNLIKELY(val < 0)) { ErrorInvalidValue("`%s` must be non-negative.", argName); return false; } return true; } template bool ValidateNonNull(const char* const argName, const dom::Nullable& maybe) const { if (maybe.IsNull()) { ErrorInvalidValue("%s: Cannot be null.", argName); return false; } return true; } //// protected: void DestroyResourcesAndContext(); // helpers bool ConvertImage(size_t width, size_t height, size_t srcStride, size_t dstStride, const uint8_t* src, uint8_t* dst, WebGLTexelFormat srcFormat, bool srcPremultiplied, WebGLTexelFormat dstFormat, bool dstPremultiplied, size_t dstTexelSize); ////// public: template bool ValidateObject(const char* const argName, const T& object) const { // Todo: Remove all callers. return true; } template bool ValidateObject(const char* const argName, const T* const object) const { // Todo: Remove most (all?) callers. if (!object) { ErrorInvalidOperation( "%s: Object argument cannot have been marked for" " deletion.", argName); return false; } return true; } //// private: void LoseContextLruLocked(webgl::ContextLossReason reason) MOZ_REQUIRES(sLruMutex); public: void LoseContext( webgl::ContextLossReason reason = webgl::ContextLossReason::None); protected: nsTArray> mBound2DTextures; nsTArray> mBoundCubeMapTextures; nsTArray> mBound3DTextures; nsTArray> mBound2DArrayTextures; nsTArray> mBoundSamplers; void ResolveTexturesForDraw() const; RefPtr mCurrentProgram; RefPtr mActiveProgramLinkInfo; bool ValidateFramebufferTarget(GLenum target) const; bool ValidateInvalidateFramebuffer(GLenum target, const Range& attachments, std::vector* const scopedVector, GLsizei* const out_glNumAttachments, const GLenum** const out_glAttachments); RefPtr mBoundDrawFramebuffer; RefPtr mBoundReadFramebuffer; RefPtr mBoundTransformFeedback; RefPtr mBoundVertexArray; public: const auto& BoundReadFb() const { return mBoundReadFramebuffer; } protected: RefPtr mDefaultTransformFeedback; RefPtr mDefaultVertexArray; //////////////////////////////////// protected: GLuint mEmptyTFO = 0; // Generic Vertex Attributes // Though CURRENT_VERTEX_ATTRIB is listed under "Vertex Shader State" in the // spec state tables, this isn't vertex shader /object/ state. This array is // merely state useful to vertex shaders, but is global state. std::vector mGenericVertexAttribTypes; CacheInvalidator mGenericVertexAttribTypeInvalidator; GLuint mFakeVertexAttrib0BufferObject = 0; intptr_t mFakeVertexAttrib0BufferObjectSize = 0; bool mFakeVertexAttrib0DataDefined = false; alignas(alignof(float)) uint8_t mGenericVertexAttrib0Data[sizeof(float) * 4] = {}; alignas(alignof(float)) uint8_t mFakeVertexAttrib0Data[sizeof(float) * 4] = {}; GLint mStencilRefFront = 0; GLint mStencilRefBack = 0; GLuint mStencilValueMaskFront = 0; GLuint mStencilValueMaskBack = 0; GLuint mStencilWriteMaskFront = 0; GLuint mStencilWriteMaskBack = 0; uint8_t mColorWriteMask0 = 0xf; // bitmask mutable uint8_t mDriverColorMask0 = 0xf; bool mDepthWriteMask = true; GLfloat mColorClearValue[4] = {0, 0, 0, 0}; GLint mStencilClearValue = 0; GLfloat mDepthClearValue = 1.0f; std::bitset mColorWriteMaskNonzero = -1; std::bitset mBlendEnabled = 0; GLint mViewportX = 0; GLint mViewportY = 0; GLsizei mViewportWidth = 0; GLsizei mViewportHeight = 0; bool mAlreadyWarnedAboutViewportLargerThanDest = false; GLfloat mLineWidth = 1.0; WebGLContextLossHandler mContextLossHandler; // Used for some hardware (particularly Tegra 2 and 4) that likes to // be Flushed while doing hundreds of draw calls. mutable uint64_t mDrawCallsSinceLastFlush = 0; mutable uint64_t mWarningCount = 0; const uint64_t mMaxWarnings; bool mAlreadyWarnedAboutFakeVertexAttrib0 = false; bool ShouldGenerateWarnings() const { return mWarningCount < mMaxWarnings; } bool ShouldGeneratePerfWarnings() const { return mNumPerfWarnings < mMaxPerfWarnings; } bool mNeedsFakeNoAlpha = false; bool mNeedsFakeNoDepth = false; bool mNeedsFakeNoStencil = false; bool mNeedsFakeNoStencil_UserFBs = false; bool mDriverDepthTest = false; bool mDriverStencilTest = false; bool mNeedsLegacyVertexAttrib0Handling = false; bool mMaybeNeedsLegacyVertexAttrib0Handling = false; bool mNeedsIndexValidation = false; bool mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst = false; const bool mAllowFBInvalidation; bool Has64BitTimestamps() const; // -- const uint8_t mMsaaSamples; mutable uvec2 mRequestedSize; mutable UniquePtr mDefaultFB; mutable bool mDefaultFB_IsInvalid = false; mutable UniquePtr mResolvedDefaultFB; gl::SwapChain mSwapChain; gl::SwapChain mWebVRSwapChain; RefPtr mRemoteTextureOwner; bool PushRemoteTexture(WebGLFramebuffer*, gl::SwapChain&, std::shared_ptr, const webgl::SwapChainOptions& options); // -- bool EnsureDefaultFB(); bool ValidateAndInitFB( const WebGLFramebuffer* fb, GLenum incompleteFbError = LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); void DoBindFB(const WebGLFramebuffer* fb, GLenum target = LOCAL_GL_FRAMEBUFFER) const; bool BindCurFBForDraw(); bool BindCurFBForColorRead( const webgl::FormatUsageInfo** out_format, uint32_t* out_width, uint32_t* out_height, GLenum incompleteFbError = LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); void DoColorMask(Maybe i, uint8_t bitmask) const; void BlitBackbufferToCurDriverFB( WebGLFramebuffer* const srcAsWebglFb = nullptr, const gl::MozFramebuffer* const srcAsMozFb = nullptr, bool srcIsBGRA = false) const; bool BindDefaultFBForRead(); // -- public: // console logging helpers template void GenerateWarning(const char* const fmt, const Args&... args) const { GenerateError(0, fmt, args...); } template void GeneratePerfWarning(const char* const fmt, const Args&... args) const { GenerateError(webgl::kErrorPerfWarning, fmt, args...); } public: UniquePtr mFormatUsage; virtual UniquePtr CreateFormatUsage( gl::GLContext* gl) const; const decltype(mBound2DTextures)* TexListForElemType(GLenum elemType) const; // Friend list friend class ScopedCopyTexImageSource; friend class ScopedResolveTexturesForDraw; friend class webgl::TexUnpackBlob; friend class webgl::TexUnpackBytes; friend class webgl::TexUnpackImage; friend class webgl::TexUnpackSurface; friend struct webgl::UniformInfo; friend class WebGLTexture; friend class WebGLFBAttachPoint; friend class WebGLFramebuffer; friend class WebGLRenderbuffer; friend class WebGLProgram; friend class WebGLQuery; friend class WebGLBuffer; friend class WebGLSampler; friend class WebGLShader; friend class WebGLSync; friend class WebGLTransformFeedback; friend class WebGLVertexArray; friend class WebGLVertexArrayFake; friend class WebGLVertexArrayGL; }; // Returns `value` rounded to the next highest multiple of `multiple`. // AKA PadToAlignment, StrideForAlignment. template V RoundUpToMultipleOf(const V& value, const M& multiple) { return ((value + multiple - 1) / multiple) * multiple; } class ScopedFBRebinder final { private: const WebGLContext* const mWebGL; public: explicit ScopedFBRebinder(const WebGLContext* const webgl) : mWebGL(webgl) {} ~ScopedFBRebinder(); }; // - constexpr inline bool IsBufferTargetLazilyBound(const GLenum target) { return target != LOCAL_GL_ELEMENT_ARRAY_BUFFER; } void DoBindBuffer(gl::GLContext&, GLenum target, const WebGLBuffer*); class ScopedLazyBind final { private: gl::GLContext& mGL; const GLenum mTarget; public: ScopedLazyBind(gl::GLContext* const gl, const GLenum target, const WebGLBuffer* const buf) : mGL(*gl), mTarget(IsBufferTargetLazilyBound(target) ? target : 0) { if (mTarget) { DoBindBuffer(mGL, mTarget, buf); } } ~ScopedLazyBind() { if (mTarget) { DoBindBuffer(mGL, mTarget, nullptr); } } }; //// bool Intersect(int32_t srcSize, int32_t read0, int32_t readSize, int32_t* out_intRead0, int32_t* out_intWrite0, int32_t* out_intSize); uint64_t AvailGroups(uint64_t totalAvailItems, uint64_t firstItemOffset, uint32_t groupSize, uint32_t groupStride); //// class ScopedDrawCallWrapper final { public: WebGLContext& mWebGL; explicit ScopedDrawCallWrapper(WebGLContext& webgl); ~ScopedDrawCallWrapper(); }; namespace webgl { class ScopedPrepForResourceClear final { const WebGLContext& webgl; public: explicit ScopedPrepForResourceClear(const WebGLContext&); ~ScopedPrepForResourceClear(); }; struct IndexedName final { std::string name; uint64_t index; }; Maybe ParseIndexed(const std::string& str); } // namespace webgl webgl::LinkActiveInfo GetLinkActiveInfo( gl::GLContext& gl, const GLuint prog, const bool webgl2, const std::unordered_map& nameUnmap); } // namespace mozilla #endif