/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Mozilla Corporation. * Portions created by the Initial Developer are Copyright (C) 2007 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Vladimir Vukicevic (original author) * Mark Steele * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef WEBGLCONTEXT_H_ #define WEBGLCONTEXT_H_ #include #include "nsTArray.h" #include "nsDataHashtable.h" #include "nsRefPtrHashtable.h" #include "nsHashKeys.h" #include "nsIDocShell.h" #include "nsICanvasRenderingContextWebGL.h" #include "nsICanvasRenderingContextInternal.h" #include "nsHTMLCanvasElement.h" #include "nsWeakReference.h" #include "nsIDOMHTMLElement.h" #include "nsIJSNativeInitializer.h" #include "GLContext.h" #include "Layers.h" class nsIDocShell; namespace mozilla { class WebGLTexture; class WebGLBuffer; class WebGLProgram; class WebGLShader; class WebGLFramebuffer; class WebGLRenderbuffer; class WebGLUniformLocation; class WebGLZeroingObject; class WebGLObjectBaseRefPtr { protected: friend class WebGLZeroingObject; WebGLObjectBaseRefPtr() : mRawPtr(0) { } WebGLObjectBaseRefPtr(nsISupports *rawPtr) : mRawPtr(rawPtr) { } void Zero() { if (mRawPtr) { // Note: RemoveRefOwner isn't called here, because // the entire owner array will be cleared. mRawPtr->Release(); mRawPtr = 0; } } protected: nsISupports *mRawPtr; }; template class WebGLObjectRefPtr : public WebGLObjectBaseRefPtr { public: typedef T element_type; WebGLObjectRefPtr() { } WebGLObjectRefPtr(const WebGLObjectRefPtr& aSmartPtr) : WebGLObjectBaseRefPtr(aSmartPtr.mRawPtr) { if (mRawPtr) { RawPtr()->AddRef(); RawPtr()->AddRefOwner(this); } } WebGLObjectRefPtr(T *aRawPtr) : WebGLObjectBaseRefPtr(aRawPtr) { if (mRawPtr) { RawPtr()->AddRef(); RawPtr()->AddRefOwner(this); } } WebGLObjectRefPtr(const already_AddRefed& aSmartPtr) : WebGLObjectBaseRefPtr(aSmartPtr.mRawPtr) // construct from |dont_AddRef(expr)| { if (mRawPtr) { RawPtr()->AddRef(); RawPtr()->AddRefOwner(this); } } ~WebGLObjectRefPtr() { if (mRawPtr) { RawPtr()->RemoveRefOwner(this); RawPtr()->Release(); } } WebGLObjectRefPtr& operator=(const WebGLObjectRefPtr& rhs) { assign_with_AddRef(static_cast(rhs.mRawPtr)); return *this; } WebGLObjectRefPtr& operator=(T* rhs) { assign_with_AddRef(rhs); return *this; } WebGLObjectRefPtr& operator=(const already_AddRefed& rhs) { assign_assuming_AddRef(static_cast(rhs.mRawPtr)); return *this; } T* get() const { return const_cast(static_cast(mRawPtr)); } operator T*() const { return get(); } T* operator->() const { NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL WebGLObjectRefPtr with operator->()!"); return get(); } T& operator*() const { NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL WebGLObjectRefPtr with operator*()!"); return *get(); } private: T* RawPtr() { return static_cast(mRawPtr); } void assign_with_AddRef(T* rawPtr) { if (rawPtr) { rawPtr->AddRef(); rawPtr->AddRefOwner(this); } assign_assuming_AddRef(rawPtr); } void assign_assuming_AddRef(T* newPtr) { T* oldPtr = RawPtr(); mRawPtr = newPtr; if (oldPtr) { oldPtr->RemoveRefOwner(this); oldPtr->Release(); } } }; class WebGLBuffer; struct WebGLVertexAttribData { WebGLVertexAttribData() : buf(0), stride(0), size(0), byteOffset(0), type(0), enabled(PR_FALSE) { } WebGLObjectRefPtr buf; WebGLuint stride; WebGLuint size; GLuint byteOffset; GLenum type; PRBool enabled; GLuint actualStride() const { if (stride) return stride; GLuint componentSize = 0; switch(type) { case LOCAL_GL_BYTE: componentSize = sizeof(GLbyte); break; case LOCAL_GL_UNSIGNED_BYTE: componentSize = sizeof(GLubyte); break; case LOCAL_GL_SHORT: componentSize = sizeof(GLshort); break; case LOCAL_GL_UNSIGNED_SHORT: componentSize = sizeof(GLushort); break; // XXX case LOCAL_GL_FIXED: case LOCAL_GL_FLOAT: componentSize = sizeof(GLfloat); break; } return size * componentSize; } }; class WebGLContext : public nsICanvasRenderingContextWebGL, public nsICanvasRenderingContextInternal, public nsSupportsWeakReference { public: WebGLContext(); virtual ~WebGLContext(); NS_DECL_ISUPPORTS NS_DECL_NSICANVASRENDERINGCONTEXTWEBGL // nsICanvasRenderingContextInternal NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas); 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 Render(gfxContext *ctx, gfxPattern::GraphicsFilter f); NS_IMETHOD GetInputStream(const char* aMimeType, const PRUnichar* aEncoderOptions, nsIInputStream **aStream); NS_IMETHOD GetThebesSurface(gfxASurface **surface); NS_IMETHOD SetIsOpaque(PRBool b) { return NS_OK; }; nsresult SynthesizeGLError(WebGLenum err); nsresult SynthesizeGLError(WebGLenum err, const char *fmt, ...); nsresult ErrorInvalidEnum(const char *fmt, ...); nsresult ErrorInvalidOperation(const char *fmt, ...); nsresult ErrorInvalidValue(const char *fmt, ...); already_AddRefed GetCanvasLayer(LayerManager *manager); void MarkContextClean() { } protected: nsHTMLCanvasElement* mCanvasElement; nsRefPtr gl; PRInt32 mWidth, mHeight; PRBool mInvalidated; WebGLuint mActiveTexture; WebGLenum mSynthesizedGLError; PRBool SafeToCreateCanvas3DContext(nsHTMLCanvasElement *canvasElement); PRBool ValidateGL(); PRBool ValidateBuffers(PRUint32 count); void Invalidate(); void MakeContextCurrent() { gl->MakeCurrent(); } // helpers nsresult TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum internalformat, WebGLsizei width, WebGLsizei height, WebGLint border, WebGLenum format, WebGLenum type, void *data, PRUint32 byteLength); nsresult TexSubImage2D_base(WebGLenum target, WebGLint level, WebGLint xoffset, WebGLint yoffset, WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type, void *pixels, PRUint32 byteLength); nsresult DOMElementToImageSurface(nsIDOMElement *imageOrCanvas, gfxImageSurface **imageOut, PRBool flipY, PRBool premultiplyAlpha); // the buffers bound to the current program's attribs nsTArray mAttribBuffers; // the textures bound to any sampler uniforms nsTArray > mUniformTextures; // textures bound to nsTArray > mBound2DTextures; nsTArray > mBoundCubeMapTextures; WebGLObjectRefPtr mBoundArrayBuffer; WebGLObjectRefPtr mBoundElementArrayBuffer; WebGLObjectRefPtr mCurrentProgram; // XXX these 3 are wrong types, and aren't used atm (except for the length of the attachments) nsTArray > mFramebufferColorAttachments; nsRefPtr mFramebufferDepthAttachment; nsRefPtr mFramebufferStencilAttachment; nsRefPtr mBoundFramebuffer; nsRefPtr mBoundRenderbuffer; // lookup tables for GL name -> object wrapper nsRefPtrHashtable mMapTextures; nsRefPtrHashtable mMapBuffers; nsRefPtrHashtable mMapPrograms; nsRefPtrHashtable mMapShaders; nsRefPtrHashtable mMapFramebuffers; nsRefPtrHashtable mMapRenderbuffers; public: // console logging helpers static void LogMessage (const char *fmt, ...); static void LogMessage(const char *fmt, va_list ap); }; // this class is a mixin for the named type wrappers, and is used // by WebGLObjectRefPtr to tell the object who holds references, so that // we can zero them out appropriately when the object is deleted, because // it will be unbound in the GL. class WebGLZeroingObject { public: WebGLZeroingObject() { } void AddRefOwner(WebGLObjectBaseRefPtr *owner) { mRefOwners.AppendElement(owner); } void RemoveRefOwner(WebGLObjectBaseRefPtr *owner) { mRefOwners.RemoveElement(owner); } void ZeroOwners() { WebGLObjectBaseRefPtr **owners = mRefOwners.Elements(); for (PRUint32 i = 0; i < mRefOwners.Length(); i++) { owners[i]->Zero(); } mRefOwners.Clear(); } protected: nsTArray mRefOwners; }; class WebGLRectangleObject { protected: WebGLRectangleObject() : mWidth(0), mHeight(0) { } public: WebGLsizei width() { return mWidth; } void width(WebGLsizei value) { mWidth = value; } WebGLsizei height() { 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; } } protected: WebGLsizei mWidth; WebGLsizei mHeight; }; #define WEBGLBUFFER_PRIVATE_IID \ {0xd69f22e9, 0x6f98, 0x48bd, {0xb6, 0x94, 0x34, 0x17, 0xed, 0x06, 0x11, 0xab}} class WebGLBuffer : public nsIWebGLBuffer, public WebGLZeroingObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLBUFFER_PRIVATE_IID) WebGLBuffer(WebGLuint name) : mName(name), mDeleted(PR_FALSE), mByteLength(0), mTarget(LOCAL_GL_NONE), mData(nsnull) { } ~WebGLBuffer() { Delete(); } void Delete() { if (mDeleted) return; ZeroOwners(); free(mData); mData = nsnull; mDeleted = PR_TRUE; mByteLength = 0; } PRBool Deleted() const { return mDeleted; } GLuint GLName() const { return mName; } 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. void CopyDataIfElementArray(const void* data) { if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { mData = realloc(mData, mByteLength); memcpy(mData, data, mByteLength); } } // same comments as for CopyElementArrayData void ZeroDataIfElementArray() { if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { mData = realloc(mData, mByteLength); memset(mData, 0, mByteLength); } } // same comments as for CopyElementArrayData void CopySubDataIfElementArray(GLuint byteOffset, GLuint byteLength, const void* data) { if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { 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 interprete the array elements as, must be GLushort or GLubyte. template T FindMaximum(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; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLBUFFER protected: WebGLuint mName; PRBool mDeleted; GLuint mByteLength; GLenum mTarget; void* mData; // in the case of an Element Array Buffer, we keep a copy. }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLBuffer, WEBGLBUFFER_PRIVATE_IID) #define WEBGLTEXTURE_PRIVATE_IID \ {0x4c19f189, 0x1f86, 0x4e61, {0x96, 0x21, 0x0a, 0x11, 0xda, 0x28, 0x10, 0xdd}} class WebGLTexture : public nsIWebGLTexture, public WebGLZeroingObject, public WebGLRectangleObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLTEXTURE_PRIVATE_IID) WebGLTexture(WebGLuint name) : mName(name), mDeleted(PR_FALSE) { } void Delete() { if (mDeleted) return; ZeroOwners(); mDeleted = PR_TRUE; } PRBool Deleted() { return mDeleted; } WebGLuint GLName() { return mName; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLTEXTURE protected: WebGLuint mName; PRBool mDeleted; }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLTexture, WEBGLTEXTURE_PRIVATE_IID) #define WEBGLPROGRAM_PRIVATE_IID \ {0xb3084a5b, 0xa5b4, 0x4ee0, {0xa0, 0xf0, 0xfb, 0xdd, 0x64, 0xaf, 0x8e, 0x82}} class WebGLProgram : public nsIWebGLProgram, public WebGLZeroingObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLPROGRAM_PRIVATE_IID) WebGLProgram(WebGLuint name) : mName(name), mDeleted(PR_FALSE) { } void Delete() { if (mDeleted) return; ZeroOwners(); mDeleted = PR_TRUE; } PRBool Deleted() { return mDeleted; } WebGLuint GLName() { return mName; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLPROGRAM protected: WebGLuint mName; PRBool mDeleted; }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLProgram, WEBGLPROGRAM_PRIVATE_IID) #define WEBGLSHADER_PRIVATE_IID \ {0x48cce975, 0xd459, 0x4689, {0x83, 0x82, 0x37, 0x82, 0x6e, 0xac, 0xe0, 0xa7}} class WebGLShader : public nsIWebGLShader, public WebGLZeroingObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLSHADER_PRIVATE_IID) WebGLShader(WebGLuint name) : mName(name), mDeleted(PR_FALSE) { } void Delete() { if (mDeleted) return; ZeroOwners(); mDeleted = PR_TRUE; } PRBool Deleted() { return mDeleted; } WebGLuint GLName() { return mName; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLSHADER protected: WebGLuint mName; PRBool mDeleted; }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLShader, WEBGLSHADER_PRIVATE_IID) #define WEBGLFRAMEBUFFER_PRIVATE_IID \ {0x0052a16f, 0x4bc9, 0x4a55, {0x9d, 0xa3, 0x54, 0x95, 0xaa, 0x4e, 0x80, 0xb9}} class WebGLFramebuffer : public nsIWebGLFramebuffer, public WebGLZeroingObject, public WebGLRectangleObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLFRAMEBUFFER_PRIVATE_IID) WebGLFramebuffer(WebGLuint name) : mName(name), mDeleted(PR_FALSE) { } void Delete() { if (mDeleted) return; ZeroOwners(); mDeleted = PR_TRUE; } PRBool Deleted() { return mDeleted; } WebGLuint GLName() { return mName; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLFRAMEBUFFER protected: WebGLuint mName; PRBool mDeleted; }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLFramebuffer, WEBGLFRAMEBUFFER_PRIVATE_IID) #define WEBGLRENDERBUFFER_PRIVATE_IID \ {0x3cbc2067, 0x5831, 0x4e3f, {0xac, 0x52, 0x7e, 0xf4, 0x5c, 0x04, 0xff, 0xae}} class WebGLRenderbuffer : public nsIWebGLRenderbuffer, public WebGLZeroingObject, public WebGLRectangleObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLRENDERBUFFER_PRIVATE_IID) WebGLRenderbuffer(WebGLuint name) : mName(name), mDeleted(PR_FALSE) { } void Delete() { if (mDeleted) return; ZeroOwners(); mDeleted = PR_TRUE; } PRBool Deleted() { return mDeleted; } WebGLuint GLName() { return mName; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLRENDERBUFFER protected: WebGLuint mName; PRBool mDeleted; }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLRenderbuffer, WEBGLRENDERBUFFER_PRIVATE_IID) #define WEBGLUNIFORMLOCATION_PRIVATE_IID \ {0x01a8a614, 0xb109, 0x42f1, {0xb4, 0x40, 0x8d, 0x8b, 0x87, 0x0b, 0x43, 0xa7}} class WebGLUniformLocation : public nsIWebGLUniformLocation, public WebGLZeroingObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLUNIFORMLOCATION_PRIVATE_IID) WebGLUniformLocation(WebGLProgram *program, GLint location) : mProgram(program), mLocation(location) { } WebGLProgram *Program() const { return mProgram; } GLint Location() const { return mLocation; } // needed for our generic helpers to check nsIxxx parameters, see GetConcreteObject. PRBool Deleted() { return PR_FALSE; } NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLUNIFORMLOCATION protected: WebGLObjectRefPtr mProgram; GLint mLocation; }; NS_DEFINE_STATIC_IID_ACCESSOR(WebGLUniformLocation, WEBGLUNIFORMLOCATION_PRIVATE_IID) } #endif