/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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 "nsTArray.h" #include "nsDataHashtable.h" #include "nsRefPtrHashtable.h" #include "nsHashKeys.h" #include "nsIDocShell.h" #include "nsIDOMWebGLRenderingContext.h" #include "nsICanvasRenderingContextInternal.h" #include "nsHTMLCanvasElement.h" #include "nsWeakReference.h" #include "nsIDOMHTMLElement.h" #include "nsIMemoryReporter.h" #include "nsIJSNativeInitializer.h" #include "nsContentUtils.h" #include "nsWrapperCache.h" #include "nsIObserver.h" #include "GLContextProvider.h" #include "Layers.h" #include "nsDataHashtable.h" #include "mozilla/LinkedList.h" #include "mozilla/CheckedInt.h" #include "mozilla/dom/ImageData.h" #ifdef XP_MACOSX #include "ForceDiscreteGPUHelperCGL.h" #endif #include "angle/ShaderLang.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/Nullable.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingUtils.h" /* * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25 * https://bugzilla.mozilla.org/show_bug.cgi?id=686732 * * Exceptions: some of the following values are set to higher values than in the spec because * the values in the spec are ridiculously low. They are explicitly marked below */ #define MINVALUE_GL_MAX_TEXTURE_SIZE 1024 // Different from the spec, which sets it to 64 on page 162 #define MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE 512 // Different from the spec, which sets it to 16 on page 162 #define MINVALUE_GL_MAX_VERTEX_ATTRIBS 8 // Page 164 #define MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS 16 // Page 164 #define MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS 128 // Page 164 #define MINVALUE_GL_MAX_VARYING_VECTORS 8 // Page 164 #define MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS 8 // Page 164 #define MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0 // Page 164 #define MINVALUE_GL_MAX_RENDERBUFFER_SIZE 1024 // Different from the spec, which sets it to 1 on page 164 #define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 8 // Page 164 class nsIDocShell; class nsIPropertyBag; namespace mozilla { class WebGLTexture; class WebGLBuffer; class WebGLProgram; class WebGLShader; class WebGLFramebuffer; class WebGLRenderbuffer; class WebGLUniformLocation; class WebGLExtension; class WebGLContext; struct WebGLVertexAttribData; class WebGLMemoryPressureObserver; class WebGLRectangleObject; class WebGLContextBoundObject; class WebGLActiveInfo; class WebGLShaderPrecisionFormat; enum FakeBlackStatus { DoNotNeedFakeBlack, DoNeedFakeBlack, DontKnowIfNeedFakeBlack }; struct VertexAttrib0Status { enum { Default, EmulatedUninitializedArray, EmulatedInitializedArray }; }; struct BackbufferClearingStatus { enum { NotClearedSinceLastPresented, ClearedToDefaultValues, HasBeenDrawnTo }; }; namespace WebGLTexelConversions { /* * The formats that may participate, either as source or destination formats, * in WebGL texture conversions. This includes: * - all the formats accepted by WebGL.texImage2D, e.g. RGBA4444 * - additional formats provided by extensions, e.g. RGB32F * - additional source formats, depending on browser details, used when uploading * textures from DOM elements. See gfxImageSurface::Format(). */ enum WebGLTexelFormat { // dummy error code returned by GetWebGLTexelFormat in error cases, // after assertion failure (so this never happens in debug builds) BadFormat, // dummy pseudo-format meaning "use the other format". // For example, if SrcFormat=Auto and DstFormat=RGB8, then the source // is implicitly treated as being RGB8 itself. Auto, // 1-channel formats R8, A8, R32F, // used for OES_texture_float extension A32F, // used for OES_texture_float extension // 2-channel formats RA8, RA32F, // 3-channel formats RGB8, BGRX8, // used for DOM elements. Source format only. RGB565, RGB32F, // used for OES_texture_float extension // 4-channel formats RGBA8, BGRA8, // used for DOM elements RGBA5551, RGBA4444, RGBA32F // used for OES_texture_float extension }; } // end namespace WebGLTexelConversions using WebGLTexelConversions::WebGLTexelFormat; WebGLTexelFormat GetWebGLTexelFormat(GLenum format, GLenum type); // Zero is not an integer power of two. inline bool is_pot_assuming_nonnegative(WebGLsizei x) { return x && (x & (x-1)) == 0; } /* Each WebGL object class WebGLFoo wants to: * - inherit WebGLRefCountedObject * - implement a Delete() method * - have its destructor call DeleteOnce() * * This base class provides two features to WebGL object types: * 1. support for OpenGL object reference counting * 2. support for OpenGL deletion statuses * ***** 1. OpenGL object reference counting ***** * * WebGL objects such as WebGLTexture's really have two different refcounts: * the XPCOM refcount, that is directly exposed to JavaScript, and the OpenGL * refcount. * * For example, when in JavaScript one does: var newname = existingTexture; * that increments the XPCOM refcount, but doesn't affect the OpenGL refcount. * When one attaches the texture to a framebuffer object, that does increment * its OpenGL refcount (and also its XPCOM refcount, to prevent the regular * XPCOM refcounting mechanism from destroying objects prematurely). * * The actual OpenGL refcount is opaque to us (it's internal to the OpenGL * implementation) but is affects the WebGL semantics that we have to implement: * for example, a WebGLTexture that is attached to a WebGLFramebuffer must not * be actually deleted, even if deleteTexture has been called on it, and even * if JavaScript doesn't have references to it anymore. We can't just rely on * OpenGL to keep alive the underlying OpenGL texture for us, for a variety of * reasons, most importantly: we'd need to know when OpenGL objects are actually * deleted, and OpenGL doesn't notify us about that, so we would have to query * status very often with glIsXxx calls which isn't practical. * * This means that we have to keep track of the OpenGL refcount ourselves, * in addition to the XPCOM refcount. * * This class implements such a refcount, see the mWebGLRefCnt * member. In order to avoid name clashes (with regular XPCOM refcounting) * in the derived class, we prefix members with 'WebGL', whence the names * WebGLAddRef, WebGLRelease, etc. * * In practice, WebGLAddRef and WebGLRelease are only called from the * WebGLRefPtr class. * ***** 2. OpenGL deletion statuses ***** * * In OpenGL, an object can go through 3 different deletion statuses during its * lifetime, which correspond to the 3 enum values for DeletionStatus in this class: * - the Default status, which it has from its creation to when the * suitable glDeleteXxx function is called on it; * - the DeleteRequested status, which is has from when the suitable glDeleteXxx * function is called on it to when it is no longer referenced by other OpenGL * objects. For example, a texture that is attached to a non-current FBO * will enter that status when glDeleteTexture is called on it. For objects * with that status, GL_DELETE_STATUS queries return true, but glIsXxx * functions still return true. * - the Deleted status, which is the status of objects on which the * suitable glDeleteXxx function has been called, and that are not referenced * by other OpenGL objects. * * This state is stored in the mDeletionStatus member of this class. * * When the GL refcount hits zero, if the status is DeleteRequested then we call * the Delete() method on the derived class and the status becomes Deleted. This is * what the MaybeDelete() function does. * * The DeleteOnce() function implemented here is a helper to ensure that we don't * call Delete() twice on the same object. Since the derived class' destructor * needs to call DeleteOnce() which calls Delete(), we can't allow either to be * virtual. Strictly speaking, we could let them be virtual if the derived class * were final, but that would be impossible to enforce and would lead to strange * bugs if it were subclassed. * * This WebGLRefCountedObject class takes the Derived type * as template parameter, as a means to allow DeleteOnce to call Delete() * on the Derived class, without either method being virtual. This is a common * C++ pattern known as the "curiously recursive template pattern (CRTP)". */ template class WebGLRefCountedObject { public: enum DeletionStatus { Default, DeleteRequested, Deleted }; WebGLRefCountedObject() : mDeletionStatus(Default) { } ~WebGLRefCountedObject() { NS_ABORT_IF_FALSE(mWebGLRefCnt == 0, "destroying WebGL object still referenced by other WebGL objects"); NS_ABORT_IF_FALSE(mDeletionStatus == Deleted, "Derived class destructor must call DeleteOnce()"); } // called by WebGLRefPtr void WebGLAddRef() { ++mWebGLRefCnt; } // called by WebGLRefPtr void WebGLRelease() { NS_ABORT_IF_FALSE(mWebGLRefCnt > 0, "releasing WebGL object with WebGL refcnt already zero"); --mWebGLRefCnt; MaybeDelete(); } // this is the function that WebGL.deleteXxx() functions want to call void RequestDelete() { if (mDeletionStatus == Default) mDeletionStatus = DeleteRequested; MaybeDelete(); } bool IsDeleted() const { return mDeletionStatus == Deleted; } bool IsDeleteRequested() const { return mDeletionStatus != Default; } void DeleteOnce() { if (mDeletionStatus != Deleted) { static_cast(this)->Delete(); mDeletionStatus = Deleted; } } private: void MaybeDelete() { if (mWebGLRefCnt == 0 && mDeletionStatus == DeleteRequested) { DeleteOnce(); } } protected: nsAutoRefCnt mWebGLRefCnt; DeletionStatus mDeletionStatus; }; /* This WebGLRefPtr class is meant to be used for references between WebGL objects. * For example, a WebGLProgram holds WebGLRefPtr's to the WebGLShader's attached * to it. * * Why the need for a separate refptr class? The only special thing that WebGLRefPtr * does is that it increments and decrements the WebGL refcount of * WebGLRefCountedObject's, in addition to incrementing and decrementing the * usual XPCOM refcount. * * This means that by using a WebGLRefPtr instead of a nsRefPtr, you ensure that * the WebGL refcount is incremented, which means that the object will be kept * alive by this reference even if the matching webgl.deleteXxx() function is * called on it. */ template class WebGLRefPtr { public: WebGLRefPtr() : mRawPtr(0) { } WebGLRefPtr(const WebGLRefPtr& aSmartPtr) : mRawPtr(aSmartPtr.mRawPtr) { AddRefOnPtr(mRawPtr); } WebGLRefPtr(T *aRawPtr) : mRawPtr(aRawPtr) { AddRefOnPtr(mRawPtr); } ~WebGLRefPtr() { ReleasePtr(mRawPtr); } WebGLRefPtr& operator=(const WebGLRefPtr& rhs) { assign_with_AddRef(rhs.mRawPtr); return *this; } WebGLRefPtr& operator=(T* rhs) { assign_with_AddRef(rhs); return *this; } T* get() const { return static_cast(mRawPtr); } operator T*() const { return get(); } T* operator->() const { NS_ABORT_IF_FALSE(mRawPtr != 0, "You can't dereference a NULL WebGLRefPtr with operator->()!"); return get(); } T& operator*() const { NS_ABORT_IF_FALSE(mRawPtr != 0, "You can't dereference a NULL WebGLRefPtr with operator*()!"); return *get(); } private: static void AddRefOnPtr(T* rawPtr) { if (rawPtr) { rawPtr->WebGLAddRef(); rawPtr->AddRef(); } } static void ReleasePtr(T* rawPtr) { if (rawPtr) { rawPtr->WebGLRelease(); // must be done first before Release(), as Release() might actually destroy the object rawPtr->Release(); } } void assign_with_AddRef(T* rawPtr) { AddRefOnPtr(rawPtr); assign_assuming_AddRef(rawPtr); } void assign_assuming_AddRef(T* newPtr) { T* oldPtr = mRawPtr; mRawPtr = newPtr; ReleasePtr(oldPtr); } protected: T *mRawPtr; }; // this class is a mixin for GL objects that have dimensions // that we need to track. class WebGLRectangleObject { public: WebGLRectangleObject() : mWidth(0), mHeight(0) { } WebGLRectangleObject(WebGLsizei width, WebGLsizei height) : mWidth(width), mHeight(height) { } WebGLsizei Width() const { return mWidth; } void width(WebGLsizei value) { mWidth = value; } WebGLsizei Height() const { return mHeight; } void height(WebGLsizei value) { mHeight = value; } void setDimensions(WebGLsizei width, WebGLsizei height) { mWidth = width; mHeight = height; } void setDimensions(WebGLRectangleObject *rect) { if (rect) { mWidth = rect->Width(); mHeight = rect->Height(); } else { mWidth = 0; mHeight = 0; } } bool HasSameDimensionsAs(const WebGLRectangleObject& other) const { return Width() == other.Width() && Height() == other.Height(); } protected: WebGLsizei mWidth; WebGLsizei mHeight; }; struct WebGLContextOptions { // these are defaults WebGLContextOptions() : alpha(true), depth(true), stencil(false), premultipliedAlpha(true), antialias(true), preserveDrawingBuffer(false) { } bool operator==(const WebGLContextOptions& other) const { return alpha == other.alpha && depth == other.depth && stencil == other.stencil && premultipliedAlpha == other.premultipliedAlpha && antialias == other.antialias && preserveDrawingBuffer == other.preserveDrawingBuffer; } bool operator!=(const WebGLContextOptions& other) const { return !operator==(other); } bool alpha; bool depth; bool stencil; bool premultipliedAlpha; bool antialias; bool preserveDrawingBuffer; }; class WebGLContext : public nsIDOMWebGLRenderingContext, public nsICanvasRenderingContextInternal, public nsSupportsWeakReference, public nsITimerCallback, public WebGLRectangleObject, public nsWrapperCache { friend class WebGLMemoryMultiReporterWrapper; friend class WebGLExtensionLoseContext; friend class WebGLExtensionCompressedTextureS3TC; friend class WebGLContextUserData; friend class WebGLMemoryPressureObserver; public: WebGLContext(); virtual ~WebGLContext(); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext, nsIDOMWebGLRenderingContext) nsINode* GetParentObject() { return mCanvasElement; } virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap); NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT NS_DECL_NSITIMERCALLBACK // nsICanvasRenderingContextInternal NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height); NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, PRInt32 width, PRInt32 height) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Reset() { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter f, PRUint32 aFlags = RenderFlagPremultAlpha); NS_IMETHOD GetInputStream(const char* aMimeType, const PRUnichar* aEncoderOptions, nsIInputStream **aStream); NS_IMETHOD GetThebesSurface(gfxASurface **surface); mozilla::TemporaryRef GetSurfaceSnapshot() { return nsnull; } NS_IMETHOD SetIsOpaque(bool b) { return NS_OK; }; NS_IMETHOD SetContextOptions(nsIPropertyBag *aOptions); NS_IMETHOD SetIsIPC(bool b) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack, PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Swap(PRUint32 nativeID, PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h) { return NS_ERROR_NOT_IMPLEMENTED; } bool LoseContext(); bool RestoreContext(); void SynthesizeGLError(WebGLenum err); void SynthesizeGLError(WebGLenum err, const char *fmt, ...); void ErrorInvalidEnum(const char *fmt = 0, ...); void ErrorInvalidOperation(const char *fmt = 0, ...); void ErrorInvalidValue(const char *fmt = 0, ...); void ErrorInvalidFramebufferOperation(const char *fmt = 0, ...); void ErrorInvalidEnumInfo(const char *info, WebGLenum enumvalue) { return ErrorInvalidEnum("%s: invalid enum value 0x%x", info, enumvalue); } void ErrorOutOfMemory(const char *fmt = 0, ...); const char *ErrorName(GLenum error); bool IsTextureFormatCompressed(GLenum format); void DummyFramebufferOperation(const char *info); WebGLTexture *activeBoundTextureForTarget(WebGLenum target) { return target == LOCAL_GL_TEXTURE_2D ? mBound2DTextures[mActiveTexture] : mBoundCubeMapTextures[mActiveTexture]; } already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder, CanvasLayer *aOldLayer, LayerManager *aManager); void MarkContextClean() { mInvalidated = false; } // a number that increments every time we have an event that causes // all context resources to be lost. uint32_t Generation() { return mGeneration.value(); } const WebGLRectangleObject *FramebufferRectangleObject() const; // this is similar to GLContext::ClearSafely, but is more comprehensive // (takes care of scissor, stencil write mask, dithering, viewport...) // WebGL has more complex needs than GLContext as content controls GL state. void ForceClearFramebufferWithDefaultValues(uint32_t mask, const nsIntRect& viewportRect); // if the preserveDrawingBuffer context option is false, we need to clear the back buffer // after it's been presented to the compositor. This function does that if needed. // See section 2.2 in the WebGL spec. void EnsureBackbufferClearedAsNeeded(); // checks for GL errors, clears any pending GL error, stores the current GL error in currentGLError, // and copies it into mWebGLError if it doesn't already have an error set void UpdateWebGLErrorAndClearGLError(GLenum *currentGLError) { // get and clear GL error in ALL cases *currentGLError = gl->GetAndClearError(); // only store in mWebGLError if is hasn't already recorded an error if (!mWebGLError) mWebGLError = *currentGLError; } // checks for GL errors, clears any pending GL error, // and stores the current GL error into mWebGLError if it doesn't already have an error set void UpdateWebGLErrorAndClearGLError() { GLenum currentGLError; UpdateWebGLErrorAndClearGLError(¤tGLError); } bool MinCapabilityMode() const { return mMinCapability; } void SetupContextLossTimer() { // If the timer was already running, don't restart it here. Instead, // wait until the previous call is done, then fire it one more time. // This is an optimization to prevent unnecessary cross-communication // between threads. if (mContextLossTimerRunning) { mDrawSinceContextLossTimerSet = true; return; } mContextRestorer->InitWithCallback(static_cast(this), PR_MillisecondsToInterval(1000), nsITimer::TYPE_ONE_SHOT); mContextLossTimerRunning = true; mDrawSinceContextLossTimerSet = false; } void TerminateContextLossTimer() { if (mContextLossTimerRunning) { mContextRestorer->Cancel(); mContextLossTimerRunning = false; } } // WebIDL WebGLRenderingContext API nsHTMLCanvasElement* GetCanvas() const { return mCanvasElement; } WebGLsizei GetDrawingBufferWidth() const { if (!IsContextStable()) return 0; return mWidth; } WebGLsizei GetDrawingBufferHeight() const { if (!IsContextStable()) return 0; return mHeight; } JSObject *GetContextAttributes(ErrorResult &rv); bool IsContextLost() const { return !IsContextStable(); } void GetSupportedExtensions(dom::Nullable< nsTArray > &retval); nsIWebGLExtension* GetExtension(const nsAString& aName); void ActiveTexture(WebGLenum texture); void AttachShader(WebGLProgram* program, WebGLShader* shader); void BindAttribLocation(WebGLProgram* program, WebGLuint location, const nsAString& name); void BindBuffer(WebGLenum target, WebGLBuffer* buf); void BindFramebuffer(WebGLenum target, WebGLFramebuffer* wfb); void BindRenderbuffer(WebGLenum target, WebGLRenderbuffer* wrb); void BindTexture(WebGLenum target, WebGLTexture *tex); void BlendColor(WebGLclampf r, WebGLclampf g, WebGLclampf b, WebGLclampf a) { if (!IsContextStable()) return; MakeContextCurrent(); gl->fBlendColor(r, g, b, a); } void BlendEquation(WebGLenum mode); void BlendEquationSeparate(WebGLenum modeRGB, WebGLenum modeAlpha); void BlendFunc(WebGLenum sfactor, WebGLenum dfactor); void BlendFuncSeparate(WebGLenum srcRGB, WebGLenum dstRGB, WebGLenum srcAlpha, WebGLenum dstAlpha); void BufferData(WebGLenum target, WebGLsizeiptr size, WebGLenum usage); void BufferData(WebGLenum target, dom::ArrayBufferView &data, WebGLenum usage); void BufferData(WebGLenum target, dom::ArrayBuffer *data, WebGLenum usage); void BufferSubData(WebGLenum target, WebGLsizeiptr byteOffset, dom::ArrayBufferView &data); void BufferSubData(WebGLenum target, WebGLsizeiptr byteOffset, dom::ArrayBuffer *data); WebGLenum CheckFramebufferStatus(WebGLenum target); void Clear(WebGLbitfield mask); void ClearColor(WebGLclampf r, WebGLclampf g, WebGLclampf b, WebGLclampf a); void ClearDepth(WebGLclampf v); void ClearStencil(WebGLint v); void ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a); void CompileShader(WebGLShader *shader); void CompressedTexImage2D(WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLsizei width, WebGLsizei height, WebGLint border, dom::ArrayBufferView& view); void CompressedTexSubImage2D(WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLsizei width, WebGLsizei height, WebGLenum format, dom::ArrayBufferView& view); void CopyTexImage2D(WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height, WebGLint border); void CopyTexSubImage2D(WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height); already_AddRefed CreateBuffer(); already_AddRefed CreateFramebuffer(); already_AddRefed CreateProgram(); already_AddRefed CreateRenderbuffer(); already_AddRefed CreateTexture(); already_AddRefed CreateShader(WebGLenum type); void CullFace(WebGLenum face); void DeleteBuffer(WebGLBuffer *buf); void DeleteFramebuffer(WebGLFramebuffer *fbuf); void DeleteProgram(WebGLProgram *prog); void DeleteRenderbuffer(WebGLRenderbuffer *rbuf); void DeleteShader(WebGLShader *shader); void DeleteTexture(WebGLTexture *tex); void DepthFunc(WebGLenum func); void DepthMask(WebGLboolean b); void DepthRange(WebGLclampf zNear, WebGLclampf zFar); void DetachShader(WebGLProgram *program, WebGLShader *shader); void Disable(WebGLenum cap); void DisableVertexAttribArray(WebGLuint index); void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count); void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, WebGLintptr byteOffset); void Enable(WebGLenum cap); void EnableVertexAttribArray(WebGLuint index); void Flush() { if (!IsContextStable()) return; MakeContextCurrent(); gl->fFlush(); } void Finish() { if (!IsContextStable()) return; MakeContextCurrent(); gl->fFinish(); } void FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, WebGLRenderbuffer *wrb); void FramebufferTexture2D(WebGLenum target, WebGLenum attachment, WebGLenum textarget, WebGLTexture *tobj, WebGLint level); void FrontFace(WebGLenum mode); void GenerateMipmap(WebGLenum target); already_AddRefed GetActiveAttrib(WebGLProgram *prog, WebGLuint index); already_AddRefed GetActiveUniform(WebGLProgram *prog, WebGLuint index); void GetAttachedShaders(WebGLProgram* prog, dom::Nullable< nsTArray > &retval); WebGLint GetAttribLocation(WebGLProgram* prog, const nsAString& name); JS::Value GetBufferParameter(WebGLenum target, WebGLenum pname); JS::Value GetBufferParameter(JSContext* /* unused */, WebGLenum target, WebGLenum pname) { return GetBufferParameter(target, pname); } JS::Value GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv); WebGLenum GetError(); JS::Value GetFramebufferAttachmentParameter(JSContext* cx, WebGLenum target, WebGLenum attachment, WebGLenum pname, ErrorResult& rv); JS::Value GetProgramParameter(WebGLProgram *prog, WebGLenum pname); JS::Value GetProgramParameter(JSContext* /* unused */, WebGLProgram *prog, WebGLenum pname) { return GetProgramParameter(prog, pname); } void GetProgramInfoLog(WebGLProgram *prog, nsACString& retval, ErrorResult& rv); void GetProgramInfoLog(WebGLProgram *prog, nsAString& retval, ErrorResult& rv); JS::Value GetRenderbufferParameter(WebGLenum target, WebGLenum pname); JS::Value GetRenderbufferParameter(JSContext* /* unused */, WebGLenum target, WebGLenum pname) { return GetRenderbufferParameter(target, pname); } JS::Value GetShaderParameter(WebGLShader *shader, WebGLenum pname); JS::Value GetShaderParameter(JSContext* /* unused */, WebGLShader *shader, WebGLenum pname) { return GetShaderParameter(shader, pname); } already_AddRefed GetShaderPrecisionFormat(WebGLenum shadertype, WebGLenum precisiontype); void GetShaderInfoLog(WebGLShader *shader, nsACString& retval, ErrorResult& rv); void GetShaderInfoLog(WebGLShader *shader, nsAString& retval, ErrorResult& rv); void GetShaderSource(WebGLShader *shader, nsAString& retval); JS::Value GetTexParameter(WebGLenum target, WebGLenum pname); JS::Value GetTexParameter(JSContext * /* unused */, WebGLenum target, WebGLenum pname) { return GetTexParameter(target, pname); } JS::Value GetUniform(JSContext* cx, WebGLProgram *prog, WebGLUniformLocation *location, ErrorResult& rv); already_AddRefed GetUniformLocation(WebGLProgram *prog, const nsAString& name); JS::Value GetVertexAttrib(JSContext* cx, WebGLuint index, WebGLenum pname, ErrorResult& rv); WebGLsizeiptr GetVertexAttribOffset(WebGLuint index, WebGLenum pname); void Hint(WebGLenum target, WebGLenum mode); bool IsBuffer(WebGLBuffer *buffer); bool IsEnabled(WebGLenum cap); bool IsFramebuffer(WebGLFramebuffer *fb); bool IsProgram(WebGLProgram *prog); bool IsRenderbuffer(WebGLRenderbuffer *rb); bool IsShader(WebGLShader *shader); bool IsTexture(WebGLTexture *tex); void LineWidth(WebGLfloat width) { if (!IsContextStable()) return; MakeContextCurrent(); gl->fLineWidth(width); } void LinkProgram(WebGLProgram *program, ErrorResult& rv); void PixelStorei(WebGLenum pname, WebGLint param); void PolygonOffset(WebGLfloat factor, WebGLfloat units) { if (!IsContextStable()) return; MakeContextCurrent(); gl->fPolygonOffset(factor, units); } void ReadPixels(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type, dom::ArrayBufferView* pixels, ErrorResult& rv); void RenderbufferStorage(WebGLenum target, WebGLenum internalformat, WebGLsizei width, WebGLsizei height); void SampleCoverage(WebGLclampf value, WebGLboolean invert) { if (!IsContextStable()) return; MakeContextCurrent(); gl->fSampleCoverage(value, invert); } void Scissor(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height); void ShaderSource(WebGLShader *shader, const nsAString& source); void StencilFunc(WebGLenum func, WebGLint ref, WebGLuint mask); void StencilFuncSeparate(WebGLenum face, WebGLenum func, WebGLint ref, WebGLuint mask); void StencilMask(WebGLuint mask); void StencilMaskSeparate(WebGLenum face, WebGLuint mask); void StencilOp(WebGLenum sfail, WebGLenum dpfail, WebGLenum dppass); void StencilOpSeparate(WebGLenum face, WebGLenum sfail, WebGLenum dpfail, WebGLenum dppass); void TexImage2D(JSContext* cx, WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLsizei width, WebGLsizei height, WebGLint border, WebGLenum format, WebGLenum type, dom::ArrayBufferView *pixels, ErrorResult& rv); void TexImage2D(JSContext* cx, WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLenum format, WebGLenum type, dom::ImageData* pixels, ErrorResult& rv); // Allow whatever element types the bindings are willing to pass // us in TexImage2D template void TexImage2D(JSContext* /* unused */, WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLenum format, WebGLenum type, ElementType* elt, ErrorResult& rv) { if (!IsContextStable()) return; nsRefPtr isurf; WebGLTexelFormat srcFormat; nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt); rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf), &srcFormat); if (rv.Failed()) return; uint32_t byteLength = isurf->Stride() * isurf->Height(); return TexImage2D_base(target, level, internalformat, isurf->Width(), isurf->Height(), isurf->Stride(), 0, format, type, isurf->Data(), byteLength, -1, srcFormat, mPixelStorePremultiplyAlpha); } void TexParameterf(WebGLenum target, WebGLenum pname, WebGLfloat param) { TexParameter_base(target, pname, nsnull, ¶m); } void TexParameteri(WebGLenum target, WebGLenum pname, WebGLint param) { TexParameter_base(target, pname, ¶m, nsnull); } void TexSubImage2D(JSContext* cx, WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type, dom::ArrayBufferView* pixels, ErrorResult& rv); void TexSubImage2D(JSContext* cx, WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLenum format, WebGLenum type, dom::ImageData* pixels, ErrorResult& rv); // Allow whatever element types the bindings are willing to pass // us in TexSubImage2D template void TexSubImage2D(JSContext* /* unused */, WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLenum format, WebGLenum type, ElementType* elt, ErrorResult& rv) { if (!IsContextStable()) return; nsRefPtr isurf; WebGLTexelFormat srcFormat; nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt); rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf), &srcFormat); if (rv.Failed()) return; uint32_t byteLength = isurf->Stride() * isurf->Height(); return TexSubImage2D_base(target, level, xoffset, yoffset, isurf->Width(), isurf->Height(), isurf->Stride(), format, type, isurf->Data(), byteLength, -1, srcFormat, mPixelStorePremultiplyAlpha); } void Uniform1i(WebGLUniformLocation* location, WebGLint x); void Uniform2i(WebGLUniformLocation* location, WebGLint x, WebGLint y); void Uniform3i(WebGLUniformLocation* location, WebGLint x, WebGLint y, WebGLint z); void Uniform4i(WebGLUniformLocation* location, WebGLint x, WebGLint y, WebGLint z, WebGLint w); void Uniform1f(WebGLUniformLocation* location, WebGLfloat x); void Uniform2f(WebGLUniformLocation* location, WebGLfloat x, WebGLfloat y); void Uniform3f(WebGLUniformLocation* location, WebGLfloat x, WebGLfloat y, WebGLfloat z); void Uniform4f(WebGLUniformLocation* location, WebGLfloat x, WebGLfloat y, WebGLfloat z, WebGLfloat w); void Uniform1iv(WebGLUniformLocation* location, dom::Int32Array& arr) { Uniform1iv_base(location, arr.mLength, arr.mData); } void Uniform1iv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform1iv_base(location, arr.Length(), arr.Elements()); } void Uniform1iv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLint* data); void Uniform2iv(WebGLUniformLocation* location, dom::Int32Array& arr) { Uniform2iv_base(location, arr.mLength, arr.mData); } void Uniform2iv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform2iv_base(location, arr.Length(), arr.Elements()); } void Uniform2iv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLint* data); void Uniform3iv(WebGLUniformLocation* location, dom::Int32Array& arr) { Uniform3iv_base(location, arr.mLength, arr.mData); } void Uniform3iv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform3iv_base(location, arr.Length(), arr.Elements()); } void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLint* data); void Uniform4iv(WebGLUniformLocation* location, dom::Int32Array& arr) { Uniform4iv_base(location, arr.mLength, arr.mData); } void Uniform4iv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform4iv_base(location, arr.Length(), arr.Elements()); } void Uniform4iv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLint* data); void Uniform1fv(WebGLUniformLocation* location, dom::Float32Array& arr) { Uniform1fv_base(location, arr.mLength, arr.mData); } void Uniform1fv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform1fv_base(location, arr.Length(), arr.Elements()); } void Uniform1fv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLfloat* data); void Uniform2fv(WebGLUniformLocation* location, dom::Float32Array& arr) { Uniform2fv_base(location, arr.mLength, arr.mData); } void Uniform2fv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform2fv_base(location, arr.Length(), arr.Elements()); } void Uniform2fv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLfloat* data); void Uniform3fv(WebGLUniformLocation* location, dom::Float32Array& arr) { Uniform3fv_base(location, arr.mLength, arr.mData); } void Uniform3fv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform3fv_base(location, arr.Length(), arr.Elements()); } void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLfloat* data); void Uniform4fv(WebGLUniformLocation* location, dom::Float32Array& arr) { Uniform4fv_base(location, arr.mLength, arr.mData); } void Uniform4fv(WebGLUniformLocation* location, const dom::Sequence& arr) { Uniform4fv_base(location, arr.Length(), arr.Elements()); } void Uniform4fv_base(WebGLUniformLocation* location, uint32_t arrayLength, const WebGLfloat* data); void UniformMatrix2fv(WebGLUniformLocation* location, WebGLboolean transpose, dom::Float32Array &value) { UniformMatrix2fv_base(location, transpose, value.mLength, value.mData); } void UniformMatrix2fv(WebGLUniformLocation* location, WebGLboolean transpose, const dom::Sequence &value) { UniformMatrix2fv_base(location, transpose, value.Length(), value.Elements()); } void UniformMatrix2fv_base(WebGLUniformLocation* location, WebGLboolean transpose, uint32_t arrayLength, const float* data); void UniformMatrix3fv(WebGLUniformLocation* location, WebGLboolean transpose, dom::Float32Array &value) { UniformMatrix3fv_base(location, transpose, value.mLength, value.mData); } void UniformMatrix3fv(WebGLUniformLocation* location, WebGLboolean transpose, const dom::Sequence &value) { UniformMatrix3fv_base(location, transpose, value.Length(), value.Elements()); } void UniformMatrix3fv_base(WebGLUniformLocation* location, WebGLboolean transpose, uint32_t arrayLength, const float* data); void UniformMatrix4fv(WebGLUniformLocation* location, WebGLboolean transpose, dom::Float32Array &value) { UniformMatrix4fv_base(location, transpose, value.mLength, value.mData); } void UniformMatrix4fv(WebGLUniformLocation* location, WebGLboolean transpose, const dom::Sequence &value) { UniformMatrix4fv_base(location, transpose, value.Length(), value.Elements()); } void UniformMatrix4fv_base(WebGLUniformLocation* location, WebGLboolean transpose, uint32_t arrayLength, const float* data); void UseProgram(WebGLProgram *prog); void ValidateProgram(WebGLProgram *prog); void VertexAttrib1f(WebGLuint index, WebGLfloat x0); void VertexAttrib2f(WebGLuint index, WebGLfloat x0, WebGLfloat x1); void VertexAttrib3f(WebGLuint index, WebGLfloat x0, WebGLfloat x1, WebGLfloat x2); void VertexAttrib4f(WebGLuint index, WebGLfloat x0, WebGLfloat x1, WebGLfloat x2, WebGLfloat x3); void VertexAttrib1fv(WebGLuint idx, dom::Float32Array &arr) { VertexAttrib1fv_base(idx, arr.mLength, arr.mData); } void VertexAttrib1fv(WebGLuint idx, const dom::Sequence& arr) { VertexAttrib1fv_base(idx, arr.Length(), arr.Elements()); } void VertexAttrib1fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr); void VertexAttrib2fv(WebGLuint idx, dom::Float32Array &arr) { VertexAttrib2fv_base(idx, arr.mLength, arr.mData); } void VertexAttrib2fv(WebGLuint idx, const dom::Sequence& arr) { VertexAttrib2fv_base(idx, arr.Length(), arr.Elements()); } void VertexAttrib2fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr); void VertexAttrib3fv(WebGLuint idx, dom::Float32Array &arr) { VertexAttrib3fv_base(idx, arr.mLength, arr.mData); } void VertexAttrib3fv(WebGLuint idx, const dom::Sequence& arr) { VertexAttrib3fv_base(idx, arr.Length(), arr.Elements()); } void VertexAttrib3fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr); void VertexAttrib4fv(WebGLuint idx, dom::Float32Array &arr) { VertexAttrib4fv_base(idx, arr.mLength, arr.mData); } void VertexAttrib4fv(WebGLuint idx, const dom::Sequence& arr) { VertexAttrib4fv_base(idx, arr.Length(), arr.Elements()); } void VertexAttrib4fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr); void VertexAttribPointer(WebGLuint index, WebGLint size, WebGLenum type, WebGLboolean normalized, WebGLsizei stride, WebGLintptr byteOffset); void Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height); protected: void SetDontKnowIfNeedFakeBlack() { mFakeBlackStatus = DontKnowIfNeedFakeBlack; } bool NeedFakeBlack(); void BindFakeBlackTextures(); void UnbindFakeBlackTextures(); int WhatDoesVertexAttrib0Need(); bool DoFakeVertexAttrib0(WebGLuint vertexCount); void UndoFakeVertexAttrib0(); void InvalidateFakeVertexAttrib0(); static CheckedUint32 GetImageSize(WebGLsizei height, WebGLsizei width, uint32_t pixelSize, uint32_t alignment); // Returns x rounded to the next highest multiple of y. static CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) { return ((x + y - 1) / y) * y; } nsRefPtr gl; CheckedUint32 mGeneration; WebGLContextOptions mOptions; bool mInvalidated; bool mResetLayer; bool mOptionsFrozen; bool mMinCapability; bool mDisableExtensions; bool mHasRobustness; template void DeleteWebGLObjectsArray(nsTArray& array); WebGLuint mActiveTexture; WebGLenum mWebGLError; // whether shader validation is supported bool mShaderValidation; // some GL constants int32_t mGLMaxVertexAttribs; int32_t mGLMaxTextureUnits; int32_t mGLMaxTextureSize; int32_t mGLMaxCubeMapTextureSize; int32_t mGLMaxTextureImageUnits; int32_t mGLMaxVertexTextureImageUnits; int32_t mGLMaxVaryingVectors; int32_t mGLMaxFragmentUniformVectors; int32_t mGLMaxVertexUniformVectors; // Represents current status, or state, of the context. That is, is it lost // or stable and what part of the context lost process are we currently at. // This is used to support the WebGL spec's asyncronous nature in handling // context loss. enum ContextStatus { // The context is stable; there either are none or we don't know of any. ContextStable, // The context has been lost, but we have not yet sent an event to the // script informing it of this. ContextLostAwaitingEvent, // The context has been lost, and we have sent the script an event // informing it of this. ContextLost, // The context is lost, an event has been sent to the script, and the // script correctly handled the event. We are waiting for the context to // be restored. ContextLostAwaitingRestore }; // extensions enum WebGLExtensionID { WebGL_OES_texture_float, WebGL_OES_standard_derivatives, WebGL_EXT_texture_filter_anisotropic, WebGL_WEBGL_lose_context, WebGL_WEBGL_compressed_texture_s3tc, WebGLExtensionID_Max }; nsAutoTArray, WebGLExtensionID_Max> mEnabledExtensions; bool IsExtensionEnabled(WebGLExtensionID ext) const { NS_ABORT_IF_FALSE(ext >= 0 && ext < WebGLExtensionID_Max, "bogus index!"); return mEnabledExtensions[ext] != nsnull; } bool IsExtensionSupported(WebGLExtensionID ei); nsTArray mCompressedTextureFormats; bool InitAndValidateGL(); bool ValidateBuffers(int32_t *maxAllowedCount, const char *info); bool ValidateCapabilityEnum(WebGLenum cap, const char *info); bool ValidateBlendEquationEnum(WebGLenum cap, const char *info); bool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info); bool ValidateBlendFuncSrcEnum(WebGLenum mode, const char *info); bool ValidateBlendFuncEnumsCompatibility(WebGLenum sfactor, WebGLenum dfactor, const char *info); bool ValidateTextureTargetEnum(WebGLenum target, const char *info); bool ValidateComparisonEnum(WebGLenum target, const char *info); bool ValidateStencilOpEnum(WebGLenum action, const char *info); bool ValidateFaceEnum(WebGLenum face, const char *info); bool ValidateBufferUsageEnum(WebGLenum target, const char *info); bool ValidateTexFormatAndType(WebGLenum format, WebGLenum type, int jsArrayType, uint32_t *texelSize, const char *info); bool ValidateDrawModeEnum(WebGLenum mode, const char *info); bool ValidateAttribIndex(WebGLuint index, const char *info); bool ValidateStencilParamsForDrawCall(); bool ValidateGLSLVariableName(const nsAString& name, const char *info); bool ValidateGLSLCharacter(PRUnichar c); bool ValidateGLSLString(const nsAString& string, const char *info); bool ValidateTexImage2DTarget(WebGLenum target, WebGLsizei width, WebGLsizei height, const char* info); bool ValidateCompressedTextureSize(WebGLint level, WebGLenum format, WebGLsizei width, WebGLsizei height, uint32_t byteLength, const char* info); bool ValidateLevelWidthHeightForTarget(WebGLenum target, WebGLint level, WebGLsizei width, WebGLsizei height, const char* info); static uint32_t GetBitsPerTexel(WebGLenum format, WebGLenum type); void Invalidate(); void DestroyResourcesAndContext(); void MakeContextCurrent() { gl->MakeCurrent(); } // helpers void TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLsizei width, WebGLsizei height, WebGLsizei srcStrideOrZero, WebGLint border, WebGLenum format, WebGLenum type, void *data, uint32_t byteLength, int jsArrayType, WebGLTexelFormat srcFormat, bool srcPremultiplied); void TexSubImage2D_base(WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLsizei width, WebGLsizei height, WebGLsizei srcStrideOrZero, WebGLenum format, WebGLenum type, void *pixels, uint32_t byteLength, int jsArrayType, WebGLTexelFormat srcFormat, bool srcPremultiplied); void TexParameter_base(WebGLenum target, WebGLenum pname, WebGLint *intParamPtr, WebGLfloat *floatParamPtr); void 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); template nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType* aElement) { MOZ_ASSERT(aElement); uint32_t flags = nsLayoutUtils::SFE_WANT_NEW_SURFACE | nsLayoutUtils::SFE_WANT_IMAGE_SURFACE; if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE) flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; if (!mPixelStorePremultiplyAlpha) flags |= nsLayoutUtils::SFE_NO_PREMULTIPLY_ALPHA; return nsLayoutUtils::SurfaceFromElement(aElement, flags); } nsresult SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res, gfxImageSurface **imageOut, WebGLTexelFormat *format); void CopyTexSubImage2D_base(WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLint xoffset, WebGLint yoffset, WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height, bool sub); // Returns false if aObject is null or not valid template bool ValidateObject(const char* info, ObjectType *aObject); // Returns false if aObject is not valid. Considers null to be valid. template bool ValidateObjectAllowNull(const char* info, ObjectType *aObject); // Returns false if aObject is not valid, but considers deleted // objects and null objects valid. template bool ValidateObjectAllowDeletedOrNull(const char* info, ObjectType *aObject); // Returns false if aObject is null or not valid, but considers deleted // objects valid. template bool ValidateObjectAllowDeleted(const char* info, ObjectType *aObject); private: // Like ValidateObject, but only for cases when aObject is known // to not be null already. template bool ValidateObjectAssumeNonNull(const char* info, ObjectType *aObject); protected: int32_t MaxTextureSizeForTarget(WebGLenum target) const { return target == LOCAL_GL_TEXTURE_2D ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize; } /** like glBufferData but if the call may change the buffer size, checks any GL error generated * by this glBufferData call and returns it */ GLenum CheckedBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); /** like glTexImage2D but if the call may change the texture size, checks any GL error generated * by this glTexImage2D call and returns it */ GLenum CheckedTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data); void MaybeRestoreContext(); bool IsContextStable() const { return mContextStatus == ContextStable; } void ForceLoseContext(); void ForceRestoreContext(); // the buffers bound to the current program's attribs nsTArray mAttribBuffers; nsTArray > mBound2DTextures; nsTArray > mBoundCubeMapTextures; WebGLRefPtr mBoundArrayBuffer; WebGLRefPtr mBoundElementArrayBuffer; WebGLRefPtr mCurrentProgram; uint32_t mMaxFramebufferColorAttachments; WebGLRefPtr mBoundFramebuffer; WebGLRefPtr mBoundRenderbuffer; LinkedList mTextures; LinkedList mBuffers; LinkedList mPrograms; LinkedList mShaders; LinkedList mRenderbuffers; LinkedList mFramebuffers; // PixelStore parameters uint32_t mPixelStorePackAlignment, mPixelStoreUnpackAlignment, mPixelStoreColorspaceConversion; bool mPixelStoreFlipY, mPixelStorePremultiplyAlpha; FakeBlackStatus mFakeBlackStatus; WebGLuint mBlackTexture2D, mBlackTextureCubeMap; bool mBlackTexturesAreInitialized; WebGLfloat mVertexAttrib0Vector[4]; WebGLfloat mFakeVertexAttrib0BufferObjectVector[4]; size_t mFakeVertexAttrib0BufferObjectSize; GLuint mFakeVertexAttrib0BufferObject; int mFakeVertexAttrib0BufferStatus; WebGLint mStencilRefFront, mStencilRefBack; WebGLuint mStencilValueMaskFront, mStencilValueMaskBack, mStencilWriteMaskFront, mStencilWriteMaskBack; realGLboolean mColorWriteMask[4]; realGLboolean mDepthWriteMask; realGLboolean mScissorTestEnabled; realGLboolean mDitherEnabled; WebGLfloat mColorClearValue[4]; WebGLint mStencilClearValue; WebGLfloat mDepthClearValue; int mBackbufferClearingStatus; nsCOMPtr mContextRestorer; bool mAllowRestore; bool mContextLossTimerRunning; bool mDrawSinceContextLossTimerSet; ContextStatus mContextStatus; bool mContextLostErrorSet; int mAlreadyGeneratedWarnings; bool ShouldGenerateWarnings() const { return mAlreadyGeneratedWarnings < 32; } #ifdef XP_MACOSX // see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime // Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy // these objects at high frequency. Having WebGLContext's hold one such object seems fine, // because WebGLContext objects only go away during GC, which shouldn't happen too frequently. // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer). ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper; #endif nsRefPtr mMemoryPressureObserver; public: // console logging helpers void GenerateWarning(const char *fmt, ...); void GenerateWarning(const char *fmt, va_list ap); friend class WebGLTexture; friend class WebGLFramebuffer; friend class WebGLRenderbuffer; friend class WebGLProgram; friend class WebGLBuffer; friend class WebGLShader; friend class WebGLUniformLocation; }; // This class is a mixin for objects that are tied to a specific // context (which is to say, all of them). They provide initialization // as well as comparison with the current context. class WebGLContextBoundObject { public: WebGLContextBoundObject(WebGLContext *context) { mContext = context; mContextGeneration = context->Generation(); } bool IsCompatibleWithContext(WebGLContext *other) { return mContext == other && mContextGeneration == other->Generation(); } WebGLContext *Context() const { return mContext; } protected: WebGLContext *mContext; uint32_t mContextGeneration; }; struct WebGLVertexAttribData { // note that these initial values are what GL initializes vertex attribs to WebGLVertexAttribData() : buf(0), stride(0), size(4), byteOffset(0), type(LOCAL_GL_FLOAT), enabled(false), normalized(false) { } WebGLRefPtr buf; WebGLuint stride; WebGLuint size; GLuint byteOffset; GLenum type; bool enabled; bool normalized; GLuint componentSize() const { switch(type) { case LOCAL_GL_BYTE: return sizeof(GLbyte); break; case LOCAL_GL_UNSIGNED_BYTE: return sizeof(GLubyte); break; case LOCAL_GL_SHORT: return sizeof(GLshort); break; case LOCAL_GL_UNSIGNED_SHORT: return sizeof(GLushort); break; // XXX case LOCAL_GL_FIXED: case LOCAL_GL_FLOAT: return sizeof(GLfloat); break; default: NS_ERROR("Should never get here!"); return 0; } } GLuint actualStride() const { if (stride) return stride; return size * componentSize(); } }; // NOTE: When this class is switched to new DOM bindings, update the // (then-slow) WrapObject calls in GetParameter and GetVertexAttrib. class WebGLBuffer MOZ_FINAL : public nsIWebGLBuffer , public WebGLRefCountedObject , public LinkedListElement , public WebGLContextBoundObject { public: WebGLBuffer(WebGLContext *context) : WebGLContextBoundObject(context) , mHasEverBeenBound(false) , mByteLength(0) , mTarget(LOCAL_GL_NONE) , mData(nsnull) { mContext->MakeContextCurrent(); mContext->gl->fGenBuffers(1, &mGLName); mContext->mBuffers.insertBack(this); } ~WebGLBuffer() { DeleteOnce(); } void Delete() { mContext->MakeContextCurrent(); mContext->gl->fDeleteBuffers(1, &mGLName); free(mData); mData = nsnull; mByteLength = 0; LinkedListElement::remove(); // remove from mContext->mBuffers } size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const { return aMallocSizeOf(this) + aMallocSizeOf(mData); } bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } GLuint GLName() const { return mGLName; } GLuint ByteLength() const { return mByteLength; } GLenum Target() const { return mTarget; } const void *Data() const { return mData; } void SetByteLength(GLuint byteLength) { mByteLength = byteLength; } void SetTarget(GLenum target) { mTarget = target; } // element array buffers are the only buffers for which we need to keep a copy of the data. // this method assumes that the byte length has previously been set by calling SetByteLength. bool CopyDataIfElementArray(const void* data) { if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { mData = realloc(mData, mByteLength); if (!mData) { mByteLength = 0; return false; } memcpy(mData, data, mByteLength); } return true; } // same comments as for CopyElementArrayData bool ZeroDataIfElementArray() { if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { mData = realloc(mData, mByteLength); if (!mData) { mByteLength = 0; return false; } memset(mData, 0, mByteLength); } return true; } // same comments as for CopyElementArrayData void CopySubDataIfElementArray(GLuint byteOffset, GLuint byteLength, const void* data) { if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER && mByteLength) { memcpy((void*) (size_t(mData)+byteOffset), data, byteLength); } } // this method too is only for element array buffers. It returns the maximum value in the part of // the buffer starting at given offset, consisting of given count of elements. The type T is the type // to interpret the array elements as, must be GLushort or GLubyte. template T FindMaxElementInSubArray(GLuint count, GLuint byteOffset) { const T* start = reinterpret_cast(reinterpret_cast(mData) + byteOffset); const T* stop = start + count; T result = 0; for(const T* ptr = start; ptr != stop; ++ptr) { if (*ptr > result) result = *ptr; } return result; } void InvalidateCachedMaxElements() { mHasCachedMaxUbyteElement = false; mHasCachedMaxUshortElement = false; } int32_t FindMaxUbyteElement() { if (mHasCachedMaxUbyteElement) { return mCachedMaxUbyteElement; } else { mHasCachedMaxUbyteElement = true; mCachedMaxUbyteElement = FindMaxElementInSubArray(mByteLength, 0); return mCachedMaxUbyteElement; } } int32_t FindMaxUshortElement() { if (mHasCachedMaxUshortElement) { return mCachedMaxUshortElement; } else { mHasCachedMaxUshortElement = true; mCachedMaxUshortElement = FindMaxElementInSubArray(mByteLength>>1, 0); return mCachedMaxUshortElement; } } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLBUFFER protected: WebGLuint mGLName; bool mHasEverBeenBound; GLuint mByteLength; GLenum mTarget; uint8_t mCachedMaxUbyteElement; bool mHasCachedMaxUbyteElement; uint16_t mCachedMaxUshortElement; bool mHasCachedMaxUshortElement; void* mData; // in the case of an Element Array Buffer, we keep a copy. }; // NOTE: When this class is switched to new DOM bindings, update the (then-slow) // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter. class WebGLTexture MOZ_FINAL : public nsIWebGLTexture , public WebGLRefCountedObject , public LinkedListElement , public WebGLContextBoundObject { public: WebGLTexture(WebGLContext *context) : WebGLContextBoundObject(context) , mHasEverBeenBound(false) , mTarget(0) , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR) , mMagFilter(LOCAL_GL_LINEAR) , mWrapS(LOCAL_GL_REPEAT) , mWrapT(LOCAL_GL_REPEAT) , mFacesCount(0) , mMaxLevelWithCustomImages(0) , mHaveGeneratedMipmap(false) , mFakeBlackStatus(DoNotNeedFakeBlack) { mContext->MakeContextCurrent(); mContext->gl->fGenTextures(1, &mGLName); mContext->mTextures.insertBack(this); } ~WebGLTexture() { DeleteOnce(); } void Delete() { mImageInfos.Clear(); mContext->MakeContextCurrent(); mContext->gl->fDeleteTextures(1, &mGLName); LinkedListElement::remove(); // remove from mContext->mTextures } bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } WebGLuint GLName() { return mGLName; } GLenum Target() const { return mTarget; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLTEXTURE protected: friend class WebGLContext; friend class WebGLFramebuffer; bool mHasEverBeenBound; WebGLuint mGLName; // we store information about the various images that are part of // this texture (cubemap faces, mipmap levels) public: class ImageInfo : public WebGLRectangleObject { public: ImageInfo() : mFormat(0) , mType(0) , mIsDefined(false) {} ImageInfo(WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type) : WebGLRectangleObject(width, height) , mFormat(format) , mType(type) , mIsDefined(true) {} bool operator==(const ImageInfo& a) const { return mIsDefined == a.mIsDefined && mWidth == a.mWidth && mHeight == a.mHeight && mFormat == a.mFormat && mType == a.mType; } bool operator!=(const ImageInfo& a) const { return !(*this == a); } bool IsSquare() const { return mWidth == mHeight; } bool IsPositive() const { return mWidth > 0 && mHeight > 0; } bool IsPowerOfTwo() const { return is_pot_assuming_nonnegative(mWidth) && is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...) } int64_t MemoryUsage() const { if (!mIsDefined) return 0; int64_t texelSizeInBits = WebGLContext::GetBitsPerTexel(mFormat, mType); return int64_t(mWidth) * int64_t(mHeight) * texelSizeInBits / 8; } WebGLenum Format() const { return mFormat; } WebGLenum Type() const { return mType; } protected: WebGLenum mFormat, mType; bool mIsDefined; friend class WebGLTexture; }; ImageInfo& ImageInfoAt(size_t level, size_t face = 0) { #ifdef DEBUG if (face >= mFacesCount) NS_ERROR("wrong face index, must be 0 for TEXTURE_2D and at most 5 for cube maps"); #endif // no need to check level as a wrong value would be caught by ElementAt(). return mImageInfos.ElementAt(level * mFacesCount + face); } const ImageInfo& ImageInfoAt(size_t level, size_t face) const { return const_cast(this)->ImageInfoAt(level, face); } bool HasImageInfoAt(size_t level, size_t face) const { CheckedUint32 checked_index = CheckedUint32(level) * mFacesCount + face; return checked_index.isValid() && checked_index.value() < mImageInfos.Length() && ImageInfoAt(level, face).mIsDefined; } static size_t FaceForTarget(WebGLenum target) { return target == LOCAL_GL_TEXTURE_2D ? 0 : target - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; } int64_t MemoryUsage() const { if (IsDeleted()) return 0; int64_t result = 0; for(size_t face = 0; face < mFacesCount; face++) { if (mHaveGeneratedMipmap) { // Each mipmap level is 1/4 the size of the previous level // 1 + x + x^2 + ... = 1/(1-x) // for x = 1/4, we get 1/(1-1/4) = 4/3 result += ImageInfoAt(0, face).MemoryUsage() * 4 / 3; } else { for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) result += ImageInfoAt(level, face).MemoryUsage(); } } return result; } protected: WebGLenum mTarget; WebGLenum mMinFilter, mMagFilter, mWrapS, mWrapT; size_t mFacesCount, mMaxLevelWithCustomImages; nsTArray mImageInfos; bool mHaveGeneratedMipmap; FakeBlackStatus mFakeBlackStatus; void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) { mMaxLevelWithCustomImages = NS_MAX(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages); mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount); } bool CheckFloatTextureFilterParams() const { // Without OES_texture_float_linear, only NEAREST and NEAREST_MIMPAMP_NEAREST are supported return (mMagFilter == LOCAL_GL_NEAREST) && (mMinFilter == LOCAL_GL_NEAREST || mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST); } bool AreBothWrapModesClampToEdge() const { return mWrapS == LOCAL_GL_CLAMP_TO_EDGE && mWrapT == LOCAL_GL_CLAMP_TO_EDGE; } bool DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(size_t face) const { if (mHaveGeneratedMipmap) return true; ImageInfo expected = ImageInfoAt(0, face); // checks if custom level>0 images are all defined up to the highest level defined // and have the expected dimensions for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { const ImageInfo& actual = ImageInfoAt(level, face); if (actual != expected) return false; expected.mWidth = NS_MAX(1, expected.mWidth >> 1); expected.mHeight = NS_MAX(1, expected.mHeight >> 1); // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence // of extra useless levels. if (actual.mWidth == 1 && actual.mHeight == 1) return true; } // if we're here, we've exhausted all levels without finding a 1x1 image return false; } public: void SetDontKnowIfNeedFakeBlack() { mFakeBlackStatus = DontKnowIfNeedFakeBlack; mContext->SetDontKnowIfNeedFakeBlack(); } void Bind(WebGLenum aTarget) { // this function should only be called by bindTexture(). // it assumes that the GL context is already current. bool firstTimeThisTextureIsBound = !mHasEverBeenBound; if (!firstTimeThisTextureIsBound && aTarget != mTarget) { mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target"); // very important to return here before modifying texture state! This was the place when I lost a whole day figuring // very strange 'invalid write' crashes. return; } mTarget = aTarget; mContext->gl->fBindTexture(mTarget, mGLName); if (firstTimeThisTextureIsBound) { mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6; EnsureMaxLevelWithCustomImagesAtLeast(0); SetDontKnowIfNeedFakeBlack(); // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not // present in GLES 2, but is present in GL and it seems as if for cube maps // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior. if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES2()) mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); } mHasEverBeenBound = true; } void SetImageInfo(WebGLenum aTarget, WebGLint aLevel, WebGLsizei aWidth, WebGLsizei aHeight, WebGLenum aFormat, WebGLenum aType) { if ( (aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D) ) return; size_t face = FaceForTarget(aTarget); EnsureMaxLevelWithCustomImagesAtLeast(aLevel); ImageInfoAt(aLevel, face) = ImageInfo(aWidth, aHeight, aFormat, aType); if (aLevel > 0) SetCustomMipmap(); SetDontKnowIfNeedFakeBlack(); } void SetMinFilter(WebGLenum aMinFilter) { mMinFilter = aMinFilter; SetDontKnowIfNeedFakeBlack(); } void SetMagFilter(WebGLenum aMagFilter) { mMagFilter = aMagFilter; SetDontKnowIfNeedFakeBlack(); } void SetWrapS(WebGLenum aWrapS) { mWrapS = aWrapS; SetDontKnowIfNeedFakeBlack(); } void SetWrapT(WebGLenum aWrapT) { mWrapT = aWrapT; SetDontKnowIfNeedFakeBlack(); } WebGLenum MinFilter() const { return mMinFilter; } bool DoesMinFilterRequireMipmap() const { return !(mMinFilter == LOCAL_GL_NEAREST || mMinFilter == LOCAL_GL_LINEAR); } void SetGeneratedMipmap() { if (!mHaveGeneratedMipmap) { mHaveGeneratedMipmap = true; SetDontKnowIfNeedFakeBlack(); } } void SetCustomMipmap() { if (mHaveGeneratedMipmap) { // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode, // we need to compute now all the mipmap image info. // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info, // and are power-of-two. ImageInfo imageInfo = ImageInfoAt(0, 0); NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?"); WebGLsizei size = NS_MAX(imageInfo.mWidth, imageInfo.mHeight); // so, the size is a power of two, let's find its log in base 2. size_t maxLevel = 0; for (WebGLsizei n = size; n > 1; n >>= 1) ++maxLevel; EnsureMaxLevelWithCustomImagesAtLeast(maxLevel); for (size_t level = 1; level <= maxLevel; ++level) { // again, since the sizes are powers of two, no need for any max(1,x) computation imageInfo.mWidth >>= 1; imageInfo.mHeight >>= 1; for(size_t face = 0; face < mFacesCount; ++face) ImageInfoAt(level, face) = imageInfo; } } mHaveGeneratedMipmap = false; } bool IsFirstImagePowerOfTwo() const { return ImageInfoAt(0, 0).IsPowerOfTwo(); } bool AreAllLevel0ImageInfosEqual() const { for (size_t face = 1; face < mFacesCount; ++face) { if (ImageInfoAt(0, face) != ImageInfoAt(0, 0)) return false; } return true; } bool IsMipmapTexture2DComplete() const { if (mTarget != LOCAL_GL_TEXTURE_2D) return false; if (!ImageInfoAt(0, 0).IsPositive()) return false; if (mHaveGeneratedMipmap) return true; return DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(0); } bool IsCubeComplete() const { if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP) return false; const ImageInfo &first = ImageInfoAt(0, 0); if (!first.IsPositive() || !first.IsSquare()) return false; return AreAllLevel0ImageInfosEqual(); } bool IsMipmapCubeComplete() const { if (!IsCubeComplete()) // in particular, this checks that this is a cube map return false; for (size_t face = 0; face < mFacesCount; ++face) { if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face)) return false; } return true; } bool NeedFakeBlack() { // handle this case first, it's the generic case if (mFakeBlackStatus == DoNotNeedFakeBlack) return false; if (mFakeBlackStatus == DontKnowIfNeedFakeBlack) { // Determine if the texture needs to be faked as a black texture. // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec. for (size_t face = 0; face < mFacesCount; ++face) { if (!ImageInfoAt(0, face).mIsDefined) { // In case of undefined texture image, we don't print any message because this is a very common // and often legitimate case, for example when doing asynchronous texture loading. // An extreme case of this is the photowall google demo. // Exiting early here allows us to avoid making noise on valid webgl code. mFakeBlackStatus = DoNeedFakeBlack; return true; } } const char *msg_rendering_as_black = "A texture is going to be rendered as if it were black, as per the OpenGL ES 2.0.24 spec section 3.8.2, " "because it"; if (mTarget == LOCAL_GL_TEXTURE_2D) { if (DoesMinFilterRequireMipmap()) { if (!IsMipmapTexture2DComplete()) { mContext->GenerateWarning ("%s is a 2D texture, with a minification filter requiring a mipmap, " "and is not mipmap complete (as defined in section 3.7.10).", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } else if (!ImageInfoAt(0).IsPowerOfTwo()) { mContext->GenerateWarning ("%s is a 2D texture, with a minification filter requiring a mipmap, " "and either its width or height is not a power of two.", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } } else // no mipmap required { if (!ImageInfoAt(0).IsPositive()) { mContext->GenerateWarning ("%s is a 2D texture and its width or height is equal to zero.", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } else if (!AreBothWrapModesClampToEdge() && !ImageInfoAt(0).IsPowerOfTwo()) { mContext->GenerateWarning ("%s is a 2D texture, with a minification filter not requiring a mipmap, " "with its width or height not a power of two, and with a wrap mode " "different from CLAMP_TO_EDGE.", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } } } else // cube map { bool areAllLevel0ImagesPOT = true; for (size_t face = 0; face < mFacesCount; ++face) areAllLevel0ImagesPOT &= ImageInfoAt(0, face).IsPowerOfTwo(); if (DoesMinFilterRequireMipmap()) { if (!IsMipmapCubeComplete()) { mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, " "and is not mipmap cube complete (as defined in section 3.7.10).", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } else if (!areAllLevel0ImagesPOT) { mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, " "and either the width or the height of some level 0 image is not a power of two.", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } } else // no mipmap required { if (!IsCubeComplete()) { mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, " "and is not cube complete (as defined in section 3.7.10).", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } else if (!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT) { mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, " "with some level 0 image having width or height not a power of two, and with a wrap mode " "different from CLAMP_TO_EDGE.", msg_rendering_as_black); mFakeBlackStatus = DoNeedFakeBlack; } } } // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, // that means that we do NOT need it. if (mFakeBlackStatus == DontKnowIfNeedFakeBlack) mFakeBlackStatus = DoNotNeedFakeBlack; } return mFakeBlackStatus == DoNeedFakeBlack; } }; struct WebGLMappedIdentifier { nsCString original, mapped; // ASCII strings WebGLMappedIdentifier(const nsACString& o, const nsACString& m) : original(o), mapped(m) {} }; struct WebGLUniformInfo { uint32_t arraySize; bool isArray; ShDataType type; WebGLUniformInfo(uint32_t s = 0, bool a = false, ShDataType t = SH_NONE) : arraySize(s), isArray(a), type(t) {} int ElementSize() const { switch (type) { case SH_INT: case SH_FLOAT: case SH_BOOL: case SH_SAMPLER_2D: case SH_SAMPLER_CUBE: return 1; case SH_INT_VEC2: case SH_FLOAT_VEC2: case SH_BOOL_VEC2: return 2; case SH_INT_VEC3: case SH_FLOAT_VEC3: case SH_BOOL_VEC3: return 3; case SH_INT_VEC4: case SH_FLOAT_VEC4: case SH_BOOL_VEC4: case SH_FLOAT_MAT2: return 4; case SH_FLOAT_MAT3: return 9; case SH_FLOAT_MAT4: return 16; default: NS_ABORT(); // should never get here return 0; } } }; class WebGLShader MOZ_FINAL : public nsIWebGLShader , public WebGLRefCountedObject , public LinkedListElement , public WebGLContextBoundObject { friend class WebGLContext; friend class WebGLProgram; public: WebGLShader(WebGLContext *context, WebGLenum stype) : WebGLContextBoundObject(context) , mType(stype) , mNeedsTranslation(true) , mAttribMaxNameLength(0) { mContext->MakeContextCurrent(); mGLName = mContext->gl->fCreateShader(mType); mContext->mShaders.insertBack(this); } ~WebGLShader() { DeleteOnce(); } size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const { return aMallocSizeOf(this) + mSource.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + mTranslationLog.SizeOfExcludingThisIfUnshared(aMallocSizeOf); } void Delete() { mSource.Truncate(); mTranslationLog.Truncate(); mContext->MakeContextCurrent(); mContext->gl->fDeleteShader(mGLName); LinkedListElement::remove(); // remove from mContext->mShaders } WebGLuint GLName() { return mGLName; } WebGLenum ShaderType() { return mType; } void SetSource(const nsAString& src) { // XXX do some quick gzip here maybe -- getting this will be very rare mSource.Assign(src); } const nsString& Source() const { return mSource; } void SetNeedsTranslation() { mNeedsTranslation = true; } bool NeedsTranslation() const { return mNeedsTranslation; } void SetTranslationSuccess() { mTranslationLog.SetIsVoid(true); mNeedsTranslation = false; } void SetTranslationFailure(const nsCString& msg) { mTranslationLog.Assign(msg); } const nsCString& TranslationLog() const { return mTranslationLog; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLSHADER protected: WebGLuint mGLName; WebGLenum mType; nsString mSource; nsCString mTranslationLog; // The translation log should contain only ASCII characters bool mNeedsTranslation; nsTArray mAttributes; nsTArray mUniforms; nsTArray mUniformInfos; int mAttribMaxNameLength; }; /** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]" in bracketPart * * \param string input/output: the string to split, becomes the string without the bracket part * \param bracketPart output: gets the bracket part. * * Notice that if there are multiple brackets like "foo[i].bar[j]", only the last bracket is split. */ static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart) { MOZ_ASSERT(bracketPart.IsEmpty(), "SplitLastSquareBracket must be called with empty bracketPart string"); if (string.IsEmpty()) return false; char *string_start = string.BeginWriting(); char *s = string_start + string.Length() - 1; if (*s != ']') return false; while (*s != '[' && s != string_start) s--; if (*s != '[') return false; bracketPart.Assign(s); *s = 0; string.EndWriting(); string.SetLength(s - string_start); return true; } typedef nsDataHashtable CStringMap; typedef nsDataHashtable CStringToUniformInfoMap; // NOTE: When this class is switched to new DOM bindings, update the // (then-slow) WrapObject call in GetParameter. class WebGLProgram MOZ_FINAL : public nsIWebGLProgram , public WebGLRefCountedObject , public LinkedListElement , public WebGLContextBoundObject { public: WebGLProgram(WebGLContext *context) : WebGLContextBoundObject(context) , mLinkStatus(false) , mGeneration(0) , mAttribMaxNameLength(0) { mContext->MakeContextCurrent(); mGLName = mContext->gl->fCreateProgram(); mContext->mPrograms.insertBack(this); } ~WebGLProgram() { DeleteOnce(); } void Delete() { DetachShaders(); mContext->MakeContextCurrent(); mContext->gl->fDeleteProgram(mGLName); LinkedListElement::remove(); // remove from mContext->mPrograms } void DetachShaders() { mAttachedShaders.Clear(); } WebGLuint GLName() { return mGLName; } const nsTArray >& AttachedShaders() const { return mAttachedShaders; } bool LinkStatus() { return mLinkStatus; } uint32_t Generation() const { return mGeneration.value(); } void SetLinkStatus(bool val) { mLinkStatus = val; } bool ContainsShader(WebGLShader *shader) { return mAttachedShaders.Contains(shader); } // return true if the shader wasn't already attached bool AttachShader(WebGLShader *shader) { if (ContainsShader(shader)) return false; mAttachedShaders.AppendElement(shader); mContext->MakeContextCurrent(); mContext->gl->fAttachShader(GLName(), shader->GLName()); return true; } // return true if the shader was found and removed bool DetachShader(WebGLShader *shader) { if (!mAttachedShaders.RemoveElement(shader)) return false; mContext->MakeContextCurrent(); mContext->gl->fDetachShader(GLName(), shader->GLName()); return true; } bool HasAttachedShaderOfType(GLenum shaderType) { for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) { return true; } } return false; } bool HasBothShaderTypesAttached() { return HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) && HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER); } bool NextGeneration() { if (!(mGeneration + 1).isValid()) return false; // must exit without changing mGeneration ++mGeneration; return true; } /* Called only after LinkProgram */ bool UpdateInfo(); /* Getters for cached program info */ bool IsAttribInUse(unsigned i) const { return mAttribsInUse[i]; } /* Maps identifier |name| to the mapped identifier |*mappedName| * Both are ASCII strings. */ void MapIdentifier(const nsACString& name, nsCString *mappedName) { if (!mIdentifierMap) { // if the identifier map doesn't exist yet, build it now mIdentifierMap = new CStringMap; mIdentifierMap->Init(); for (size_t i = 0; i < mAttachedShaders.Length(); i++) { for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; mIdentifierMap->Put(attrib.original, attrib.mapped); } for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; mIdentifierMap->Put(uniform.original, uniform.mapped); } } } nsCString mutableName(name); nsCString bracketPart; bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); if (hadBracketPart) mutableName.AppendLiteral("[0]"); if (mIdentifierMap->Get(mutableName, mappedName)) { if (hadBracketPart) { nsCString mappedBracketPart; bool mappedHadBracketPart = SplitLastSquareBracket(*mappedName, mappedBracketPart); if (mappedHadBracketPart) mappedName->Append(bracketPart); } return; } // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. mutableName.AppendLiteral("[0]"); if (mIdentifierMap->Get(mutableName, mappedName)) return; // not found? return name unchanged. This case happens e.g. on bad user input, or when // we're not using identifier mapping, or if we didn't store an identifier in the map because // e.g. its mapping is trivial (as happens for short identifiers) mappedName->Assign(name); } /* Un-maps mapped identifier |name| to the original identifier |*reverseMappedName| * Both are ASCII strings. */ void ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName) { if (!mIdentifierReverseMap) { // if the identifier reverse map doesn't exist yet, build it now mIdentifierReverseMap = new CStringMap; mIdentifierReverseMap->Init(); for (size_t i = 0; i < mAttachedShaders.Length(); i++) { for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; mIdentifierReverseMap->Put(attrib.mapped, attrib.original); } for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; mIdentifierReverseMap->Put(uniform.mapped, uniform.original); } } } nsCString mutableName(name); nsCString bracketPart; bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); if (hadBracketPart) mutableName.AppendLiteral("[0]"); if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) { if (hadBracketPart) { nsCString reverseMappedBracketPart; bool reverseMappedHadBracketPart = SplitLastSquareBracket(*reverseMappedName, reverseMappedBracketPart); if (reverseMappedHadBracketPart) reverseMappedName->Append(bracketPart); } return; } // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. mutableName.AppendLiteral("[0]"); if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) return; // not found? return name unchanged. This case happens e.g. on bad user input, or when // we're not using identifier mapping, or if we didn't store an identifier in the map because // e.g. its mapping is trivial (as happens for short identifiers) reverseMappedName->Assign(name); } /* Returns the uniform array size (or 1 if the uniform is not an array) of * the uniform with given mapped identifier. * * Note: the input string |name| is the mapped identifier, not the original identifier. */ WebGLUniformInfo GetUniformInfoForMappedIdentifier(const nsACString& name) { if (!mUniformInfoMap) { // if the identifier-to-array-size map doesn't exist yet, build it now mUniformInfoMap = new CStringToUniformInfoMap; mUniformInfoMap->Init(); for (size_t i = 0; i < mAttachedShaders.Length(); i++) { for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j]; mUniformInfoMap->Put(uniform.mapped, info); } } } nsCString mutableName(name); nsCString bracketPart; bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); // if there is a bracket, we're either an array or an entry in an array. if (hadBracketPart) mutableName.AppendLiteral("[0]"); WebGLUniformInfo info; mUniformInfoMap->Get(mutableName, &info); // we don't check if that Get failed, as if it did, it left info with default values // if there is a bracket and it's not [0], then we're not an array, we're just an entry in an array if (hadBracketPart && !bracketPart.EqualsLiteral("[0]")) { info.isArray = false; info.arraySize = 1; } return info; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLPROGRAM protected: WebGLuint mGLName; bool mLinkStatus; // attached shaders of the program object nsTArray > mAttachedShaders; CheckedUint32 mGeneration; // post-link data std::vector mAttribsInUse; nsAutoPtr mIdentifierMap, mIdentifierReverseMap; nsAutoPtr mUniformInfoMap; int mAttribMaxNameLength; }; // NOTE: When this class is switched to new DOM bindings, update the (then-slow) // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter. class WebGLRenderbuffer MOZ_FINAL : public nsIWebGLRenderbuffer , public WebGLRefCountedObject , public LinkedListElement , public WebGLRectangleObject , public WebGLContextBoundObject { public: WebGLRenderbuffer(WebGLContext *context) : WebGLContextBoundObject(context) , mInternalFormat(0) , mInternalFormatForGL(0) , mHasEverBeenBound(false) , mInitialized(false) { mContext->MakeContextCurrent(); mContext->gl->fGenRenderbuffers(1, &mGLName); mContext->mRenderbuffers.insertBack(this); } ~WebGLRenderbuffer() { DeleteOnce(); } void Delete() { mContext->MakeContextCurrent(); mContext->gl->fDeleteRenderbuffers(1, &mGLName); LinkedListElement::remove(); // remove from mContext->mRenderbuffers } bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } WebGLuint GLName() const { return mGLName; } bool Initialized() const { return mInitialized; } void SetInitialized(bool aInitialized) { mInitialized = aInitialized; } WebGLenum InternalFormat() const { return mInternalFormat; } void SetInternalFormat(WebGLenum aInternalFormat) { mInternalFormat = aInternalFormat; } WebGLenum InternalFormatForGL() const { return mInternalFormatForGL; } void SetInternalFormatForGL(WebGLenum aInternalFormatForGL) { mInternalFormatForGL = aInternalFormatForGL; } int64_t MemoryUsage() const { int64_t pixels = int64_t(Width()) * int64_t(Height()); switch (mInternalFormatForGL) { case LOCAL_GL_STENCIL_INDEX8: return pixels; case LOCAL_GL_RGBA4: case LOCAL_GL_RGB5_A1: case LOCAL_GL_RGB565: case LOCAL_GL_DEPTH_COMPONENT16: return 2 * pixels; case LOCAL_GL_RGB8: case LOCAL_GL_DEPTH_COMPONENT24: return 3*pixels; case LOCAL_GL_RGBA8: case LOCAL_GL_DEPTH24_STENCIL8: return 4*pixels; default: break; } NS_ABORT(); return 0; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLRENDERBUFFER protected: WebGLuint mGLName; WebGLenum mInternalFormat; WebGLenum mInternalFormatForGL; bool mHasEverBeenBound; bool mInitialized; friend class WebGLFramebuffer; }; class WebGLFramebufferAttachment { // deleting a texture or renderbuffer immediately detaches it WebGLRefPtr mTexturePtr; WebGLRefPtr mRenderbufferPtr; WebGLenum mAttachmentPoint; WebGLint mTextureLevel; WebGLenum mTextureCubeMapFace; public: WebGLFramebufferAttachment(WebGLenum aAttachmentPoint) : mAttachmentPoint(aAttachmentPoint) {} bool IsDefined() const { return Texture() || Renderbuffer(); } bool IsDeleteRequested() const { return Texture() ? Texture()->IsDeleteRequested() : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() : false; } bool HasAlpha() const { WebGLenum format = 0; if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) format = Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace).Format(); else if (Renderbuffer()) format = Renderbuffer()->InternalFormat(); return format == LOCAL_GL_RGBA || format == LOCAL_GL_LUMINANCE_ALPHA || format == LOCAL_GL_ALPHA || format == LOCAL_GL_RGBA4 || format == LOCAL_GL_RGB5_A1; } void SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face) { mTexturePtr = tex; mRenderbufferPtr = nsnull; mTextureLevel = level; mTextureCubeMapFace = face; } void SetRenderbuffer(WebGLRenderbuffer *rb) { mTexturePtr = nsnull; mRenderbufferPtr = rb; } const WebGLTexture *Texture() const { return mTexturePtr; } WebGLTexture *Texture() { return mTexturePtr; } const WebGLRenderbuffer *Renderbuffer() const { return mRenderbufferPtr; } WebGLRenderbuffer *Renderbuffer() { return mRenderbufferPtr; } WebGLint TextureLevel() const { return mTextureLevel; } WebGLenum TextureCubeMapFace() const { return mTextureCubeMapFace; } bool HasUninitializedRenderbuffer() const { return mRenderbufferPtr && !mRenderbufferPtr->Initialized(); } void Reset() { mTexturePtr = nsnull; mRenderbufferPtr = nsnull; } const WebGLRectangleObject* RectangleObject() const { if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) return &Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace); else if (Renderbuffer()) return Renderbuffer(); else return nsnull; } bool HasSameDimensionsAs(const WebGLFramebufferAttachment& other) const { const WebGLRectangleObject *thisRect = RectangleObject(); const WebGLRectangleObject *otherRect = other.RectangleObject(); return thisRect && otherRect && thisRect->HasSameDimensionsAs(*otherRect); } bool IsComplete() const { const WebGLRectangleObject *thisRect = RectangleObject(); if (!thisRect || !thisRect->Width() || !thisRect->Height()) return false; if (mTexturePtr) return mAttachmentPoint == LOCAL_GL_COLOR_ATTACHMENT0; if (mRenderbufferPtr) { WebGLenum format = mRenderbufferPtr->InternalFormat(); switch (mAttachmentPoint) { case LOCAL_GL_COLOR_ATTACHMENT0: return format == LOCAL_GL_RGB565 || format == LOCAL_GL_RGB5_A1 || format == LOCAL_GL_RGBA4; case LOCAL_GL_DEPTH_ATTACHMENT: return format == LOCAL_GL_DEPTH_COMPONENT16; case LOCAL_GL_STENCIL_ATTACHMENT: return format == LOCAL_GL_STENCIL_INDEX8; case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: return format == LOCAL_GL_DEPTH_STENCIL; default: NS_ABORT(); // should have been validated earlier } } NS_ABORT(); // should never get there return false; } }; // NOTE: When this class is switched to new DOM bindings, update the // (then-slow) WrapObject call in GetParameter. class WebGLFramebuffer MOZ_FINAL : public nsIWebGLFramebuffer , public WebGLRefCountedObject , public LinkedListElement , public WebGLContextBoundObject { public: WebGLFramebuffer(WebGLContext *context) : WebGLContextBoundObject(context) , mHasEverBeenBound(false) , mColorAttachment(LOCAL_GL_COLOR_ATTACHMENT0) , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT) , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT) , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { mContext->MakeContextCurrent(); mContext->gl->fGenFramebuffers(1, &mGLName); mContext->mFramebuffers.insertBack(this); } ~WebGLFramebuffer() { DeleteOnce(); } void Delete() { mColorAttachment.Reset(); mDepthAttachment.Reset(); mStencilAttachment.Reset(); mDepthStencilAttachment.Reset(); mContext->MakeContextCurrent(); mContext->gl->fDeleteFramebuffers(1, &mGLName); LinkedListElement::remove(); // remove from mContext->mFramebuffers } bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } WebGLuint GLName() { return mGLName; } void FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, WebGLRenderbuffer *wrb) { if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb)) { return; } if (target != LOCAL_GL_FRAMEBUFFER) return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target); if (rbtarget != LOCAL_GL_RENDERBUFFER) return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); switch (attachment) { case LOCAL_GL_DEPTH_ATTACHMENT: mDepthAttachment.SetRenderbuffer(wrb); break; case LOCAL_GL_STENCIL_ATTACHMENT: mStencilAttachment.SetRenderbuffer(wrb); break; case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: mDepthStencilAttachment.SetRenderbuffer(wrb); break; default: // finish checking that the 'attachment' parameter is among the allowed values if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment); mColorAttachment.SetRenderbuffer(wrb); break; } mContext->MakeContextCurrent(); WebGLuint renderbuffername = wrb ? wrb->GLName() : 0; if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, renderbuffername); mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, renderbuffername); } else { mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername); } } void FramebufferTexture2D(WebGLenum target, WebGLenum attachment, WebGLenum textarget, WebGLTexture *wtex, WebGLint level) { if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", wtex)) { return; } if (target != LOCAL_GL_FRAMEBUFFER) return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target); if (textarget != LOCAL_GL_TEXTURE_2D && (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget); if (level != 0) return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0"); size_t face = WebGLTexture::FaceForTarget(textarget); switch (attachment) { case LOCAL_GL_DEPTH_ATTACHMENT: mDepthAttachment.SetTexture(wtex, level, face); break; case LOCAL_GL_STENCIL_ATTACHMENT: mStencilAttachment.SetTexture(wtex, level, face); break; case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: mDepthStencilAttachment.SetTexture(wtex, level, face); break; default: if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment); mColorAttachment.SetTexture(wtex, level, face); break; } mContext->MakeContextCurrent(); WebGLuint texturename = wtex ? wtex->GLName() : 0; if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, texturename, level); mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, texturename, level); } else { mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level); } return; } bool HasIncompleteAttachment() const { return (mColorAttachment.IsDefined() && !mColorAttachment.IsComplete()) || (mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) || (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) || (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete()); } bool HasDepthStencilConflict() const { return int(mDepthAttachment.IsDefined()) + int(mStencilAttachment.IsDefined()) + int(mDepthStencilAttachment.IsDefined()) >= 2; } bool HasAttachmentsOfMismatchedDimensions() const { return (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) || (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) || (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment)); } const WebGLFramebufferAttachment& ColorAttachment() const { return mColorAttachment; } const WebGLFramebufferAttachment& DepthAttachment() const { return mDepthAttachment; } const WebGLFramebufferAttachment& StencilAttachment() const { return mStencilAttachment; } const WebGLFramebufferAttachment& DepthStencilAttachment() const { return mDepthStencilAttachment; } const WebGLFramebufferAttachment& GetAttachment(WebGLenum attachment) const { if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) return mDepthStencilAttachment; if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) return mDepthAttachment; if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) return mStencilAttachment; NS_ASSERTION(attachment == LOCAL_GL_COLOR_ATTACHMENT0, "bad attachment!"); return mColorAttachment; } void DetachTexture(const WebGLTexture *tex) { if (mColorAttachment.Texture() == tex) FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nsnull, 0); if (mDepthAttachment.Texture() == tex) FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nsnull, 0); if (mStencilAttachment.Texture() == tex) FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nsnull, 0); if (mDepthStencilAttachment.Texture() == tex) FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nsnull, 0); } void DetachRenderbuffer(const WebGLRenderbuffer *rb) { if (mColorAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nsnull); if (mDepthAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nsnull); if (mStencilAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nsnull); if (mDepthStencilAttachment.Renderbuffer() == rb) FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nsnull); } const WebGLRectangleObject *RectangleObject() { return mColorAttachment.RectangleObject(); } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLFRAMEBUFFER bool CheckAndInitializeRenderbuffers() { // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not // generate the INVALID_FRAMEBUFFER_OPERATION that we need here if (HasDepthStencilConflict()) return false; if (!mColorAttachment.HasUninitializedRenderbuffer() && !mDepthAttachment.HasUninitializedRenderbuffer() && !mStencilAttachment.HasUninitializedRenderbuffer() && !mDepthStencilAttachment.HasUninitializedRenderbuffer()) return true; // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case const WebGLRectangleObject *rect = mColorAttachment.RectangleObject(); if (!rect || !rect->Width() || !rect->Height()) return false; mContext->MakeContextCurrent(); WebGLenum status; mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER, &status); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) return false; uint32_t mask = 0; if (mColorAttachment.HasUninitializedRenderbuffer()) mask |= LOCAL_GL_COLOR_BUFFER_BIT; if (mDepthAttachment.HasUninitializedRenderbuffer() || mDepthStencilAttachment.HasUninitializedRenderbuffer()) { mask |= LOCAL_GL_DEPTH_BUFFER_BIT; } if (mStencilAttachment.HasUninitializedRenderbuffer() || mDepthStencilAttachment.HasUninitializedRenderbuffer()) { mask |= LOCAL_GL_STENCIL_BUFFER_BIT; } mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0, 0, rect->Width(), rect->Height())); if (mColorAttachment.HasUninitializedRenderbuffer()) mColorAttachment.Renderbuffer()->SetInitialized(true); if (mDepthAttachment.HasUninitializedRenderbuffer()) mDepthAttachment.Renderbuffer()->SetInitialized(true); if (mStencilAttachment.HasUninitializedRenderbuffer()) mStencilAttachment.Renderbuffer()->SetInitialized(true); if (mDepthStencilAttachment.HasUninitializedRenderbuffer()) mDepthStencilAttachment.Renderbuffer()->SetInitialized(true); return true; } WebGLuint mGLName; bool mHasEverBeenBound; // we only store pointers to attached renderbuffers, not to attached textures, because // we will only need to initialize renderbuffers. Textures are already initialized. WebGLFramebufferAttachment mColorAttachment, mDepthAttachment, mStencilAttachment, mDepthStencilAttachment; }; class WebGLUniformLocation MOZ_FINAL : public nsIWebGLUniformLocation , public WebGLContextBoundObject { public: WebGLUniformLocation(WebGLContext *context, WebGLProgram *program, GLint location, const WebGLUniformInfo& info) : WebGLContextBoundObject(context) , mProgram(program) , mProgramGeneration(program->Generation()) , mLocation(location) , mInfo(info) { mElementSize = info.ElementSize(); } ~WebGLUniformLocation() { } // needed for certain helper functions like ValidateObject. // WebGLUniformLocation's can't be 'Deleted' in the WebGL sense. bool IsDeleted() const { return false; } const WebGLUniformInfo &Info() const { return mInfo; } WebGLProgram *Program() const { return mProgram; } GLint Location() const { return mLocation; } uint32_t ProgramGeneration() const { return mProgramGeneration; } int ElementSize() const { return mElementSize; } virtual JSObject* WrapObject(JSContext *cx, JSObject *scope); NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLUNIFORMLOCATION protected: // nsRefPtr, not WebGLRefPtr, so that we don't prevent the program from being explicitly deleted. // we just want to avoid having a dangling pointer. nsRefPtr mProgram; uint32_t mProgramGeneration; GLint mLocation; WebGLUniformInfo mInfo; int mElementSize; friend class WebGLProgram; }; class WebGLActiveInfo MOZ_FINAL : public nsIWebGLActiveInfo { public: WebGLActiveInfo(WebGLint size, WebGLenum type, const nsACString& name) : mSize(size), mType(type), mName(NS_ConvertASCIItoUTF16(name)) {} NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLACTIVEINFO protected: WebGLint mSize; WebGLenum mType; nsString mName; }; class WebGLShaderPrecisionFormat MOZ_FINAL : public nsIWebGLShaderPrecisionFormat { public: WebGLShaderPrecisionFormat(WebGLint rangeMin, WebGLint rangeMax, WebGLint precision) : mRangeMin(rangeMin), mRangeMax(rangeMax), mPrecision(precision) { } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLSHADERPRECISIONFORMAT protected: WebGLint mRangeMin; WebGLint mRangeMax; WebGLint mPrecision; }; class WebGLExtension : public nsIWebGLExtension , public WebGLContextBoundObject , public nsWrapperCache { public: WebGLExtension(WebGLContext *baseContext) : WebGLContextBoundObject(baseContext) {} NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLExtension) NS_DECL_NSIWEBGLEXTENSION virtual ~WebGLExtension() {} }; inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const { return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject() : static_cast(this); } /** ** Template implementations **/ template inline bool WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info, ObjectType *aObject) { if (aObject && !aObject->IsCompatibleWithContext(this)) { ErrorInvalidOperation("%s: object from different WebGL context " "(or older generation of this one) " "passed as argument", info); return false; } return true; } template inline bool WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType *aObject) { MOZ_ASSERT(aObject); if (!ValidateObjectAllowDeletedOrNull(info, aObject)) return false; if (aObject->IsDeleted()) { ErrorInvalidValue("%s: deleted object passed as argument", info); return false; } return true; } template inline bool WebGLContext::ValidateObjectAllowNull(const char* info, ObjectType *aObject) { if (!aObject) { return true; } return ValidateObjectAssumeNonNull(info, aObject); } template inline bool WebGLContext::ValidateObjectAllowDeleted(const char* info, ObjectType *aObject) { if (!aObject) { ErrorInvalidValue("%s: null object passed as argument", info); return false; } return ValidateObjectAllowDeletedOrNull(info, aObject); } template inline bool WebGLContext::ValidateObject(const char* info, ObjectType *aObject) { if (!aObject) { ErrorInvalidValue("%s: null object passed as argument", info); return false; } return ValidateObjectAssumeNonNull(info, aObject); } class WebGLMemoryMultiReporterWrapper { WebGLMemoryMultiReporterWrapper(); ~WebGLMemoryMultiReporterWrapper(); static WebGLMemoryMultiReporterWrapper* sUniqueInstance; // here we store plain pointers, not RefPtrs: we don't want the // WebGLMemoryMultiReporterWrapper unique instance to keep alive all // WebGLContexts ever created. typedef nsTArray ContextsArrayType; ContextsArrayType mContexts; nsCOMPtr mReporter; static WebGLMemoryMultiReporterWrapper* UniqueInstance(); static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; } public: static void AddWebGLContext(const WebGLContext* c) { Contexts().AppendElement(c); } static void RemoveWebGLContext(const WebGLContext* c) { ContextsArrayType & contexts = Contexts(); contexts.RemoveElement(c); if (contexts.IsEmpty()) { delete sUniqueInstance; sUniqueInstance = nsnull; } } static int64_t GetTextureMemoryUsed() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLTexture *texture = contexts[i]->mTextures.getFirst(); texture; texture = texture->getNext()) { result += texture->MemoryUsage(); } } return result; } static int64_t GetTextureCount() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLTexture *texture = contexts[i]->mTextures.getFirst(); texture; texture = texture->getNext()) { result++; } } return result; } static int64_t GetBufferMemoryUsed() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLBuffer *buffer = contexts[i]->mBuffers.getFirst(); buffer; buffer = buffer->getNext()) { result += buffer->ByteLength(); } } return result; } static int64_t GetBufferCacheMemoryUsed(); static int64_t GetBufferCount() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLBuffer *buffer = contexts[i]->mBuffers.getFirst(); buffer; buffer = buffer->getNext()) { result++; } } return result; } static int64_t GetRenderbufferMemoryUsed() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLRenderbuffer *rb = contexts[i]->mRenderbuffers.getFirst(); rb; rb = rb->getNext()) { result += rb->MemoryUsage(); } } return result; } static int64_t GetRenderbufferCount() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLRenderbuffer *rb = contexts[i]->mRenderbuffers.getFirst(); rb; rb = rb->getNext()) { result++; } } return result; } static int64_t GetShaderSize(); static int64_t GetShaderCount() { const ContextsArrayType & contexts = Contexts(); int64_t result = 0; for(size_t i = 0; i < contexts.Length(); ++i) { for (const WebGLShader *shader = contexts[i]->mShaders.getFirst(); shader; shader = shader->getNext()) { result++; } } return result; } static int64_t GetContextCount() { return Contexts().Length(); } }; class WebGLMemoryPressureObserver MOZ_FINAL : public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER WebGLMemoryPressureObserver(WebGLContext *context) : mContext(context) {} private: WebGLContext *mContext; }; } #endif