☠☠ backed out by 6dab2820469a ☠ ☠ | |
author | Jeff Gilbert <jgilbert@mozilla.com> |
Fri, 09 Jan 2015 18:40:56 -0800 | |
changeset 223101 | ca411b1cf0019393548598a66af9e08a711cd6f8 |
parent 223100 | 203e26771e770b3d072e2e3f2e5bb7ca2ca93555 |
child 223102 | d4be320ebecbc710f8f19950a4d7cf0e69075d92 |
push id | 28082 |
push user | cbook@mozilla.com |
push date | Mon, 12 Jan 2015 10:44:52 +0000 |
treeherder | mozilla-central@643589c3ef94 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kamidphish |
bugs | 1109945 |
milestone | 37.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/canvas/WebGL2Context.cpp +++ b/dom/canvas/WebGL2Context.cpp @@ -147,12 +147,14 @@ WebGLContext::InitWebGL2() mBoundUniformBuffers = MakeUnique<WebGLRefPtr<WebGLBuffer>[]>(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; auto xfBuffers = new WebGLRefPtr<WebGLBuffer>[mGLMaxTransformFeedbackSeparateAttribs]; mBoundTransformFeedbackBuffers.reset(xfBuffers); + mBypassShaderValidation = true; + return true; } } // namespace mozilla
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp +++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp @@ -207,48 +207,50 @@ WebGL2Context::TransformFeedbackVaryings GLsizei count = varyings.Length(); GLchar** tmpVaryings = (GLchar**) nsMemory::Alloc(count * sizeof(GLchar*)); for (GLsizei n = 0; n < count; n++) { tmpVaryings[n] = (GLchar*) ToNewCString(varyings[n]); } - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; MakeContextCurrent(); gl->fTransformFeedbackVaryings(progname, count, tmpVaryings, bufferMode); NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, tmpVaryings); } - already_AddRefed<WebGLActiveInfo> WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index) { if (IsContextLost()) return nullptr; if (!ValidateObject("getTransformFeedbackVarying: program", program)) return nullptr; MakeContextCurrent(); GLint len = 0; - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; gl->fGetProgramiv(progname, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &len); if (!len) return nullptr; UniquePtr<char[]> name(new char[len]); GLint tfsize = 0; GLuint tftype = 0; gl->fGetTransformFeedbackVarying(progname, index, len, &len, &tfsize, &tftype, name.get()); if (len == 0 || tfsize == 0 || tftype == 0) return nullptr; - // TODO(djg): Reverse lookup of name - // nsCString reverseMappedName; - // prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName); + MOZ_CRASH("todo"); + /* + // Reverse lookup of name + nsCString reverseMappedName; + prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName); nsRefPtr<WebGLActiveInfo> result = new WebGLActiveInfo(tfsize, tftype, nsDependentCString(name.get())); return result.forget(); + */ }
--- a/dom/canvas/WebGL2ContextUniforms.cpp +++ b/dom/canvas/WebGL2ContextUniforms.cpp @@ -391,17 +391,17 @@ WebGL2Context::GetUniformIndices(WebGLPr return; if (!ValidateObject("getUniformIndices: program", program)) return; if (!uniformNames.Length()) return; - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; size_t count = uniformNames.Length(); nsTArray<GLuint>& arr = retval.SetValue(); MakeContextCurrent(); for (size_t n = 0; n < count; n++) { NS_LossyConvertUTF16toASCII name(uniformNames[n]); // const GLchar* glname = name.get(); @@ -426,17 +426,17 @@ WebGL2Context::GetActiveUniforms(WebGLPr if (!ValidateObject("getActiveUniforms: program", program)) return; size_t count = uniformIndices.Length(); if (!count) return; - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; nsTArray<GLint>& arr = retval.SetValue(); arr.SetLength(count); MakeContextCurrent(); gl->fGetActiveUniformsiv(progname, count, uniformIndices.Elements(), pname, arr.Elements()); } @@ -445,27 +445,24 @@ WebGL2Context::GetUniformBlockIndex(WebG const nsAString& uniformBlockName) { if (IsContextLost()) return 0; if (!ValidateObject("getUniformBlockIndex: program", program)) return 0; - if (!ValidateGLSLVariableName(uniformBlockName, "getUniformBlockIndex")) - return 0; + // Leave this unchecked for now. - NS_LossyConvertUTF16toASCII cname(uniformBlockName); - nsCString mappedName; - program->MapIdentifier(cname, &mappedName); + const NS_LossyConvertUTF16toASCII cname(uniformBlockName); - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; MakeContextCurrent(); - return gl->fGetUniformBlockIndex(progname, mappedName.get()); + return gl->fGetUniformBlockIndex(progname, cname.BeginReading()); } static bool GetUniformBlockActiveUniforms(gl::GLContext* gl, JSContext* cx, WebGL2Context* owner, GLuint progname, GLuint uniformBlockIndex, JS::MutableHandleObject out_array) { @@ -496,17 +493,17 @@ WebGL2Context::GetActiveUniformBlockPara { retval.SetNull(); if (IsContextLost()) return; if (!ValidateObject("getActiveUniformBlockParameter: program", program)) return; - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; GLint param = 0; MakeContextCurrent(); switch(pname) { case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: gl->fGetActiveUniformBlockiv(progname, uniformBlockIndex, pname, ¶m); @@ -546,17 +543,17 @@ WebGL2Context::GetActiveUniformBlockName nsAString& retval) { if (IsContextLost()) return; if (!ValidateObject("getActiveUniformBlockName: program", program)) return; - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; GLchar nameBuffer[WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH]; GLsizei length = 0; MakeContextCurrent(); gl->fGetActiveUniformBlockName(progname, uniformBlockIndex, WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH, &length, nameBuffer); retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(nameBuffer))); @@ -569,13 +566,13 @@ WebGL2Context::UniformBlockBinding(WebGL GLuint uniformBlockBinding) { if (IsContextLost()) return; if (!ValidateObject("uniformBlockBinding: program", program)) return; - GLuint progname = program->GLName(); + GLuint progname = program->mGLName; MakeContextCurrent(); gl->fUniformBlockBinding(progname, uniformBlockIndex, uniformBlockBinding); }
--- a/dom/canvas/WebGLActiveInfo.cpp +++ b/dom/canvas/WebGLActiveInfo.cpp @@ -1,20 +1,99 @@ /* -*- Mode: C++; tab-width: 20; 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/. */ #include "WebGLActiveInfo.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" -#include "WebGLContext.h" -#include "WebGLTexture.h" namespace mozilla { -JSObject* -WebGLActiveInfo::WrapObject(JSContext* cx) +static uint8_t +ElemSizeFromType(GLenum elemType) { - return dom::WebGLActiveInfoBinding::Wrap(cx, this); + switch (elemType) { + case LOCAL_GL_BOOL: + case LOCAL_GL_FLOAT: + case LOCAL_GL_INT: + case LOCAL_GL_INT_SAMPLER_2D: + case LOCAL_GL_INT_SAMPLER_2D_ARRAY: + case LOCAL_GL_INT_SAMPLER_3D: + case LOCAL_GL_INT_SAMPLER_CUBE: + case LOCAL_GL_SAMPLER_2D: + case LOCAL_GL_SAMPLER_2D_ARRAY: + case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: + case LOCAL_GL_SAMPLER_2D_SHADOW: + case LOCAL_GL_SAMPLER_CUBE: + case LOCAL_GL_SAMPLER_CUBE_SHADOW: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: + return 1; + + case LOCAL_GL_BOOL_VEC2: + case LOCAL_GL_FLOAT_VEC2: + case LOCAL_GL_INT_VEC2: + return 2; + + case LOCAL_GL_BOOL_VEC3: + case LOCAL_GL_FLOAT_VEC3: + case LOCAL_GL_INT_VEC3: + return 3; + + + case LOCAL_GL_BOOL_VEC4: + case LOCAL_GL_FLOAT_MAT2: + case LOCAL_GL_FLOAT_VEC4: + case LOCAL_GL_INT_VEC4: + return 4; + + case LOCAL_GL_FLOAT_MAT2x3: + case LOCAL_GL_FLOAT_MAT3x2: + return 6; + + case LOCAL_GL_FLOAT_MAT2x4: + case LOCAL_GL_FLOAT_MAT4x2: + return 8; + + case LOCAL_GL_FLOAT_MAT3: + return 9; + + case LOCAL_GL_FLOAT_MAT3x4: + case LOCAL_GL_FLOAT_MAT4x3: + return 12; + + case LOCAL_GL_FLOAT_MAT4: + return 16; + + default: + MOZ_CRASH("Bad `elemType`."); + } } +WebGLActiveInfo::WebGLActiveInfo(GLint elemCount, GLenum elemType, bool isArray, + const nsACString& baseUserName, + const nsACString& baseMappedName) + : mElemCount(elemCount) + , mElemType(elemType) + , mBaseUserName(baseUserName) + , mIsArray(isArray) + , mElemSize(ElemSizeFromType(elemType)) + , mBaseMappedName(baseMappedName) +{ } + +//////////////////////////////////////////////////////////////////////////////// + +JSObject* +WebGLActiveInfo::WrapObject(JSContext* js) +{ + return dom::WebGLActiveInfoBinding::Wrap(js, this); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLActiveInfo) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLActiveInfo, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLActiveInfo, Release) + } // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.h +++ b/dom/canvas/WebGLActiveInfo.h @@ -1,55 +1,83 @@ /* -*- 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 WEBGL_ACTIVE_INFO_H_ #define WEBGL_ACTIVE_INFO_H_ +#include "GLDefs.h" #include "js/TypeDecls.h" +#include "mozilla/Attributes.h" +#include "nsISupportsImpl.h" // NS_INLINE_DECL_REFCOUNTING #include "nsString.h" -#include "WebGLObjectModel.h" +#include "nsWrapperCache.h" namespace mozilla { class WebGLActiveInfo MOZ_FINAL + : public nsWrapperCache { public: - WebGLActiveInfo(GLint size, GLenum type, const nsACString& name) - : mSize(size) - , mType(type) - , mName(NS_ConvertASCIItoUTF16(name)) - {} + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLActiveInfo) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLActiveInfo) + + const GLint mElemCount; // `size` + const GLenum mElemType; // `type` + const nsCString mBaseUserName; // `name`, but ASCII, and without any final "[0]". + + // Not actually part of ActiveInfo: + const bool mIsArray; + const uint8_t mElemSize; + const nsCString mBaseMappedName; // Without any final "[0]". + + WebGLActiveInfo(GLint elemCount, GLenum elemType, bool isArray, + const nsACString& baseUserName, const nsACString& baseMappedName); + + + /* GLES 2.0.25, p33: + * This command will return as much information about active + * attributes as possible. If no information is available, length will + * be set to zero and name will be an empty string. This situation + * could arise if GetActiveAttrib is issued after a failed link. + * + * It's the same for GetActiveUniform. + */ + static WebGLActiveInfo* CreateInvalid() { + return new WebGLActiveInfo(); + } // WebIDL attributes - GLint Size() const { - return mSize; + return mElemCount; } GLenum Type() const { - return mType; + return mElemType; } void GetName(nsString& retval) const { - retval = mName; + CopyASCIItoUTF16(mBaseUserName, retval); + if (mIsArray) + retval.AppendLiteral("[0]"); } - JSObject* WrapObject(JSContext* cx); - - NS_INLINE_DECL_REFCOUNTING(WebGLActiveInfo) + virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE; private: + WebGLActiveInfo() + : mElemCount(0) + , mElemType(0) + , mBaseUserName("") + , mIsArray(false) + , mElemSize(0) + , mBaseMappedName("") + { } + // Private destructor, to discourage deletion outside of Release(): - ~WebGLActiveInfo() - { - } - - GLint mSize; - GLenum mType; - nsString mName; + ~WebGLActiveInfo() { } }; } // namespace mozilla #endif // WEBGL_ACTIVE_INFO_H_
--- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -197,31 +197,30 @@ WebGLContextOptions::WebGLContextOptions { // Set default alpha state based on preference. if (Preferences::GetBool("webgl.default-no-alpha", false)) alpha = false; } WebGLContext::WebGLContext() : WebGLContextUnchecked(nullptr) + , mBypassShaderValidation(false) , mNeedsFakeNoAlpha(false) { mGeneration = 0; mInvalidated = false; mShouldPresent = true; mResetLayer = true; mOptionsFrozen = false; mActiveTexture = 0; mPixelStoreFlipY = false; mPixelStorePremultiplyAlpha = false; mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL; - mShaderValidation = true; - mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded; mVertexAttrib0Vector[0] = 0; mVertexAttrib0Vector[1] = 0; mVertexAttrib0Vector[2] = 0; mVertexAttrib0Vector[3] = 1; mFakeVertexAttrib0BufferObjectVector[0] = 0; mFakeVertexAttrib0BufferObjectVector[1] = 0; @@ -325,16 +324,17 @@ WebGLContext::DestroyResourcesAndContext mBoundArrayBuffer = nullptr; mBoundCopyReadBuffer = nullptr; mBoundCopyWriteBuffer = nullptr; mBoundPixelPackBuffer = nullptr; mBoundPixelUnpackBuffer = nullptr; mBoundTransformFeedbackBuffer = nullptr; mBoundUniformBuffer = nullptr; mCurrentProgram = nullptr; + mActiveProgramLinkInfo = nullptr; mBoundFramebuffer = nullptr; mActiveOcclusionQuery = nullptr; mBoundRenderbuffer = nullptr; mBoundVertexArray = nullptr; mDefaultVertexArray = nullptr; mBoundTransformFeedback = nullptr; mDefaultTransformFeedback = nullptr;
--- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -14,16 +14,17 @@ #include "mozilla/WeakPtr.h" #include "GLDefs.h" #include "WebGLActiveInfo.h" #include "WebGLContextUnchecked.h" #include "WebGLObjectModel.h" #include "WebGLRenderbuffer.h" #include "WebGLTexture.h" +#include "WebGLShaderValidator.h" #include "WebGLStrongTypes.h" #include <stdarg.h> #include "nsTArray.h" #include "nsCycleCollectionNoteChild.h" #include "nsIDOMWebGLRenderingContext.h" #include "nsICanvasRenderingContextInternal.h" @@ -91,16 +92,20 @@ class ImageData; struct WebGLContextAttributes; template<typename> struct Nullable; } namespace gfx { class SourceSurface; } +namespace webgl { +struct LinkedProgramInfo; +} + WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format); void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow); struct WebGLContextOptions { // these are defaults WebGLContextOptions(); @@ -369,17 +374,19 @@ public: GLenum srcAlpha, GLenum dstAlpha); GLenum CheckFramebufferStatus(GLenum target); void Clear(GLbitfield mask); void ClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a); void ClearDepth(GLclampf v); void ClearStencil(GLint v); void ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a); void CompileShader(WebGLShader* shader); - void CompressedTexImage2D(GLenum texImageTarget, GLint level, + void CompileShaderANGLE(WebGLShader* shader); + void CompileShaderBypass(WebGLShader* shader, const nsCString& shaderSource); + void CompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, const dom::ArrayBufferView& view); void CompressedTexSubImage2D(GLenum texImageTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, const dom::ArrayBufferView& view); void CopyTexImage2D(GLenum texImageTarget, GLint level, @@ -830,18 +837,20 @@ public: UniformMatrix4fv_base(loc, transpose, value.Length(), value.Elements()); } void UniformMatrix4fv_base(WebGLUniformLocation* loc, WebGLboolean transpose, size_t arrayLength, const float* data); void UseProgram(WebGLProgram* prog); + bool ValidateAttribArraySetter(const char* name, uint32_t count, uint32_t arrayLength); + bool ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName); bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize, GLenum setterType, const char* info, GLuint* out_rawLoc); bool ValidateUniformArraySetter(WebGLUniformLocation* loc, uint8_t setterElemSize, GLenum setterType, size_t setterArraySize, const char* info, GLuint* out_rawLoc, GLsizei* out_numElementsToUpload); @@ -1111,18 +1120,19 @@ protected: GLuint mActiveTexture; // glGetError sources: bool mEmitContextLostErrorOnce; GLenum mWebGLError; GLenum mUnderlyingGLError; GLenum GetAndFlushUnderlyingGLErrors(); - // whether shader validation is supported - bool mShaderValidation; + bool mBypassShaderValidation; + + webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const; // some GL constants int32_t mGLMaxVertexAttribs; int32_t mGLMaxTextureUnits; int32_t mGLMaxTextureSize; int32_t mGLMaxTextureSizeLog2; int32_t mGLMaxCubeMapTextureSize; int32_t mGLMaxCubeMapTextureSizeLog2; @@ -1137,16 +1147,20 @@ protected: GLuint mGLMaxTransformFeedbackSeparateAttribs; GLuint mGLMaxUniformBufferBindings; public: GLuint MaxVertexAttribs() const { return mGLMaxVertexAttribs; } + GLuint GLMaxTextureUnits() const { + return mGLMaxTextureUnits; + } + bool IsFormatValidForFB(GLenum sizedFormat) const; protected: // Represents current status of the context with respect to context loss. // That is, whether the context is lost, and what part of the context loss // process we currently are at. // This is used to support the WebGL spec's asyncronous nature in handling @@ -1218,20 +1232,16 @@ protected: WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateDrawModeEnum(GLenum mode, const char* info); bool ValidateAttribIndex(GLuint index, const char* info); bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type, WebGLboolean normalized, GLsizei stride, WebGLintptr byteOffset, const char* info); bool ValidateStencilParamsForDrawCall(); - bool ValidateGLSLVariableName(const nsAString& name, const char* info); - bool ValidateGLSLCharacter(char16_t c); - bool ValidateGLSLString(const nsAString& string, const char* info); - bool ValidateCopyTexImage(GLenum internalFormat, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateSamplerParameterName(GLenum pname, const char* info); bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info); bool ValidateTexImage(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, @@ -1268,16 +1278,20 @@ protected: GLsizei levelHeight, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateCompTexImageDataSize(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, uint32_t byteLength, WebGLTexImageFunc func, WebGLTexDimensions dims); + bool ValidateUniformLocationForProgram(WebGLUniformLocation* location, + WebGLProgram* program, + const char* funcName); + void Invalidate(); void DestroyResourcesAndContext(); void MakeContextCurrent() const; // helpers // If jsArrayType is MaxTypedArrayViewType, it means no array. @@ -1396,16 +1410,17 @@ protected: void ForceLoseContext(bool simulateLoss = false); void ForceRestoreContext(); nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures; nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures; nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures; WebGLRefPtr<WebGLProgram> mCurrentProgram; + RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo; uint32_t mMaxFramebufferColorAttachments; WebGLRefPtr<WebGLFramebuffer> mBoundFramebuffer; WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer; WebGLRefPtr<WebGLTransformFeedback> mBoundTransformFeedback; WebGLRefPtr<WebGLVertexArray> mBoundVertexArray; @@ -1585,17 +1600,17 @@ inline bool WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType* object) { MOZ_ASSERT(object); if (!ValidateObjectAllowDeletedOrNull(info, object)) return false; if (object->IsDeleted()) { - ErrorInvalidValue("%s: deleted object passed as argument", info); + ErrorInvalidValue("%s: Deleted object passed as argument.", info); return false; } return true; } template<class ObjectType> inline bool
--- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -9,17 +9,16 @@ #include "mozilla/CheckedInt.h" #include "WebGLBuffer.h" #include "WebGLContextUtils.h" #include "WebGLFramebuffer.h" #include "WebGLProgram.h" #include "WebGLRenderbuffer.h" #include "WebGLShader.h" #include "WebGLTexture.h" -#include "WebGLUniformInfo.h" #include "WebGLVertexArray.h" #include "WebGLVertexAttribData.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gl; // For a Tegra workaround. @@ -405,21 +404,25 @@ void WebGLContext::Draw_cleanup() /* * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount) * that will be legal to be read from bound VBOs. */ bool WebGLContext::ValidateBufferFetching(const char* info) { + MOZ_ASSERT(mCurrentProgram); + // Note that mCurrentProgram->IsLinked() is NOT GUARANTEED. + MOZ_ASSERT(mActiveProgramLinkInfo); + #ifdef DEBUG GLint currentProgram = 0; MakeContextCurrent(); gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram); - MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(), + MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->mGLName, "WebGL: current program doesn't agree with GL state"); #endif if (mBufferFetchingIsVerified) return true; bool hasPerVertex = false; uint32_t maxVertices = UINT32_MAX; @@ -436,17 +439,17 @@ WebGLContext::ValidateBufferFetching(con if (vd.buf == nullptr) { ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i); return false; } // If the attrib is not in use, then we don't have to validate // it, just need to make sure that the binding is non-null. - if (!mCurrentProgram->IsAttribInUse(i)) + if (!mActiveProgramLinkInfo->HasActiveAttrib(i)) continue; // the base offset CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset; CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size; if (!checked_byteLength.isValid() || !checked_sizeOfLastElement.isValid()) @@ -493,34 +496,35 @@ WebGLContext::ValidateBufferFetching(con return true; } WebGLVertexAttrib0Status WebGLContext::WhatDoesVertexAttrib0Need() { MOZ_ASSERT(mCurrentProgram); + MOZ_ASSERT(mActiveProgramLinkInfo); // work around Mac OSX crash, see bug 631420 #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs() && mBoundVertexArray->IsAttribArrayEnabled(0) && - !mCurrentProgram->IsAttribInUse(0)) + !mActiveProgramLinkInfo->HasActiveAttrib(0)) { return WebGLVertexAttrib0Status::EmulatedUninitializedArray; } #endif if (MOZ_LIKELY(gl->IsGLES() || mBoundVertexArray->IsAttribArrayEnabled(0))) { return WebGLVertexAttrib0Status::Default; } - return mCurrentProgram->IsAttribInUse(0) + return mActiveProgramLinkInfo->HasActiveAttrib(0) ? WebGLVertexAttrib0Status::EmulatedInitializedArray : WebGLVertexAttrib0Status::EmulatedUninitializedArray; } bool WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount) { WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
--- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -47,21 +47,18 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/ImageData.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/Endian.h" #include "mozilla/fallible.h" using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::gfx; using namespace mozilla::gl; -using namespace mozilla::gfx; - -static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum* baseType, - GLint* unitSize); const WebGLRectangleObject* WebGLContext::CurValidFBRectObject() const { const WebGLRectangleObject* rect = nullptr; if (mBoundFramebuffer) { // We don't really need to ask the driver. @@ -104,63 +101,34 @@ WebGLContext::ActiveTexture(GLenum textu void WebGLContext::AttachShader(WebGLProgram* program, WebGLShader* shader) { if (IsContextLost()) return; if (!ValidateObject("attachShader: program", program) || !ValidateObject("attachShader: shader", shader)) + { return; - - // Per GLSL ES 2.0, we can only have one of each type of shader - // attached. This renders the next test somewhat moot, but we'll - // leave it for when we support more than one shader of each type. - if (program->HasAttachedShaderOfType(shader->ShaderType())) - return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program"); - - if (!program->AttachShader(shader)) - return ErrorInvalidOperation("attachShader: shader is already attached"); + } + + program->AttachShader(shader); } void WebGLContext::BindAttribLocation(WebGLProgram* prog, GLuint location, const nsAString& name) { if (IsContextLost()) return; if (!ValidateObject("bindAttribLocation: program", prog)) return; - GLuint progname = prog->GLName(); - - if (!ValidateGLSLVariableName(name, "bindAttribLocation")) - return; - - if (location >= MaxVertexAttribs()) { - return ErrorInvalidValue("bindAttribLocation: `location` must be less" - " than MAX_VERTEX_ATTRIBS."); - } - - if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) - return ErrorInvalidOperation("bindAttribLocation: can't set the" - " location of a name that starts with" - " 'gl_'."); - - NS_LossyConvertUTF16toASCII cname(name); - nsCString mappedName; - if (mShaderValidation) { - WebGLProgram::HashMapIdentifier(cname, &mappedName); - } else { - mappedName.Assign(cname); - } - - MakeContextCurrent(); - gl->fBindAttribLocation(progname, location, mappedName.get()); + prog->BindAttribLocation(location, name); } void WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb) { if (IsContextLost()) return; @@ -775,24 +743,25 @@ WebGLContext::DeleteShader(WebGLShader* } void WebGLContext::DetachShader(WebGLProgram* program, WebGLShader* shader) { if (IsContextLost()) return; + // It's valid to attempt to detach a deleted shader, since it's still a + // shader. if (!ValidateObject("detachShader: program", program) || - // it's valid to attempt to detach a deleted shader, since it's - // still a shader !ValidateObjectAllowDeleted("detashShader: shader", shader)) + { return; - - if (!program->DetachShader(shader)) - return ErrorInvalidOperation("detachShader: shader is not attached"); + } + + program->DetachShader(shader); } void WebGLContext::DepthFunc(GLenum func) { if (IsContextLost()) return; @@ -880,60 +849,16 @@ WebGLContext::FrontFace(GLenum mode) default: return ErrorInvalidEnumInfo("frontFace: mode", mode); } MakeContextCurrent(); gl->fFrontFace(mode); } -already_AddRefed<WebGLActiveInfo> -WebGLContext::GetActiveAttrib(WebGLProgram* prog, uint32_t index) -{ - if (IsContextLost()) - return nullptr; - - if (!ValidateObject("getActiveAttrib: program", prog)) - return nullptr; - - MakeContextCurrent(); - GLuint progname = prog->GLName(); - - GLuint activeAttribs = 0; - gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTES, - (GLint*)&activeAttribs); - if (index >= activeAttribs) { - ErrorInvalidValue("`index` (%i) must be less than ACTIVE_ATTRIBUTES" - " (%i).", - index, activeAttribs); - return nullptr; - } - - GLint len = 0; - gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len); - if (len == 0) - return nullptr; - - nsAutoArrayPtr<char> name(new char[len]); - GLint attrsize = 0; - GLuint attrtype = 0; - - gl->fGetActiveAttrib(progname, index, len, &len, &attrsize, &attrtype, name); - if (attrsize == 0 || attrtype == 0) { - return nullptr; - } - - nsCString reverseMappedName; - prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName); - - nsRefPtr<WebGLActiveInfo> retActiveInfo = - new WebGLActiveInfo(attrsize, attrtype, reverseMappedName); - return retActiveInfo.forget(); -} - void WebGLContext::GenerateMipmap(GLenum rawTarget) { if (IsContextLost()) return; if (!ValidateTextureTargetEnum(rawTarget, "generateMipmap")) return; @@ -989,119 +914,68 @@ WebGLContext::GenerateMipmap(GLenum rawT gl->fGenerateMipmap(target.get()); gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, tex->MinFilter().get()); } else { gl->fGenerateMipmap(target.get()); } } already_AddRefed<WebGLActiveInfo> -WebGLContext::GetActiveUniform(WebGLProgram* prog, uint32_t index) +WebGLContext::GetActiveAttrib(WebGLProgram* prog, GLuint index) +{ + if (IsContextLost()) + return nullptr; + + if (!ValidateObject("getActiveAttrib: program", prog)) + return nullptr; + + return prog->GetActiveAttrib(index); +} + +already_AddRefed<WebGLActiveInfo> +WebGLContext::GetActiveUniform(WebGLProgram* prog, GLuint index) { if (IsContextLost()) return nullptr; if (!ValidateObject("getActiveUniform: program", prog)) return nullptr; - MakeContextCurrent(); - GLuint progname = prog->GLName(); - - GLuint activeUniforms = 0; - gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, - (GLint*)&activeUniforms); - if (index >= activeUniforms) { - ErrorInvalidValue("`index` (%i) must be less than ACTIVE_UNIFORMS" - " (%i).", - index, activeUniforms); - return nullptr; - } - - GLint len = 0; - gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len); - if (len == 0) - return nullptr; - - nsAutoArrayPtr<char> name(new char[len]); - - GLint usize = 0; - GLuint utype = 0; - - gl->fGetActiveUniform(progname, index, len, &len, &usize, &utype, name); - if (len == 0 || usize == 0 || utype == 0) { - return nullptr; - } - - nsCString reverseMappedName; - prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName); - - // OpenGL ES 2.0 specifies that if foo is a uniform array, GetActiveUniform returns its name as "foo[0]". - // See section 2.10 page 35 in the OpenGL ES 2.0.24 specification: - // - // > If the active uniform is an array, the uniform name returned in name will always - // > be the name of the uniform array appended with "[0]". - // - // There is no such requirement in the OpenGL (non-ES) spec and indeed we have OpenGL implementations returning - // "foo" instead of "foo[0]". So, when implementing WebGL on top of desktop OpenGL, we must check if the - // returned name ends in [0], and if it doesn't, append that. - // - // In principle we don't need to do that on OpenGL ES, but this is such a tricky difference between the ES and non-ES - // specs that it seems probable that some ES implementers will overlook it. Since the work-around is quite cheap, - // we do it unconditionally. - if (usize > 1 && reverseMappedName.CharAt(reverseMappedName.Length()-1) != ']') - reverseMappedName.AppendLiteral("[0]"); - - nsRefPtr<WebGLActiveInfo> retActiveInfo = - new WebGLActiveInfo(usize, utype, reverseMappedName); - return retActiveInfo.forget(); + return prog->GetActiveUniform(index); } void WebGLContext::GetAttachedShaders(WebGLProgram* prog, Nullable<nsTArray<nsRefPtr<WebGLShader>>>& retval) { retval.SetNull(); if (IsContextLost()) return; - if (!ValidateObjectAllowNull("getAttachedShaders", prog)) + if (!prog) { + ErrorInvalidValue("getAttachedShaders: Invalid program."); return; - - MakeContextCurrent(); - - if (!prog) { - retval.SetNull(); - ErrorInvalidValue("getAttachedShaders: invalid program"); - } else if (prog->AttachedShaders().Length() == 0) { - retval.SetValue().TruncateLength(0); - } else { - retval.SetValue().AppendElements(prog->AttachedShaders()); } + + if (!ValidateObject("getAttachedShaders", prog)) + return; + + prog->GetAttachedShaders(&retval.SetValue()); } GLint WebGLContext::GetAttribLocation(WebGLProgram* prog, const nsAString& name) { if (IsContextLost()) return -1; if (!ValidateObject("getAttribLocation: program", prog)) return -1; - if (!ValidateGLSLVariableName(name, "getAttribLocation")) - return -1; - - NS_LossyConvertUTF16toASCII cname(name); - nsCString mappedName; - prog->MapIdentifier(cname, &mappedName); - - GLuint progname = prog->GLName(); - - MakeContextCurrent(); - return gl->fGetAttribLocation(progname, mappedName.get()); + return prog->GetAttribLocation(name); } JS::Value WebGLContext::GetBufferParameter(GLenum target, GLenum pname) { if (IsContextLost()) return JS::NullValue(); @@ -1435,110 +1309,33 @@ JS::Value WebGLContext::GetProgramParameter(WebGLProgram* prog, GLenum pname) { if (IsContextLost()) return JS::NullValue(); if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog)) return JS::NullValue(); - GLuint progname = prog->GLName(); - - MakeContextCurrent(); - - GLint i = 0; - - if (IsWebGL2()) { - switch (pname) { - case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS: - case LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: - gl->fGetProgramiv(progname, pname, &i); - return JS::Int32Value(i); - } - } - - switch (pname) { - case LOCAL_GL_ATTACHED_SHADERS: - case LOCAL_GL_ACTIVE_UNIFORMS: - case LOCAL_GL_ACTIVE_ATTRIBUTES: - gl->fGetProgramiv(progname, pname, &i); - return JS::Int32Value(i); - - case LOCAL_GL_DELETE_STATUS: - return JS::BooleanValue(prog->IsDeleteRequested()); - - case LOCAL_GL_LINK_STATUS: - return JS::BooleanValue(prog->LinkStatus()); - - case LOCAL_GL_VALIDATE_STATUS: -#ifdef XP_MACOSX - // See comment in ValidateProgram below. - if (gl->WorkAroundDriverBugs()) - i = 1; - else - gl->fGetProgramiv(progname, pname, &i); -#else - gl->fGetProgramiv(progname, pname, &i); -#endif - return JS::BooleanValue(bool(i)); - - default: - ErrorInvalidEnumInfo("getProgramParameter: parameter", pname); - } - - return JS::NullValue(); + return prog->GetProgramParameter(pname); } void WebGLContext::GetProgramInfoLog(WebGLProgram* prog, nsAString& retval) { - nsAutoCString s; - GetProgramInfoLog(prog, s); - if (s.IsVoid()) - retval.SetIsVoid(true); - else - CopyASCIItoUTF16(s, retval); -} - -void -WebGLContext::GetProgramInfoLog(WebGLProgram* prog, nsACString& retval) -{ + retval.SetIsVoid(true); + if (IsContextLost()) - { - retval.SetIsVoid(true); - return; - } - - if (!ValidateObject("getProgramInfoLog: program", prog)) { - retval.Truncate(); return; - } - - GLuint progname = prog->GLName(); - - MakeContextCurrent(); - - GLint k = -1; - gl->fGetProgramiv(progname, LOCAL_GL_INFO_LOG_LENGTH, &k); - if (k == -1) { - // If GetProgramiv doesn't modify |k|, - // it's because there was a GL error. - // GetProgramInfoLog should return null on error. (Bug 746740) - retval.SetIsVoid(true); + + if (!ValidateObject("getProgramInfoLog: program", prog)) return; - } - - if (k == 0) { - retval.Truncate(); - return; - } - - retval.SetCapacity(k); - gl->fGetProgramInfoLog(progname, k, &k, (char*) retval.BeginWriting()); - retval.SetLength(k); + + prog->GetProgramInfoLog(&retval); + + retval.SetIsVoid(false); } // here we have to support all pnames with both int and float params. // See this discussion: // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname, GLint* intParamPtr, GLfloat* floatParamPtr) @@ -1736,181 +1533,44 @@ WebGLContext::GetTexParameterInternal(co default: ErrorInvalidEnumInfo("getTexParameter: parameter", pname); } return JS::NullValue(); } JS::Value -WebGLContext::GetUniform(JSContext* cx, WebGLProgram* prog, - WebGLUniformLocation* location) +WebGLContext::GetUniform(JSContext* js, WebGLProgram* prog, + WebGLUniformLocation* loc) { if (IsContextLost()) return JS::NullValue(); - if (!ValidateObject("getUniform: program", prog)) - return JS::NullValue(); - - if (!ValidateObject("getUniform: location", location)) + if (!ValidateObject("getUniform: `program`", prog)) return JS::NullValue(); - if (location->Program() != prog) { - ErrorInvalidValue("getUniform: this uniform location corresponds to another program"); - return JS::NullValue(); - } - - if (location->ProgramGeneration() != prog->Generation()) { - ErrorInvalidOperation("getUniform: this uniform location is obsolete since the program has been relinked"); + if (!ValidateObject("getUniform: `location`", loc)) return JS::NullValue(); - } - - GLuint progname = prog->GLName(); - - MakeContextCurrent(); - - GLint uniforms = 0; - GLint uniformNameMaxLength = 0; - gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, &uniforms); - gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength); - - // we now need the type info to switch between fGetUniformfv and fGetUniformiv - // the only way to get that is to iterate through all active uniforms by index until - // one matches the given uniform location. - GLenum uniformType = 0; - nsAutoArrayPtr<GLchar> uniformName(new GLchar[uniformNameMaxLength]); - // this buffer has 16 more bytes to be able to store [index] at the end. - nsAutoArrayPtr<GLchar> uniformNameBracketIndex(new GLchar[uniformNameMaxLength + 16]); - - GLint index; - for (index = 0; index < uniforms; ++index) { - GLsizei length; - GLint size; - gl->fGetActiveUniform(progname, index, uniformNameMaxLength, &length, - &size, &uniformType, uniformName); - if (gl->fGetUniformLocation(progname, uniformName) == location->Location()) - break; - - // now we handle the case of array uniforms. In that case, fGetActiveUniform returned as 'size' - // the biggest index used plus one, so we need to loop over that. The 0 index has already been handled above, - // so we can start at one. For each index, we construct the string uniformName + "[" + index + "]". - if (size > 1) { - bool found_it = false; - if (uniformName[length - 1] == ']') { // if uniformName ends in [0] - // remove the [0] at the end - length -= 3; - uniformName[length] = 0; - } - for (GLint arrayIndex = 1; arrayIndex < size; arrayIndex++) { - sprintf(uniformNameBracketIndex.get(), "%s[%d]", uniformName.get(), arrayIndex); - if (gl->fGetUniformLocation(progname, uniformNameBracketIndex) == location->Location()) { - found_it = true; - break; - } - } - if (found_it) break; - } - } - - if (index == uniforms) { - GenerateWarning("getUniform: internal error: hit an OpenGL driver bug"); + + if (!loc->ValidateForProgram(prog, this, "getUniform")) return JS::NullValue(); - } - - GLenum baseType; - GLint unitSize; - if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) { - GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType); - return JS::NullValue(); - } - - // this should never happen - if (unitSize > 16) { - GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize); - return JS::NullValue(); - } - - if (baseType == LOCAL_GL_FLOAT) { - GLfloat fv[16] = { GLfloat(0) }; - gl->fGetUniformfv(progname, location->Location(), fv); - if (unitSize == 1) { - return JS::DoubleValue(fv[0]); - } else { - JSObject* obj = Float32Array::Create(cx, this, unitSize, fv); - if (!obj) { - ErrorOutOfMemory("getUniform: out of memory"); - return JS::NullValue(); - } - return JS::ObjectOrNullValue(obj); - } - } else if (baseType == LOCAL_GL_INT) { - GLint iv[16] = { 0 }; - gl->fGetUniformiv(progname, location->Location(), iv); - if (unitSize == 1) { - return JS::Int32Value(iv[0]); - } else { - JSObject* obj = Int32Array::Create(cx, this, unitSize, iv); - if (!obj) { - ErrorOutOfMemory("getUniform: out of memory"); - return JS::NullValue(); - } - return JS::ObjectOrNullValue(obj); - } - } else if (baseType == LOCAL_GL_BOOL) { - GLint iv[16] = { 0 }; - gl->fGetUniformiv(progname, location->Location(), iv); - if (unitSize == 1) { - return JS::BooleanValue(iv[0] ? true : false); - } else { - bool uv[16]; - for (int k = 0; k < unitSize; k++) - uv[k] = iv[k]; - JS::Rooted<JS::Value> val(cx); - // Be careful: we don't want to convert all of |uv|! - if (!ToJSValue(cx, uv, unitSize, &val)) { - ErrorOutOfMemory("getUniform: out of memory"); - return JS::NullValue(); - } - return val; - } - } - - // Else preserving behavior, but I'm not sure this is correct per spec - return JS::UndefinedValue(); + + return loc->GetUniform(js, this); } already_AddRefed<WebGLUniformLocation> WebGLContext::GetUniformLocation(WebGLProgram* prog, const nsAString& name) { if (IsContextLost()) return nullptr; if (!ValidateObject("getUniformLocation: program", prog)) return nullptr; - if (!ValidateGLSLVariableName(name, "getUniformLocation")) - return nullptr; - - NS_LossyConvertUTF16toASCII cname(name); - nsCString mappedName; - prog->MapIdentifier(cname, &mappedName); - - GLuint progname = prog->GLName(); - MakeContextCurrent(); - GLint intlocation = gl->fGetUniformLocation(progname, mappedName.get()); - - nsRefPtr<WebGLUniformLocation> loc; - if (intlocation >= 0) { - WebGLUniformInfo info = prog->GetUniformInfoForMappedIdentifier(mappedName); - loc = new WebGLUniformLocation(this, - prog, - intlocation, - info); - } - return loc.forget(); + return prog->GetUniformLocation(name); } void WebGLContext::Hint(GLenum target, GLenum mode) { if (IsContextLost()) return; @@ -1980,175 +1640,37 @@ WebGLContext::IsTexture(WebGLTexture* te if (IsContextLost()) return false; return ValidateObjectAllowDeleted("isTexture", tex) && !tex->IsDeleted() && tex->HasEverBeenBound(); } -// Try to bind an attribute that is an array to location 0: -bool -WebGLContext::BindArrayAttribToLocation0(WebGLProgram* program) -{ - if (mBoundVertexArray->IsAttribArrayEnabled(0)) { - return false; - } - - GLint leastArrayLocation = -1; - - std::map<GLint, nsCString>::iterator itr; - for (itr = program->mActiveAttribMap.begin(); - itr != program->mActiveAttribMap.end(); - itr++) { - int32_t index = itr->first; - if (mBoundVertexArray->IsAttribArrayEnabled(index) && - index < leastArrayLocation) - { - leastArrayLocation = index; - } - } - - if (leastArrayLocation > 0) { - nsCString& attrName = program->mActiveAttribMap.find(leastArrayLocation)->second; - const char* attrNameCStr = attrName.get(); - gl->fBindAttribLocation(program->GLName(), 0, attrNameCStr); - return true; - } - return false; -} - -static void -LinkAndUpdateProgram(GLContext* gl, WebGLProgram* prog) -{ - GLuint name = prog->GLName(); - gl->fLinkProgram(name); - - prog->SetLinkStatus(false); - - GLint ok = 0; - gl->fGetProgramiv(name, LOCAL_GL_LINK_STATUS, &ok); - if (!ok) - return; - - if (!prog->UpdateInfo()) - return; - - prog->SetLinkStatus(true); -} - void -WebGLContext::LinkProgram(WebGLProgram* program) +WebGLContext::LinkProgram(WebGLProgram* prog) { if (IsContextLost()) return; - if (!ValidateObject("linkProgram", program)) - return; - - InvalidateBufferFetching(); // we do it early in this function - // as some of the validation below changes program state - - if (!program->NextGeneration()) { - // XXX throw? - return; - } - - if (!program->HasBothShaderTypesAttached()) { - GenerateWarning("linkProgram: this program doesn't have both a vertex" - " shader and a fragment shader"); - program->SetLinkStatus(false); - return; - } - - if (program->HasBadShaderAttached()) { - GenerateWarning("linkProgram: The program has bad shaders attached."); - program->SetLinkStatus(false); + if (!ValidateObject("linkProgram", prog)) return; - } - - // bug 777028 - // Mesa can't handle more than 16 samplers per program, counting each array entry. - if (gl->WorkAroundDriverBugs() && - mIsMesa && - program->UpperBoundNumSamplerUniforms() > 16) - { - GenerateWarning("Programs with more than 16 samplers are disallowed on" - " Mesa drivers to avoid a Mesa crasher."); - program->SetLinkStatus(false); - return; - } - - MakeContextCurrent(); - LinkAndUpdateProgram(gl, program); - - if (program->LinkStatus()) { - if (BindArrayAttribToLocation0(program)) { - GenerateWarning("linkProgram: Relinking program to make attrib0 an" - " array."); - LinkAndUpdateProgram(gl, program); + + prog->LinkProgram(); + + if (prog->IsLinked()) { + mActiveProgramLinkInfo = prog->LinkInfo(); + + if (gl->WorkAroundDriverBugs() && + gl->Vendor() == gl::GLVendor::NVIDIA) + { + if (mCurrentProgram == prog) + gl->fUseProgram(prog->mGLName); } } - - if (!program->LinkStatus()) { - if (ShouldGenerateWarnings()) { - // report shader/program infoLogs as warnings. - // note that shader compilation errors can be deferred to linkProgram, - // which is why we can't do anything in compileShader. In practice we could - // report in compileShader the translation errors generated by ANGLE, - // but it seems saner to keep a single way of obtaining shader infologs. - - nsAutoCString log; - - bool alreadyReportedShaderInfoLog = false; - - for (size_t i = 0; i < program->AttachedShaders().Length(); i++) { - - WebGLShader* shader = program->AttachedShaders()[i]; - - if (shader->CompileStatus()) - continue; - - const char* shaderTypeName = nullptr; - if (shader->ShaderType() == LOCAL_GL_VERTEX_SHADER) { - shaderTypeName = "vertex"; - } else if (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) { - shaderTypeName = "fragment"; - } else { - // should have been validated earlier - MOZ_ASSERT(false); - shaderTypeName = "<unknown>"; - } - - GetShaderInfoLog(shader, log); - - GenerateWarning("linkProgram: a %s shader used in this program failed to " - "compile, with this log:\n%s\n", - shaderTypeName, - log.get()); - alreadyReportedShaderInfoLog = true; - } - - if (!alreadyReportedShaderInfoLog) { - GetProgramInfoLog(program, log); - if (!log.IsEmpty()) { - GenerateWarning("linkProgram failed, with this log:\n%s\n", - log.get()); - } - } - } - return; - } - - if (gl->WorkAroundDriverBugs() && - gl->Vendor() == gl::GLVendor::NVIDIA) - { - if (program == mCurrentProgram) - gl->fUseProgram(program->GLName()); - } } void WebGLContext::PixelStorei(GLenum pname, GLint param) { if (IsContextLost()) return; @@ -2777,17 +2299,17 @@ WebGLContext::SurfaceFromElementResultTo void WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1) { GLuint rawLoc; if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, "uniform1i", &rawLoc)) return; // Only uniform1i can take sampler settings. - if (!ValidateSamplerUniformSetter("Uniform1i", loc, a1)) + if (!loc->ValidateSamplerSetter(a1, this, "uniform1i")) return; MakeContextCurrent(); gl->fUniform1i(rawLoc, a1); } void WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2) @@ -2880,17 +2402,17 @@ WebGLContext::Uniform1iv_base(WebGLUnifo GLsizei numElementsToUpload; if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_INT, arrayLength, "uniform1iv", &rawLoc, &numElementsToUpload)) { return; } - if (!ValidateSamplerUniformSetter("uniform1iv", loc, data[0])) + if (!loc->ValidateSamplerSetter(data[0], this, "uniform1iv")) return; MakeContextCurrent(); gl->fUniform1iv(rawLoc, numElementsToUpload, data); } void WebGLContext::Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength, @@ -2900,18 +2422,18 @@ WebGLContext::Uniform2iv_base(WebGLUnifo GLsizei numElementsToUpload; if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_INT, arrayLength, "uniform2iv", &rawLoc, &numElementsToUpload)) { return; } - if (!ValidateSamplerUniformSetter("uniform2iv", loc, data[0]) || - !ValidateSamplerUniformSetter("uniform2iv", loc, data[1])) + if (!loc->ValidateSamplerSetter(data[0], this, "uniform2iv") || + !loc->ValidateSamplerSetter(data[1], this, "uniform2iv")) { return; } MakeContextCurrent(); gl->fUniform2iv(rawLoc, numElementsToUpload, data); } @@ -2923,19 +2445,19 @@ WebGLContext::Uniform3iv_base(WebGLUnifo GLsizei numElementsToUpload; if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_INT, arrayLength, "uniform3iv", &rawLoc, &numElementsToUpload)) { return; } - if (!ValidateSamplerUniformSetter("uniform3iv", loc, data[0]) || - !ValidateSamplerUniformSetter("uniform3iv", loc, data[1]) || - !ValidateSamplerUniformSetter("uniform3iv", loc, data[2])) + if (!loc->ValidateSamplerSetter(data[0], this, "uniform3iv") || + !loc->ValidateSamplerSetter(data[1], this, "uniform3iv") || + !loc->ValidateSamplerSetter(data[2], this, "uniform3iv")) { return; } MakeContextCurrent(); gl->fUniform3iv(rawLoc, numElementsToUpload, data); } @@ -2947,20 +2469,20 @@ WebGLContext::Uniform4iv_base(WebGLUnifo GLsizei numElementsToUpload; if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_INT, arrayLength, "uniform4iv", &rawLoc, &numElementsToUpload)) { return; } - if (!ValidateSamplerUniformSetter("uniform4iv", loc, data[0]) || - !ValidateSamplerUniformSetter("uniform4iv", loc, data[1]) || - !ValidateSamplerUniformSetter("uniform4iv", loc, data[2]) || - !ValidateSamplerUniformSetter("uniform4iv", loc, data[3])) + if (!loc->ValidateSamplerSetter(data[0], this, "uniform4iv") || + !loc->ValidateSamplerSetter(data[1], this, "uniform4iv") || + !loc->ValidateSamplerSetter(data[2], this, "uniform4iv") || + !loc->ValidateSamplerSetter(data[3], this, "uniform4iv")) { return; } MakeContextCurrent(); gl->fUniform4iv(rawLoc, numElementsToUpload, data); } @@ -3089,54 +2611,41 @@ WebGLContext::UniformMatrix4fv_base(WebG //////////////////////////////////////////////////////////////////////////////// void WebGLContext::UseProgram(WebGLProgram* prog) { if (IsContextLost()) return; - if (!ValidateObjectAllowNull("useProgram", prog)) + if (!prog) { + mCurrentProgram = nullptr; + mActiveProgramLinkInfo = nullptr; return; - - MakeContextCurrent(); - - InvalidateBufferFetching(); - - GLuint progname = prog ? prog->GLName() : 0; - - if (prog && !prog->LinkStatus()) - return ErrorInvalidOperation("useProgram: program was not linked successfully"); - - gl->fUseProgram(progname); - - mCurrentProgram = prog; + } + + if (!ValidateObject("useProgram", prog)) + return; + + if (prog->UseProgram()) { + mCurrentProgram = prog; + mActiveProgramLinkInfo = mCurrentProgram->LinkInfo(); + } } void WebGLContext::ValidateProgram(WebGLProgram* prog) { if (IsContextLost()) return; if (!ValidateObject("validateProgram", prog)) return; - MakeContextCurrent(); - -#ifdef XP_MACOSX - // see bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed with Mac OS 10.6.7 - if (gl->WorkAroundDriverBugs()) { - GenerateWarning("validateProgram: implemented as a no-operation on Mac to work around crashes"); - return; - } -#endif - - GLuint progname = prog->GLName(); - gl->fValidateProgram(progname); + prog->ValidateProgram(); } already_AddRefed<WebGLFramebuffer> WebGLContext::CreateFramebuffer() { if (IsContextLost()) return nullptr; @@ -3179,315 +2688,17 @@ void WebGLContext::CompileShader(WebGLShader* shader) { if (IsContextLost()) return; if (!ValidateObject("compileShader", shader)) return; - GLuint shadername = shader->GLName(); - - shader->SetCompileStatus(false); - - // nothing to do if the validator is disabled - if (!mShaderValidation) - return; - - // nothing to do if translation was already done - if (!shader->NeedsTranslation()) - return; - - MakeContextCurrent(); - - ShShaderOutput targetShaderSourceLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT; - - ShHandle compiler = 0; - ShBuiltInResources resources; - - memset(&resources, 0, sizeof(ShBuiltInResources)); - - ShInitBuiltInResources(&resources); - - resources.MaxVertexAttribs = mGLMaxVertexAttribs; - resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors; - resources.MaxVaryingVectors = mGLMaxVaryingVectors; - resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits; - resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits; - resources.MaxTextureImageUnits = mGLMaxTextureImageUnits; - resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; - resources.MaxDrawBuffers = mGLMaxDrawBuffers; - - if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) - resources.EXT_frag_depth = 1; - - if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) - resources.OES_standard_derivatives = 1; - - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) - resources.EXT_draw_buffers = 1; - - if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) - resources.EXT_shader_texture_lod = 1; - - // Tell ANGLE to allow highp in frag shaders. (unless disabled) - // If underlying GLES doesn't have highp in frag shaders, it should complain anyways. - resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1; - - resources.HashFunction = WebGLProgram::IdentifierHashFunction; - - if (gl->WorkAroundDriverBugs()) { -#ifdef XP_MACOSX - if (gl->Vendor() == gl::GLVendor::NVIDIA) { - // Work around bug 890432 - resources.MaxExpressionComplexity = 1000; - } -#endif - } - - // We're storing an actual instance of StripComments because, if we don't, the - // cleanSource nsAString instance will be destroyed before the reference is - // actually used. - StripComments stripComments(shader->Source()); - const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length()); - if (!ValidateGLSLString(cleanSource, "compileShader")) - return; - - // shaderSource() already checks that the source stripped of comments is in the - // 7-bit ASCII range, so we can skip the NS_IsAscii() check. - NS_LossyConvertUTF16toASCII sourceCString(cleanSource); - - if (gl->WorkAroundDriverBugs()) { - const uint32_t maxSourceLength = 0x3ffff; - if (sourceCString.Length() > maxSourceLength) - return ErrorInvalidValue("compileShader: source has more than %d characters", - maxSourceLength); - } - - const char* s = sourceCString.get(); - -#define WEBGL2_BYPASS_ANGLE -#ifdef WEBGL2_BYPASS_ANGLE - /* - * The bypass don't bring a full support for GLSL ES 3.0, but the main purpose - * is to natively bring gl_InstanceID (to do instanced rendering) and gl_FragData - * - * To remove the bypass code, just comment #define WEBGL2_BYPASS_ANGLE above - * - * To bypass angle, the context must be a WebGL 2 and the shader must have the - * following line at the very top : - * #version proto-200 - * - * In this case, byPassANGLE == true and here is what we do : - * We create two shader source code: - * - one for the driver, that enable GL_EXT_gpu_shader4 - * - one for the angle compilor, to get informations about vertex attributes - * and uniforms - */ - static const char* bypassPrefixSearch = "#version proto-200"; - static const char* bypassANGLEPrefix[2] = {"precision mediump float;\n" - "#define gl_VertexID 0\n" - "#define gl_InstanceID 0\n", - - "precision mediump float;\n" - "#extension GL_EXT_draw_buffers : enable\n" - "#define gl_PrimitiveID 0\n"}; - - const bool bypassANGLE = IsWebGL2() && (strstr(s, bypassPrefixSearch) != 0); - - const char* angleShaderCode = s; - nsTArray<char> bypassANGLEShaderCode; - nsTArray<char> bypassDriverShaderCode; - - if (bypassANGLE) { - const int bypassStage = (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) ? 1 : 0; - const char* originalShader = strstr(s, bypassPrefixSearch) + strlen(bypassPrefixSearch); - int originalShaderSize = strlen(s) - (originalShader - s); - int bypassShaderCodeSize = originalShaderSize + 4096 + 1; - - bypassANGLEShaderCode.SetLength(bypassShaderCodeSize); - strcpy(bypassANGLEShaderCode.Elements(), bypassANGLEPrefix[bypassStage]); - strcat(bypassANGLEShaderCode.Elements(), originalShader); - - bypassDriverShaderCode.SetLength(bypassShaderCodeSize); - strcpy(bypassDriverShaderCode.Elements(), "#extension GL_EXT_gpu_shader4 : enable\n"); - strcat(bypassDriverShaderCode.Elements(), originalShader); - - angleShaderCode = bypassANGLEShaderCode.Elements(); - } -#endif - - compiler = ShConstructCompiler(shader->ShaderType(), - SH_WEBGL_SPEC, - targetShaderSourceLanguage, - &resources); - - int compileOptions = SH_VARIABLES | - SH_ENFORCE_PACKING_RESTRICTIONS | - SH_INIT_VARYINGS_WITHOUT_STATIC_USE | - SH_OBJECT_CODE | - SH_LIMIT_CALL_STACK_DEPTH; - - if (resources.MaxExpressionComplexity > 0) { - compileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY; - } - -#ifndef XP_MACOSX - // We want to do this everywhere, but to do this on Mac, we need - // to do it only on Mac OSX > 10.6 as this causes the shader - // compiler in 10.6 to crash - compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS; -#endif - -#ifdef XP_MACOSX - if (gl->WorkAroundDriverBugs()) { - // Work around bug 665578 and bug 769810 - if (gl->Vendor() == gl::GLVendor::ATI) { - compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; - } - - // Work around bug 735560 - if (gl->Vendor() == gl::GLVendor::Intel) { - compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; - } - - // Work around bug 636926 - if (gl->Vendor() == gl::GLVendor::NVIDIA) { - compileOptions |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX; - } - - // Work around https://bugs.webkit.org/show_bug.cgi?id=124684, - // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb - compileOptions |= SH_UNFOLD_SHORT_CIRCUIT; - } -#endif - -#ifdef WEBGL2_BYPASS_ANGLE - if (!ShCompile(compiler, &angleShaderCode, 1, compileOptions)) { -#else - if (!ShCompile(compiler, &s, 1, compileOptions)) { -#endif - size_t lenWithNull = 0; - ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &lenWithNull); - - if (!lenWithNull) { - // Error in ShGetInfo. - shader->SetTranslationFailure(NS_LITERAL_CSTRING("Internal error: failed to get shader info log")); - } else { - size_t len = lenWithNull - 1; - - nsAutoCString info; - if (len) { - // Don't allocate or try to write to zero length string - info.SetLength(len); // Allocates len+1, for the null-term. - ShGetInfoLog(compiler, info.BeginWriting()); - } - shader->SetTranslationFailure(info); - } - ShDestruct(compiler); - shader->SetCompileStatus(false); - return; - } - - size_t num_attributes = 0; - ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTES, &num_attributes); - size_t num_uniforms = 0; - ShGetInfo(compiler, SH_ACTIVE_UNIFORMS, &num_uniforms); - size_t attrib_max_length = 0; - ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attrib_max_length); - size_t uniform_max_length = 0; - ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_length); - size_t mapped_max_length = 0; - ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_max_length); - - shader->mAttribMaxNameLength = attrib_max_length; - - shader->mAttributes.Clear(); - shader->mUniforms.Clear(); - shader->mUniformInfos.Clear(); - - nsAutoArrayPtr<char> attribute_name(new char[attrib_max_length+1]); - nsAutoArrayPtr<char> uniform_name(new char[uniform_max_length+1]); - nsAutoArrayPtr<char> mapped_name(new char[mapped_max_length+1]); - - for (size_t i = 0; i < num_uniforms; i++) { - size_t length; - int size; - sh::GLenum type; - ShPrecisionType precision; - int staticUse; - ShGetVariableInfo(compiler, SH_ACTIVE_UNIFORMS, (int)i, - &length, &size, &type, - &precision, &staticUse, - uniform_name, - mapped_name); - - shader->mUniforms.AppendElement(WebGLMappedIdentifier( - nsDependentCString(uniform_name), - nsDependentCString(mapped_name))); - - // we need uniform info to validate uniform setter calls - char mappedNameLength = strlen(mapped_name); - char mappedNameLastChar = mappedNameLength > 1 - ? mapped_name[mappedNameLength - 1] - : 0; - shader->mUniformInfos.AppendElement(WebGLUniformInfo( - size, - mappedNameLastChar == ']', - type)); - } - - for (size_t i = 0; i < num_attributes; i++) { - size_t length; - int size; - sh::GLenum type; - ShPrecisionType precision; - int staticUse; - ShGetVariableInfo(compiler, SH_ACTIVE_ATTRIBUTES, (int)i, - &length, &size, &type, - &precision, &staticUse, - attribute_name, - mapped_name); - shader->mAttributes.AppendElement(WebGLMappedIdentifier( - nsDependentCString(attribute_name), - nsDependentCString(mapped_name))); - } - - size_t lenWithNull = 0; - ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &lenWithNull); - MOZ_ASSERT(lenWithNull >= 1); - size_t len = lenWithNull - 1; - - nsAutoCString translatedSrc; - translatedSrc.SetLength(len); // Allocates len+1, for the null-term. - ShGetObjectCode(compiler, translatedSrc.BeginWriting()); - - CopyASCIItoUTF16(translatedSrc, shader->mTranslatedSource); - - const char* ts = translatedSrc.get(); - -#ifdef WEBGL2_BYPASS_ANGLE - if (bypassANGLE) { - const char* driverShaderCode = bypassDriverShaderCode.Elements(); - gl->fShaderSource(shadername, 1, (const GLchar**) &driverShaderCode, nullptr); - } else { - gl->fShaderSource(shadername, 1, &ts, nullptr); - } -#else - gl->fShaderSource(shadername, 1, &ts, nullptr); -#endif - - shader->SetTranslationSuccess(); - - ShDestruct(compiler); - - gl->fCompileShader(shadername); - GLint ok; - gl->fGetShaderiv(shadername, LOCAL_GL_COMPILE_STATUS, &ok); - shader->SetCompileStatus(ok); + shader->CompileShader(); } void WebGLContext::CompressedTexImage2D(GLenum rawTexImgTarget, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, const ArrayBufferView& view) @@ -3609,91 +2820,33 @@ JS::Value WebGLContext::GetShaderParameter(WebGLShader* shader, GLenum pname) { if (IsContextLost()) return JS::NullValue(); if (!ValidateObject("getShaderParameter: shader", shader)) return JS::NullValue(); - GLuint shadername = shader->GLName(); - - MakeContextCurrent(); - - switch (pname) { - case LOCAL_GL_SHADER_TYPE: - { - GLint i = 0; - gl->fGetShaderiv(shadername, pname, &i); - return JS::NumberValue(uint32_t(i)); - } - break; - case LOCAL_GL_DELETE_STATUS: - return JS::BooleanValue(shader->IsDeleteRequested()); - break; - case LOCAL_GL_COMPILE_STATUS: - { - GLint i = 0; - gl->fGetShaderiv(shadername, pname, &i); - return JS::BooleanValue(bool(i)); - } - break; - default: - ErrorInvalidEnumInfo("getShaderParameter: parameter", pname); - } - - return JS::NullValue(); + return shader->GetShaderParameter(pname); } void WebGLContext::GetShaderInfoLog(WebGLShader* shader, nsAString& retval) { - nsAutoCString s; - GetShaderInfoLog(shader, s); - if (s.IsVoid()) - retval.SetIsVoid(true); - else - CopyASCIItoUTF16(s, retval); -} - -void -WebGLContext::GetShaderInfoLog(WebGLShader* shader, nsACString& retval) -{ + retval.SetIsVoid(true); + if (IsContextLost()) - { - retval.SetIsVoid(true); return; - } if (!ValidateObject("getShaderInfoLog: shader", shader)) return; - retval = shader->TranslationLog(); - if (!retval.IsVoid()) { - return; - } - - MakeContextCurrent(); - - GLuint shadername = shader->GLName(); - GLint k = -1; - gl->fGetShaderiv(shadername, LOCAL_GL_INFO_LOG_LENGTH, &k); - if (k == -1) { - // XXX GL Error? should never happen. - return; - } - - if (k == 0) { - retval.Truncate(); - return; - } - - retval.SetCapacity(k); - gl->fGetShaderInfoLog(shadername, k, &k, (char*) retval.BeginWriting()); - retval.SetLength(k); + shader->GetShaderInfoLog(&retval); + + retval.SetIsVoid(false); } already_AddRefed<WebGLShaderPrecisionFormat> WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype) { if (IsContextLost()) return nullptr; @@ -3737,61 +2890,51 @@ WebGLContext::GetShaderPrecisionFormat(G nsRefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision); return retShaderPrecisionFormat.forget(); } void WebGLContext::GetShaderSource(WebGLShader* shader, nsAString& retval) { - if (IsContextLost()) { - retval.SetIsVoid(true); + retval.SetIsVoid(true); + + if (IsContextLost()) return; - } if (!ValidateObject("getShaderSource: shader", shader)) return; - retval.Assign(shader->Source()); + shader->GetShaderSource(&retval); } void WebGLContext::ShaderSource(WebGLShader* shader, const nsAString& source) { if (IsContextLost()) return; if (!ValidateObject("shaderSource: shader", shader)) return; - // We're storing an actual instance of StripComments because, if we don't, the - // cleanSource nsAString instance will be destroyed before the reference is - // actually used. - StripComments stripComments(source); - const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length()); - if (!ValidateGLSLString(cleanSource, "compileShader")) - return; - - shader->SetSource(source); - - shader->SetNeedsTranslation(); + shader->ShaderSource(source); } void WebGLContext::GetShaderTranslatedSource(WebGLShader* shader, nsAString& retval) { - if (IsContextLost()) { - retval.SetIsVoid(true); + retval.SetIsVoid(true); + + if (IsContextLost()) return; - } if (!ValidateObject("getShaderTranslatedSource: shader", shader)) return; - retval.Assign(shader->TranslatedSource()); + shader->GetShaderTranslatedSource(&retval); } GLenum WebGLContext::CheckedTexImage2D(TexImageTarget texImageTarget, GLint level, TexInternalFormat internalformat, GLsizei width, GLsizei height, GLint border, @@ -4259,87 +3402,16 @@ WebGLContext::RestoreContext() // restoreContext(). if (!mAllowContextRestore) return ErrorInvalidOperation("restoreContext: Context cannot be restored."); ForceRestoreContext(); } -bool -BaseTypeAndSizeFromUniformType(GLenum uType, GLenum* baseType, GLint* unitSize) -{ - switch (uType) { - case LOCAL_GL_INT: - case LOCAL_GL_INT_VEC2: - case LOCAL_GL_INT_VEC3: - case LOCAL_GL_INT_VEC4: - case LOCAL_GL_SAMPLER_2D: - case LOCAL_GL_SAMPLER_CUBE: - *baseType = LOCAL_GL_INT; - break; - case LOCAL_GL_FLOAT: - case LOCAL_GL_FLOAT_VEC2: - case LOCAL_GL_FLOAT_VEC3: - case LOCAL_GL_FLOAT_VEC4: - case LOCAL_GL_FLOAT_MAT2: - case LOCAL_GL_FLOAT_MAT3: - case LOCAL_GL_FLOAT_MAT4: - *baseType = LOCAL_GL_FLOAT; - break; - case LOCAL_GL_BOOL: - case LOCAL_GL_BOOL_VEC2: - case LOCAL_GL_BOOL_VEC3: - case LOCAL_GL_BOOL_VEC4: - *baseType = LOCAL_GL_BOOL; // pretend these are int - break; - default: - return false; - } - - switch (uType) { - case LOCAL_GL_INT: - case LOCAL_GL_FLOAT: - case LOCAL_GL_BOOL: - case LOCAL_GL_SAMPLER_2D: - case LOCAL_GL_SAMPLER_CUBE: - *unitSize = 1; - break; - case LOCAL_GL_INT_VEC2: - case LOCAL_GL_FLOAT_VEC2: - case LOCAL_GL_BOOL_VEC2: - *unitSize = 2; - break; - case LOCAL_GL_INT_VEC3: - case LOCAL_GL_FLOAT_VEC3: - case LOCAL_GL_BOOL_VEC3: - *unitSize = 3; - break; - case LOCAL_GL_INT_VEC4: - case LOCAL_GL_FLOAT_VEC4: - case LOCAL_GL_BOOL_VEC4: - *unitSize = 4; - break; - case LOCAL_GL_FLOAT_MAT2: - *unitSize = 4; - break; - case LOCAL_GL_FLOAT_MAT3: - *unitSize = 9; - break; - case LOCAL_GL_FLOAT_MAT4: - *unitSize = 16; - break; - default: - return false; - } - - return true; -} - - WebGLTexelFormat mozilla::GetWebGLTexelFormat(TexInternalFormat effectiveInternalFormat) { switch (effectiveInternalFormat.get()) { case LOCAL_GL_RGBA8: return WebGLTexelFormat::RGBA8; case LOCAL_GL_SRGB8_ALPHA8: return WebGLTexelFormat::RGBA8; case LOCAL_GL_RGB8: return WebGLTexelFormat::RGB8; case LOCAL_GL_SRGB8: return WebGLTexelFormat::RGB8;
--- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -1026,17 +1026,17 @@ WebGLContext::AssertCachedBindings() GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound); } // Bound object state GLuint bound = mBoundFramebuffer ? mBoundFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound); - bound = mCurrentProgram ? mCurrentProgram->GLName() : 0; + bound = mCurrentProgram ? mCurrentProgram->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound); // Textures GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0; AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture); WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D); bound = curTex ? curTex->GLName() : 0;
--- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -17,16 +17,17 @@ #include "WebGLBuffer.h" #include "WebGLContextUtils.h" #include "WebGLFramebuffer.h" #include "WebGLProgram.h" #include "WebGLRenderbuffer.h" #include "WebGLShader.h" #include "WebGLTexture.h" #include "WebGLUniformLocation.h" +#include "WebGLValidateStrings.h" #include "WebGLVertexArray.h" #include "WebGLVertexAttribData.h" #if defined(MOZ_WIDGET_COCOA) #include "nsCocoaFeatures.h" #endif namespace mozilla { @@ -362,60 +363,16 @@ WebGLContext::ValidateDrawModeEnum(GLenu return true; default: ErrorInvalidEnumInfo(info, mode); return false; } } -bool -WebGLContext::ValidateGLSLVariableName(const nsAString& name, const char* info) -{ - if (name.IsEmpty()) - return false; - - const uint32_t maxSize = 256; - if (name.Length() > maxSize) { - ErrorInvalidValue("%s: Identifier is %d characters long, exceeds the" - " maximum allowed length of %d characters.", info, - name.Length(), maxSize); - return false; - } - - if (!ValidateGLSLString(name, info)) - return false; - - nsString prefix1 = NS_LITERAL_STRING("webgl_"); - nsString prefix2 = NS_LITERAL_STRING("_webgl_"); - - if (Substring(name, 0, prefix1.Length()).Equals(prefix1) || - Substring(name, 0, prefix2.Length()).Equals(prefix2)) - { - ErrorInvalidOperation("%s: String contains a reserved GLSL prefix.", - info); - return false; - } - - return true; -} - -bool WebGLContext::ValidateGLSLString(const nsAString& string, const char* info) -{ - for (uint32_t i = 0; i < string.Length(); ++i) { - if (!ValidateGLSLCharacter(string.CharAt(i))) { - ErrorInvalidValue("%s: String contains the illegal character" - " '%d'.", info, string.CharAt(i)); - return false; - } - } - - return true; -} - /** * Return true if the framebuffer attachment is valid. Attachment must * be one of depth/stencil/depth_stencil/color attachment. */ bool WebGLContext::ValidateFramebufferAttachment(GLenum attachment, const char* funcName) { @@ -1508,261 +1465,129 @@ WebGLContext::ValidateTexImage(TexImageT return false; } // Parameters are OK return true; } bool -WebGLContext::ValidateUniformLocation(const char* info, - WebGLUniformLocation* loc) +WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName) { - if (!ValidateObjectAllowNull(info, loc)) - return false; - + /* GLES 2.0.25, p38: + * If the value of location is -1, the Uniform* commands will silently + * ignore the data passed in, and the current uniform values will not be + * changed. + */ if (!loc) return false; - // The need to check specifically for !mCurrentProgram here is explained in - // bug 657556. - if (!mCurrentProgram) { - ErrorInvalidOperation("%s: No program is currently bound.", info); + if (!ValidateObject(funcName, loc)) return false; - } - if (mCurrentProgram != loc->Program()) { - ErrorInvalidOperation("%s: This uniform location doesn't correspond to" - " the current program.", info); + if (!mCurrentProgram) { + ErrorInvalidOperation("%s: No program is currently bound.", funcName); return false; } - if (mCurrentProgram->Generation() != loc->ProgramGeneration()) { - ErrorInvalidOperation("%s: This uniform location is obsolete since the" - " program has been relinked.", info); - return false; - } - - return true; + return loc->ValidateForProgram(mCurrentProgram, this, funcName); } bool -WebGLContext::ValidateSamplerUniformSetter(const char* info, - WebGLUniformLocation* loc, - GLint value) -{ - if (loc->Info().type != LOCAL_GL_SAMPLER_2D && - loc->Info().type != LOCAL_GL_SAMPLER_CUBE) - { - return true; - } - - if (value >= 0 && value < mGLMaxTextureUnits) - return true; - - ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a" - " valid texture unit.", info, value); - return false; -} - -bool -WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t cnt, +WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize, uint32_t arrayLength) { if (IsContextLost()) return false; - if (arrayLength < cnt) { - ErrorInvalidOperation("%s: Array must be >= %d elements.", name, cnt); - return false; - } - - return true; -} - -static bool -IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType) -{ - switch (uniformType) { - case LOCAL_GL_BOOL: - case LOCAL_GL_BOOL_VEC2: - case LOCAL_GL_BOOL_VEC3: - case LOCAL_GL_BOOL_VEC4: - return true; // GLfloat(0.0) sets a bool to false. - - case LOCAL_GL_INT: - case LOCAL_GL_INT_SAMPLER_2D: - case LOCAL_GL_INT_SAMPLER_2D_ARRAY: - case LOCAL_GL_INT_SAMPLER_3D: - case LOCAL_GL_INT_SAMPLER_CUBE: - case LOCAL_GL_INT_VEC2: - case LOCAL_GL_INT_VEC3: - case LOCAL_GL_INT_VEC4: - case LOCAL_GL_SAMPLER_2D: - case LOCAL_GL_SAMPLER_2D_ARRAY: - case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: - case LOCAL_GL_SAMPLER_2D_SHADOW: - case LOCAL_GL_SAMPLER_CUBE: - case LOCAL_GL_SAMPLER_CUBE_SHADOW: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: - return setterType == LOCAL_GL_INT; - - case LOCAL_GL_FLOAT: - case LOCAL_GL_FLOAT_MAT2: - case LOCAL_GL_FLOAT_MAT2x3: - case LOCAL_GL_FLOAT_MAT2x4: - case LOCAL_GL_FLOAT_MAT3: - case LOCAL_GL_FLOAT_MAT3x2: - case LOCAL_GL_FLOAT_MAT3x4: - case LOCAL_GL_FLOAT_MAT4: - case LOCAL_GL_FLOAT_MAT4x2: - case LOCAL_GL_FLOAT_MAT4x3: - case LOCAL_GL_FLOAT_VEC2: - case LOCAL_GL_FLOAT_VEC3: - case LOCAL_GL_FLOAT_VEC4: - return setterType == LOCAL_GL_FLOAT; - - default: - MOZ_ASSERT(false); // should never get here - return false; - } -} - -static bool -CheckUniformSizeAndType(WebGLContext& webgl, WebGLUniformLocation* loc, - uint8_t setterElemSize, GLenum setterType, - const char* info) -{ - if (setterElemSize != loc->ElementSize()) { - webgl.ErrorInvalidOperation("%s: Bad uniform size: %i", info, - loc->ElementSize()); - return false; - } - - if (!IsUniformSetterTypeValid(setterType, loc->Info().type)) { - webgl.ErrorInvalidOperation("%s: Bad uniform type: %i", info, - loc->Info().type); - return false; - } - - return true; -} - -static bool -CheckUniformArrayLength(WebGLContext& webgl, WebGLUniformLocation* loc, - uint8_t setterElemSize, size_t setterArraySize, - const char* info) -{ - if (setterArraySize == 0 || - setterArraySize % setterElemSize) - { - webgl.ErrorInvalidValue("%s: expected an array of length a multiple of" - " %d, got an array of length %d.", info, - setterElemSize, setterArraySize); - return false; - } - - if (!loc->Info().isArray && - setterArraySize != setterElemSize) - { - webgl.ErrorInvalidOperation("%s: expected an array of length exactly %d" - " (since this uniform is not an array" - " uniform), got an array of length %d.", - info, setterElemSize, setterArraySize); + if (arrayLength < setterElemSize) { + ErrorInvalidOperation("%s: Array must have >= %d elements.", name, + setterElemSize); return false; } return true; } bool WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterElemSize, GLenum setterType, - const char* info, GLuint* out_rawLoc) + const char* funcName, GLuint* out_rawLoc) { if (IsContextLost()) return false; - if (!ValidateUniformLocation(info, loc)) + if (!ValidateUniformLocation(loc, funcName)) return false; - if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info)) + if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName)) return false; - *out_rawLoc = loc->Location(); + *out_rawLoc = loc->mLoc; return true; } bool WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc, uint8_t setterElemSize, GLenum setterType, size_t setterArraySize, - const char* info, + const char* funcName, GLuint* const out_rawLoc, GLsizei* const out_numElementsToUpload) { if (IsContextLost()) return false; - if (!ValidateUniformLocation(info, loc)) + if (!ValidateUniformLocation(loc, funcName)) return false; - if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info)) + if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName)) return false; - if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize, - info)) - { + if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName)) return false; - } - *out_rawLoc = loc->Location(); - *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize, + *out_rawLoc = loc->mLoc; + *out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount, setterArraySize / setterElemSize); return true; } bool WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc, uint8_t setterDims, GLenum setterType, size_t setterArraySize, bool setterTranspose, - const char* info, + const char* funcName, GLuint* const out_rawLoc, GLsizei* const out_numElementsToUpload) { uint8_t setterElemSize = setterDims * setterDims; if (IsContextLost()) return false; - if (!ValidateUniformLocation(info, loc)) + if (!ValidateUniformLocation(loc, funcName)) + return false; + + if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName)) return false; - if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info)) + if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName)) return false; - if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize, - info)) - { + if (setterTranspose) { + ErrorInvalidValue("%s: `transpose` must be false.", funcName); return false; } - if (setterTranspose) { - ErrorInvalidValue("%s: `transpose` must be false.", info); - return false; - } - - *out_rawLoc = loc->Location(); - *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize, + *out_rawLoc = loc->mLoc; + *out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount, setterArraySize / setterElemSize); return true; } bool WebGLContext::ValidateAttribIndex(GLuint index, const char* info) { bool valid = (index < MaxVertexAttribs()); @@ -2090,25 +1915,23 @@ WebGLContext::InitAndValidateGL() gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT); } #endif // Check the shader validator pref NS_ENSURE_TRUE(Preferences::GetRootBranch(), false); - mShaderValidation = Preferences::GetBool("webgl.shader_validator", - mShaderValidation); + mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation", + mBypassShaderValidation); // initialize shader translator - if (mShaderValidation) { - if (!ShInitialize()) { - GenerateWarning("GLSL translator initialization failed!"); - return false; - } + if (!ShInitialize()) { + GenerateWarning("GLSL translator initialization failed!"); + return false; } // Mesa can only be detected with the GL_VERSION string, of the form // "2.1 Mesa 7.11.0" const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION)); mIsMesa = strstr(versionStr, "Mesa"); // Notice that the point of calling fGetError here is not only to check for
--- a/dom/canvas/WebGLContextVertices.cpp +++ b/dom/canvas/WebGLContextVertices.cpp @@ -8,17 +8,16 @@ #include "GLContext.h" #include "mozilla/CheckedInt.h" #include "WebGLBuffer.h" #include "WebGLFramebuffer.h" #include "WebGLProgram.h" #include "WebGLRenderbuffer.h" #include "WebGLShader.h" #include "WebGLTexture.h" -#include "WebGLUniformInfo.h" #include "WebGLVertexArray.h" #include "WebGLVertexAttribData.h" using namespace mozilla; using namespace dom; void WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
--- a/dom/canvas/WebGLProgram.cpp +++ b/dom/canvas/WebGLProgram.cpp @@ -2,371 +2,697 @@ /* 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/. */ #include "WebGLProgram.h" #include "GLContext.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" -#include "MurmurHash3.h" #include "WebGLContext.h" #include "WebGLShader.h" +#include "WebGLUniformLocation.h" +#include "WebGLValidateStrings.h" namespace mozilla { -/** 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. - */ +/* If `name`: "foo[3]" + * Then returns true, with + * `out_baseName`: "foo" + * `out_isArray`: true + * `out_index`: 3 + * + * If `name`: "foo" + * Then returns true, with + * `out_baseName`: "foo" + * `out_isArray`: false + * `out_index`: <not written> + */ static bool -SplitLastSquareBracket(nsACString& string, nsCString& bracketPart) +ParseName(const nsCString& name, nsCString* const out_baseName, + bool* const out_isArray, size_t* const out_arrayIndex) { - MOZ_ASSERT(bracketPart.IsEmpty(), - "SplitLastSquareBracket must be called with empty bracketPart" - " string."); + int32_t indexEnd = name.RFind("]"); + if (indexEnd == -1 || + (uint32_t)indexEnd != name.Length() - 1) + { + *out_baseName = name; + *out_isArray = false; + return true; + } - if (string.IsEmpty()) + int32_t indexOpenBracket = name.RFind("["); + if (indexOpenBracket == -1) return false; - char* string_start = string.BeginWriting(); - char* s = string_start + string.Length() - 1; - - if (*s != ']') + uint32_t indexStart = indexOpenBracket + 1; + uint32_t indexLen = indexEnd - indexStart; + if (indexLen == 0) return false; - while (*s != '[' && s != string_start) - s--; + const nsAutoCString indexStr(Substring(name, indexStart, indexLen)); - if (*s != '[') + nsresult errorcode; + int32_t indexNum = indexStr.ToInteger(&errorcode); + if (NS_FAILED(errorcode)) return false; - bracketPart.Assign(s); - *s = 0; - string.EndWriting(); - string.SetLength(s - string_start); + if (indexNum < 0) + return false; + + *out_baseName = StringHead(name, indexOpenBracket); + *out_isArray = true; + *out_arrayIndex = indexNum; return true; } -JSObject* -WebGLProgram::WrapObject(JSContext* cx) { - return dom::WebGLProgramBinding::Wrap(cx, this); +static void +AddActiveInfo(GLint elemCount, GLenum elemType, bool isArray, + const nsACString& baseUserName, const nsACString& baseMappedName, + std::vector<nsRefPtr<WebGLActiveInfo>>* activeInfoList, + std::map<nsCString, const WebGLActiveInfo*>* infoLocMap) +{ + nsRefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(elemCount, elemType, isArray, + baseUserName, baseMappedName); + activeInfoList->push_back(info); + + infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get())); +} + +//#define DUMP_SHADERVAR_MAPPINGS + +static TemporaryRef<const webgl::LinkedProgramInfo> +QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) +{ + RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog)); + + GLuint maxAttribLenWithNull = 0; + gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, + (GLint*)&maxAttribLenWithNull); + if (maxAttribLenWithNull < 1) + maxAttribLenWithNull = 1; + + GLuint maxUniformLenWithNull = 0; + gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, + (GLint*)&maxUniformLenWithNull); + if (maxUniformLenWithNull < 1) + maxUniformLenWithNull = 1; + +#ifdef DUMP_SHADERVAR_MAPPINGS + printf_stderr("maxAttribLenWithNull: %d\n", maxAttribLenWithNull); + printf_stderr("maxUniformLenWithNull: %d\n", maxUniformLenWithNull); +#endif + + // Attribs + + GLuint numActiveAttribs = 0; + gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, + (GLint*)&numActiveAttribs); + + for (GLuint i = 0; i < numActiveAttribs; i++) { + nsAutoCString mappedName; + mappedName.SetLength(maxAttribLenWithNull - 1); + + GLsizei lengthWithoutNull = 0; + GLint elemCount = 0; // `size` + GLenum elemType = 0; // `type` + gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull, + &elemCount, &elemType, mappedName.BeginWriting()); + + mappedName.SetLength(lengthWithoutNull); + + // Collect ActiveInfos: + + // Attribs can't be arrays, so we can skip some of the mess we have in the Uniform + // path. + nsDependentCString userName; + if (!prog->FindAttribUserNameByMappedName(mappedName, &userName)) + userName.Rebind(mappedName, 0); + +#ifdef DUMP_SHADERVAR_MAPPINGS + printf_stderr("[attrib %i] %s/%s\n", i, mappedName.BeginReading(), + userName.BeginReading()); + printf_stderr(" lengthWithoutNull: %d\n", lengthWithoutNull); +#endif + + const bool isArray = false; + AddActiveInfo(elemCount, elemType, isArray, userName, mappedName, + &info->activeAttribs, &info->attribMap); + + // Collect active locations: + GLint loc = gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading()); + if (loc == -1) + MOZ_CRASH("Active attrib has no location."); + + info->activeAttribLocs.insert(loc); + } + + // Uniforms + + const bool needsCheckForArrays = true; + + GLuint numActiveUniforms = 0; + gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS, + (GLint*)&numActiveUniforms); + + for (GLuint i = 0; i < numActiveUniforms; i++) { + nsAutoCString mappedName; + mappedName.SetLength(maxUniformLenWithNull - 1); + + GLsizei lengthWithoutNull = 0; + GLint elemCount = 0; // `size` + GLenum elemType = 0; // `type` + gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull, + &elemCount, &elemType, mappedName.BeginWriting()); + + mappedName.SetLength(lengthWithoutNull); + + nsAutoCString baseMappedName; + bool isArray; + size_t arrayIndex; + if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex)) + MOZ_CRASH("Failed to parse `mappedName` received from driver."); + + // Note that for good drivers, `isArray` should already be correct. + // However, if FindUniform succeeds, it will be validator-guaranteed correct. + + nsAutoCString baseUserName; + if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) { + baseUserName = baseMappedName; + + if (needsCheckForArrays && !isArray) { + // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is + // not an array. Our current linux Try slaves return the location of `foo` + // anyways, though. + std::string mappedName = baseMappedName.BeginReading(); + mappedName += "[0]"; + + GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedName.c_str()); + if (loc != -1) + isArray = true; + } + } + +#ifdef DUMP_SHADERVAR_MAPPINGS + printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(), + (int)isArray, baseMappedName.BeginReading(), + baseUserName.BeginReading()); + printf_stderr(" lengthWithoutNull: %d\n", lengthWithoutNull); + printf_stderr(" isArray: %d\n", (int)isArray); +#endif + + AddActiveInfo(elemCount, elemType, isArray, baseUserName, baseMappedName, + &info->activeUniforms, &info->uniformMap); + } + + return info.forget(); +} + +//////////////////////////////////////////////////////////////////////////////// + + +webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* aProg) + : prog(aProg) +{ } + +//////////////////////////////////////////////////////////////////////////////// +// WebGLProgram + +static GLuint +CreateProgram(gl::GLContext* gl) +{ + gl->MakeCurrent(); + return gl->fCreateProgram(); } WebGLProgram::WebGLProgram(WebGLContext* webgl) : WebGLContextBoundObject(webgl) - , mLinkStatus(false) - , mGeneration(0) - , mIdentifierMap(new CStringMap) - , mIdentifierReverseMap(new CStringMap) - , mUniformInfoMap(new CStringToUniformInfoMap) - , mAttribMaxNameLength(0) + , mGLName(CreateProgram(webgl->GL())) { - mContext->MakeContextCurrent(); - mGLName = mContext->gl->fCreateProgram(); mContext->mPrograms.insertBack(this); } void WebGLProgram::Delete() { - DetachShaders(); + gl::GLContext* gl = mContext->GL(); + + gl->MakeCurrent(); + gl->fDeleteProgram(mGLName); + + mVertShader = nullptr; + mFragShader = nullptr; + + mMostRecentLinkInfo = nullptr; + + LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms); +} + +//////////////////////////////////////////////////////////////////////////////// +// GL funcs + +void +WebGLProgram::AttachShader(WebGLShader* shader) +{ + WebGLRefPtr<WebGLShader>* shaderSlot; + switch (shader->mType) { + case LOCAL_GL_VERTEX_SHADER: + shaderSlot = &mVertShader; + break; + case LOCAL_GL_FRAGMENT_SHADER: + shaderSlot = &mFragShader; + break; + default: + mContext->ErrorInvalidOperation("attachShader: Bad type for shader."); + return; + } + + if (*shaderSlot) { + if (shader == *shaderSlot) { + mContext->ErrorInvalidOperation("attachShader: `shader` is already attached."); + } else { + mContext->ErrorInvalidOperation("attachShader: Only one of each type of" + " shader may be attached to a program."); + } + return; + } + + *shaderSlot = shader; + + mContext->MakeContextCurrent(); + mContext->gl->fAttachShader(mGLName, shader->mGLName); +} + +void +WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name) +{ + if (!ValidateGLSLVariableName(name, mContext, "bindAttribLocation")) + return; + + if (loc >= mContext->MaxVertexAttribs()) { + mContext->ErrorInvalidValue("bindAttribLocation: `location` must be less than" + " MAX_VERTEX_ATTRIBS."); + return; + } + + if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) { + mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the location of a" + " name that starts with 'gl_'."); + return; + } + + NS_LossyConvertUTF16toASCII asciiName(name); + + auto res = mBoundAttribLocs.insert(std::pair<nsCString, GLuint>(asciiName, loc)); + + const bool wasInserted = res.second; + if (!wasInserted) { + auto itr = res.first; + itr->second = loc; + } +} + +void +WebGLProgram::DetachShader(WebGLShader* shader) +{ + MOZ_ASSERT(shader); + + WebGLRefPtr<WebGLShader>* shaderSlot; + switch (shader->mType) { + case LOCAL_GL_VERTEX_SHADER: + shaderSlot = &mVertShader; + break; + case LOCAL_GL_FRAGMENT_SHADER: + shaderSlot = &mFragShader; + break; + default: + mContext->ErrorInvalidOperation("attachShader: Bad type for shader."); + return; + } + + if (*shaderSlot != shader) { + mContext->ErrorInvalidOperation("detachShader: `shader` is not attached."); + return; + } + + *shaderSlot = nullptr; + mContext->MakeContextCurrent(); - mContext->gl->fDeleteProgram(mGLName); - LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms); + mContext->gl->fDetachShader(mGLName, shader->mGLName); +} + +already_AddRefed<WebGLActiveInfo> +WebGLProgram::GetActiveAttrib(GLuint index) const +{ + if (!mMostRecentLinkInfo) { + nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(); + return ret.forget(); + } + + const auto& activeList = mMostRecentLinkInfo->activeAttribs; + + if (index >= activeList.size()) { + mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).", + index, "ACTIVE_ATTRIBS", activeList.size()); + return nullptr; + } + + nsRefPtr<WebGLActiveInfo> ret = activeList[index]; + return ret.forget(); +} + +already_AddRefed<WebGLActiveInfo> +WebGLProgram::GetActiveUniform(GLuint index) const +{ + if (!mMostRecentLinkInfo) { + nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(); + return ret.forget(); + } + + const auto& activeList = mMostRecentLinkInfo->activeUniforms; + + if (index >= activeList.size()) { + mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).", + index, "ACTIVE_UNIFORMS", activeList.size()); + return nullptr; + } + + nsRefPtr<WebGLActiveInfo> ret = activeList[index]; + return ret.forget(); +} + +void +WebGLProgram::GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const +{ + out->TruncateLength(0); + + if (mVertShader) + out->AppendElement(mVertShader); + + if (mFragShader) + out->AppendElement(mFragShader); +} + +GLint +WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const +{ + if (!ValidateGLSLVariableName(userName_wide, mContext, "getAttribLocation")) + return -1; + + if (!IsLinked()) { + mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked."); + return -1; + } + + const NS_LossyConvertUTF16toASCII userName(userName_wide); + + const WebGLActiveInfo* info; + if (!LinkInfo()->FindAttrib(userName, &info)) + return -1; + + const nsCString& mappedName = info->mBaseMappedName; + + gl::GLContext* gl = mContext->GL(); + gl->MakeCurrent(); + + return gl->fGetAttribLocation(mGLName, mappedName.BeginReading()); +} + +void +WebGLProgram::GetProgramInfoLog(nsAString* const out) const +{ + CopyASCIItoUTF16(mLinkLog, *out); +} + +static GLint +GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname) +{ + GLint ret = 0; + gl->fGetProgramiv(program, pname, &ret); + return ret; +} + +JS::Value +WebGLProgram::GetProgramParameter(GLenum pname) const +{ + gl::GLContext* gl = mContext->gl; + gl->MakeCurrent(); + + if (mContext->IsWebGL2()) { + switch (pname) { + case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS: + return JS::Int32Value(GetProgramiv(gl, mGLName, pname)); + } + } + + + switch (pname) { + case LOCAL_GL_ATTACHED_SHADERS: + case LOCAL_GL_ACTIVE_UNIFORMS: + case LOCAL_GL_ACTIVE_ATTRIBUTES: + return JS::Int32Value(GetProgramiv(gl, mGLName, pname)); + + case LOCAL_GL_DELETE_STATUS: + return JS::BooleanValue(IsDeleteRequested()); + + case LOCAL_GL_LINK_STATUS: + return JS::BooleanValue(IsLinked()); + + case LOCAL_GL_VALIDATE_STATUS: +#ifdef XP_MACOSX + // See comment in ValidateProgram. + if (gl->WorkAroundDriverBugs()) + return JS::BooleanValue(true); +#endif + return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname))); + + default: + mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`", + pname); + return JS::NullValue(); + } +} + +already_AddRefed<WebGLUniformLocation> +WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const +{ + if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation")) + return nullptr; + + if (!IsLinked()) { + mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked."); + return nullptr; + } + + const NS_LossyConvertUTF16toASCII userName(userName_wide); + + nsDependentCString baseUserName; + bool isArray; + size_t arrayIndex; + if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) + return nullptr; + + const WebGLActiveInfo* activeInfo; + if (!LinkInfo()->FindUniform(baseUserName, &activeInfo)) + return nullptr; + + const nsCString& baseMappedName = activeInfo->mBaseMappedName; + + nsAutoCString mappedName(baseMappedName); + if (isArray) { + mappedName.AppendLiteral("["); + mappedName.AppendInt(uint32_t(arrayIndex)); + mappedName.AppendLiteral("]"); + } + + gl::GLContext* gl = mContext->GL(); + gl->MakeCurrent(); + + GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading()); + if (loc == -1) + return nullptr; + + nsRefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(), + loc, activeInfo); + return locObj.forget(); } bool -WebGLProgram::AttachShader(WebGLShader* shader) -{ - if (ContainsShader(shader)) - return false; - - mAttachedShaders.AppendElement(shader); - - mContext->MakeContextCurrent(); - mContext->gl->fAttachShader(GLName(), shader->GLName()); - - return true; -} - -bool -WebGLProgram::DetachShader(WebGLShader* shader) +WebGLProgram::LinkProgram() { - if (!mAttachedShaders.RemoveElement(shader)) + mContext->InvalidateBufferFetching(); // we do it early in this function + // as some of the validation below changes program state + + mLinkLog.Truncate(); + mMostRecentLinkInfo = nullptr; + + if (!mVertShader || !mVertShader->IsCompiled()) { + mLinkLog.AssignLiteral("Must have a compiled vertex shader attached."); + mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading()); return false; + } - mContext->MakeContextCurrent(); - mContext->gl->fDetachShader(GLName(), shader->GLName()); + if (!mFragShader || !mFragShader->IsCompiled()) { + mLinkLog.AssignLiteral("Must have an compiled fragment shader attached."); + mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading()); + return false; + } + + if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) { + mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading()); + return false; + } + + gl::GLContext* gl = mContext->gl; + gl->MakeCurrent(); - return true; -} + // Bug 777028: Mesa can't handle more than 16 samplers per program, + // counting each array entry. + size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() + + mFragShader->CalcNumSamplerUniforms(); + if (gl->WorkAroundDriverBugs() && + mContext->mIsMesa && + numSamplerUniforms_upperBound > 16) + { + mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on" + " Mesa drivers to avoid crashing."); + mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading()); + return false; + } -bool -WebGLProgram::HasAttachedShaderOfType(GLenum shaderType) -{ - for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { - if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) - return true; + // Bind the attrib locations. + // This can't be done trivially, because we have to deal with mapped attrib names. + for (auto itr = mBoundAttribLocs.begin(); itr != mBoundAttribLocs.end(); ++itr) { + const nsCString& name = itr->first; + GLuint index = itr->second; + + mVertShader->BindAttribLocation(mGLName, name, index); + } + + if (LinkAndUpdate()) + return true; + + // Failed link. + if (mContext->ShouldGenerateWarnings()) { + // report shader/program infoLogs as warnings. + // note that shader compilation errors can be deferred to linkProgram, + // which is why we can't do anything in compileShader. In practice we could + // report in compileShader the translation errors generated by ANGLE, + // but it seems saner to keep a single way of obtaining shader infologs. + if (!mLinkLog.IsEmpty()) { + mContext->GenerateWarning("linkProgram: Failed to link, leaving the following" + " log:\n%s\n", + mLinkLog.BeginReading()); + } } return false; } bool -WebGLProgram::HasBadShaderAttached() +WebGLProgram::UseProgram() const +{ + if (!mMostRecentLinkInfo) { + mContext->ErrorInvalidOperation("useProgram: Program has not been successfully" + " linked."); + return false; + } + + mContext->MakeContextCurrent(); + + mContext->InvalidateBufferFetching(); + + mContext->gl->fUseProgram(mGLName); + return true; +} + +void +WebGLProgram::ValidateProgram() const { - for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { - if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus()) - return true; + mContext->MakeContextCurrent(); + gl::GLContext* gl = mContext->gl; + +#ifdef XP_MACOSX + // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed + // with Mac OS 10.6.7. + if (gl->WorkAroundDriverBugs()) { + mContext->GenerateWarning("validateProgram: Implemented as a no-op on" + " Mac to work around crashes."); + return; } +#endif + + gl->fValidateProgram(mGLName); +} + + +//////////////////////////////////////////////////////////////////////////////// + +bool +WebGLProgram::LinkAndUpdate() +{ + mMostRecentLinkInfo = nullptr; + + gl::GLContext* gl = mContext->gl; + gl->fLinkProgram(mGLName); + + // Grab the program log. + GLuint logLenWithNull = 0; + gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull); + if (logLenWithNull > 1) { + mLinkLog.SetLength(logLenWithNull - 1); + gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting()); + } else { + mLinkLog.SetLength(0); + } + + GLint ok = 0; + gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok); + if (!ok) + return false; + + mMostRecentLinkInfo = QueryProgramInfo(this, gl); + + MOZ_ASSERT(mMostRecentLinkInfo); + if (!mMostRecentLinkInfo) + mLinkLog.AssignLiteral("Failed to gather program info."); + + return mMostRecentLinkInfo; +} + +bool +WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName, + nsDependentCString* const out_userName) const +{ + if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName)) + return true; return false; } -size_t -WebGLProgram::UpperBoundNumSamplerUniforms() +bool +WebGLProgram::FindUniformByMappedName(const nsACString& mappedName, + nsCString* const out_userName, + bool* const out_isArray) const { - size_t numSamplerUniforms = 0; - - for (size_t i = 0; i < mAttachedShaders.Length(); ++i) { - const WebGLShader* shader = mAttachedShaders[i]; - if (!shader) - continue; - - for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) { - WebGLUniformInfo u = shader->mUniformInfos[j]; - if (u.type == LOCAL_GL_SAMPLER_2D || - u.type == LOCAL_GL_SAMPLER_CUBE) - { - numSamplerUniforms += u.arraySize; - } - } - } - - return numSamplerUniforms; -} - -void -WebGLProgram::MapIdentifier(const nsACString& name, - nsCString* const out_mappedName) -{ - MOZ_ASSERT(mIdentifierMap); - - nsCString mutableName(name); - nsCString bracketPart; - bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); - if (hadBracketPart) - mutableName.AppendLiteral("[0]"); - - if (mIdentifierMap->Get(mutableName, out_mappedName)) { - if (hadBracketPart) { - nsCString mappedBracketPart; - bool mappedHadBracketPart = SplitLastSquareBracket(*out_mappedName, - mappedBracketPart); - if (mappedHadBracketPart) - out_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, out_mappedName)) - return; + if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray)) + return true; - /* 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) - */ - out_mappedName->Assign(name); -} - -void -WebGLProgram::ReverseMapIdentifier(const nsACString& name, - nsCString* const out_reverseMappedName) -{ - MOZ_ASSERT(mIdentifierReverseMap); - - nsCString mutableName(name); - nsCString bracketPart; - bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); - if (hadBracketPart) - mutableName.AppendLiteral("[0]"); - - if (mIdentifierReverseMap->Get(mutableName, out_reverseMappedName)) { - if (hadBracketPart) { - nsCString reverseMappedBracketPart; - bool reverseMappedHadBracketPart = SplitLastSquareBracket(*out_reverseMappedName, - reverseMappedBracketPart); - if (reverseMappedHadBracketPart) - out_reverseMappedName->Append(bracketPart); - } - return; - } + if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray)) + return true; - // 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, out_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) - */ - out_reverseMappedName->Assign(name); -} - -WebGLUniformInfo -WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name) -{ - MOZ_ASSERT(mUniformInfoMap); - - 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. - - return info; + return false; } -bool -WebGLProgram::UpdateInfo() -{ - mAttribMaxNameLength = 0; - for (size_t i = 0; i < mAttachedShaders.Length(); i++) { - mAttribMaxNameLength = std::max(mAttribMaxNameLength, - mAttachedShaders[i]->mAttribMaxNameLength); - } - - GLint attribCount; - mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount); - - if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) { - mContext->ErrorOutOfMemory("updateInfo: Out of memory to allocate %d" - " attribs.", mContext->mGLMaxVertexAttribs); - return false; - } - - for (size_t i = 0; i < mAttribsInUse.Length(); i++) - mAttribsInUse[i] = false; - - nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]); - - for (int i = 0; i < attribCount; ++i) { - GLint attrnamelen; - GLint attrsize; - GLenum attrtype; - mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength, - &attrnamelen, &attrsize, &attrtype, - nameBuf); - if (attrnamelen > 0) { - GLint loc = mContext->gl->fGetAttribLocation(mGLName, nameBuf); - MOZ_ASSERT(loc >= 0, "Major oops in managing the attributes of a" - " WebGL program."); - if (loc < mContext->mGLMaxVertexAttribs) { - mAttribsInUse[loc] = true; - } else { - mContext->GenerateWarning("Program exceeds MAX_VERTEX_ATTRIBS."); - return false; - } - } - } +//////////////////////////////////////////////////////////////////////////////// - // nsAutoPtr will delete old version first - mIdentifierMap = new CStringMap; - mIdentifierReverseMap = new CStringMap; - mUniformInfoMap = new CStringToUniformInfoMap; - for (size_t i = 0; i < mAttachedShaders.Length(); i++) { - // Loop through ATTRIBUTES - for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { - const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; - - // FORWARD MAPPING - mIdentifierMap->Put(attrib.original, attrib.mapped); - // REVERSE MAPPING - mIdentifierReverseMap->Put(attrib.mapped, attrib.original); - } - - // Loop through UNIFORMS - for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { - // Add the uniforms name mapping to mIdentifier[Reverse]Map - const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; - - // FOWARD MAPPING - mIdentifierMap->Put(uniform.original, uniform.mapped); - // REVERSE MAPPING - mIdentifierReverseMap->Put(uniform.mapped, uniform.original); - - // Add uniform info to mUniformInfoMap - const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j]; - mUniformInfoMap->Put(uniform.mapped, info); - } - } - - mActiveAttribMap.clear(); - - GLint numActiveAttrs = 0; - mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs); - - // Spec says the maximum attrib name length is 256 chars, so this is - // sufficient to hold any attrib name. - char attrName[257]; - - GLint dummySize; - GLenum dummyType; - for (GLint i = 0; i < numActiveAttrs; i++) { - mContext->gl->fGetActiveAttrib(mGLName, i, 257, nullptr, &dummySize, - &dummyType, attrName); - GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName); - MOZ_ASSERT(attrLoc >= 0); - mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName))); - } - - return true; +JSObject* +WebGLProgram::WrapObject(JSContext* js) +{ + return dom::WebGLProgramBinding::Wrap(js, this); } -/*static*/ uint64_t -WebGLProgram::IdentifierHashFunction(const char* ident, size_t size) -{ - uint64_t outhash[2]; - // NB: we use the x86 function everywhere, even though it's suboptimal perf - // on x64. They return different results; not sure if that's a requirement. - MurmurHash3_x86_128(ident, size, 0, &outhash[0]); - return outhash[0]; -} - -/*static*/ void -WebGLProgram::HashMapIdentifier(const nsACString& name, - nsCString* const out_hashedName) -{ - uint64_t hash = IdentifierHashFunction(name.BeginReading(), name.Length()); - out_hashedName->Truncate(); - // This MUST MATCH HASHED_NAME_PREFIX from - // angle/src/compiler/translator/HashNames.h . - out_hashedName->AppendPrintf("webgl_%llx", hash); -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mAttachedShaders) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release) } // namespace mozilla
--- a/dom/canvas/WebGLProgram.h +++ b/dom/canvas/WebGLProgram.h @@ -4,135 +4,147 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef WEBGL_PROGRAM_H_ #define WEBGL_PROGRAM_H_ #include <map> #include "mozilla/CheckedInt.h" #include "mozilla/LinkedList.h" +#include "nsString.h" #include "nsWrapperCache.h" +#include <set> +#include <vector> #include "WebGLObjectModel.h" #include "WebGLShader.h" -#include "WebGLUniformInfo.h" namespace mozilla { +class WebGLActiveInfo; +class WebGLProgram; +class WebGLUniformLocation; + +namespace webgl { + +struct LinkedProgramInfo MOZ_FINAL + : public RefCounted<LinkedProgramInfo> + , public SupportsWeakPtr<LinkedProgramInfo> +{ + MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo) + + WebGLProgram* const prog; + std::vector<nsRefPtr<WebGLActiveInfo>> activeAttribs; + std::vector<nsRefPtr<WebGLActiveInfo>> activeUniforms; + + // Needed for Get{Attrib,Uniform}Location. The keys for these are non-mapped + // user-facing `GLActiveInfo::name`s, without any final "[0]". + std::map<nsCString, const WebGLActiveInfo*> attribMap; + std::map<nsCString, const WebGLActiveInfo*> uniformMap; + + // Needed for draw call validation. + std::set<GLuint> activeAttribLocs; + + explicit LinkedProgramInfo(WebGLProgram* aProg); + + bool FindAttrib(const nsCString& baseUserName, + const WebGLActiveInfo** const out_activeInfo) const + { + auto itr = attribMap.find(baseUserName); + if (itr == attribMap.end()) + return false; + + *out_activeInfo = itr->second; + return true; + } + + bool FindUniform(const nsCString& baseUserName, + const WebGLActiveInfo** const out_activeInfo) const + { + auto itr = uniformMap.find(baseUserName); + if (itr == uniformMap.end()) + return false; + + *out_activeInfo = itr->second; + return true; + } + + bool HasActiveAttrib(GLuint loc) const { + auto itr = activeAttribLocs.find(loc); + return itr != activeAttribLocs.end(); + } +}; + +} // namespace webgl + class WebGLShader; -struct WebGLUniformInfo; typedef nsDataHashtable<nsCStringHashKey, nsCString> CStringMap; -typedef nsDataHashtable<nsCStringHashKey, - WebGLUniformInfo> CStringToUniformInfoMap; class WebGLProgram MOZ_FINAL : public nsWrapperCache , public WebGLRefCountedObject<WebGLProgram> , public LinkedListElement<WebGLProgram> , public WebGLContextBoundObject { public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram) + explicit WebGLProgram(WebGLContext* webgl); void Delete(); - void DetachShaders() { - mAttachedShaders.Clear(); - } - - GLuint GLName() { return mGLName; } - const nsTArray<WebGLRefPtr<WebGLShader> >& 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); - - // return true if the shader was found and removed - bool DetachShader(WebGLShader* shader); - - bool HasAttachedShaderOfType(GLenum shaderType); - - bool HasBothShaderTypesAttached() { - return HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) && - HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER); - } - - bool HasBadShaderAttached(); + // GL funcs + void AttachShader(WebGLShader* shader); + void BindAttribLocation(GLuint index, const nsAString& name); + void DetachShader(WebGLShader* shader); + already_AddRefed<WebGLActiveInfo> GetActiveAttrib(GLuint index) const; + already_AddRefed<WebGLActiveInfo> GetActiveUniform(GLuint index) const; + void GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const; + GLint GetAttribLocation(const nsAString& name) const; + void GetProgramInfoLog(nsAString* const out) const; + JS::Value GetProgramParameter(GLenum pname) const; + already_AddRefed<WebGLUniformLocation> GetUniformLocation(const nsAString& name) const; + bool LinkProgram(); + bool UseProgram() const; + void ValidateProgram() const; - size_t UpperBoundNumSamplerUniforms(); - - 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(uint32_t i) const { return mAttribsInUse[i]; } + //////////////// - // Maps identifier |name| to the mapped identifier |*mappedName| - // Both are ASCII strings. - void MapIdentifier(const nsACString& name, nsCString* out_mappedName); + bool FindAttribUserNameByMappedName(const nsACString& mappedName, + nsDependentCString* const out_userName) const; + bool FindUniformByMappedName(const nsACString& mappedName, + nsCString* const out_userName, + bool* const out_isArray) const; - // Un-maps mapped identifier |name| to the original identifier - // |*reverseMappedName|. - // Both are ASCII strings. - void ReverseMapIdentifier(const nsACString& name, - nsCString* out_reverseMappedName); + bool IsLinked() const { return mMostRecentLinkInfo; } - /* 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); + const webgl::LinkedProgramInfo* LinkInfo() const { + return mMostRecentLinkInfo.get(); + } WebGLContext* GetParentObject() const { return Context(); } - virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE; - - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram) - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram) + virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE; - // public post-link data - std::map<GLint, nsCString> mActiveAttribMap; - - static uint64_t IdentifierHashFunction(const char* ident, size_t size); - static void HashMapIdentifier(const nsACString& name, - nsCString* const out_hashedName); - -protected: +private: ~WebGLProgram() { DeleteOnce(); } - GLuint mGLName; - bool mLinkStatus; - // attached shaders of the program object - nsTArray<WebGLRefPtr<WebGLShader> > mAttachedShaders; - CheckedUint32 mGeneration; + bool LinkAndUpdate(); + +public: + const GLuint mGLName; - // post-link data - FallibleTArray<bool> mAttribsInUse; - nsAutoPtr<CStringMap> mIdentifierMap, mIdentifierReverseMap; - nsAutoPtr<CStringToUniformInfoMap> mUniformInfoMap; - int mAttribMaxNameLength; +private: + WebGLRefPtr<WebGLShader> mVertShader; + WebGLRefPtr<WebGLShader> mFragShader; + std::map<nsCString, GLuint> mBoundAttribLocs; + nsCString mLinkLog; + RefPtr<const webgl::LinkedProgramInfo> mMostRecentLinkInfo; }; } // namespace mozilla #endif // WEBGL_PROGRAM_H_
--- a/dom/canvas/WebGLShader.cpp +++ b/dom/canvas/WebGLShader.cpp @@ -4,61 +4,384 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGLShader.h" #include "angle/ShaderLang.h" #include "GLContext.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "mozilla/MemoryReporting.h" +#include "nsPrintfCString.h" +#include "nsString.h" #include "WebGLContext.h" #include "WebGLObjectModel.h" +#include "WebGLShaderValidator.h" +#include "WebGLValidateStrings.h" namespace mozilla { -JSObject* -WebGLShader::WrapObject(JSContext* cx) { - return dom::WebGLShaderBinding::Wrap(cx, this); +// On success, writes to out_validator and out_translatedSource. +// On failure, writes to out_translationLog. +static bool +Translate(const nsACString& source, webgl::ShaderValidator* validator, + nsACString* const out_translationLog, nsACString* const out_translatedSource) +{ + if (!validator->ValidateAndTranslate(source.BeginReading())) { + validator->GetInfoLog(out_translationLog); + return false; + } + + // Success + validator->GetOutput(out_translatedSource); + return true; +} + +template<size_t N> +static bool +SubstringStartsWith(const std::string& testStr, size_t offset, const char (& refStr)[N]) +{ + for (size_t i = 0; i < N-1; i++) { + if (testStr[offset + i] != refStr[i]) + return false; + } + return true; +} + +/* On success, writes to out_translatedSource. + * On failure, writes to out_translationLog. + * + * Requirements: + * #version is either omitted, `#version 100`, or `version 300 es`. + */ +static bool +TranslateWithoutValidation(const nsACString& sourceNS, bool isWebGL2, + nsACString* const out_translationLog, + nsACString* const out_translatedSource) +{ + std::string source = sourceNS.BeginReading(); + + size_t versionStrStart = source.find("#version"); + size_t versionStrLen; + uint32_t glesslVersion; + + if (versionStrStart != std::string::npos) { + static const char versionStr100[] = "#version 100\n"; + static const char versionStr300es[] = "#version 300 es\n"; + + if (isWebGL2 && SubstringStartsWith(source, versionStrStart, versionStr300es)) { + glesslVersion = 300; + versionStrLen = strlen(versionStr300es); + + } else if (SubstringStartsWith(source, versionStrStart, versionStr100)) { + glesslVersion = 100; + versionStrLen = strlen(versionStr100); + + } else { + nsPrintfCString error("#version, if declared, must be %s.", + isWebGL2 ? "`100` or `300 es`" + : "`100`"); + *out_translationLog = error; + return false; + } + } else { + versionStrStart = 0; + versionStrLen = 0; + glesslVersion = 100; + } + + std::string reversionedSource = source; + reversionedSource.erase(versionStrStart, versionStrLen); + + switch (glesslVersion) { + case 100: + break; + case 300: + reversionedSource.insert(versionStrStart, "#version 330\n"); + break; + default: + MOZ_CRASH("Bad `glesslVersion`."); + } + + out_translatedSource->Assign(reversionedSource.c_str(), + reversionedSource.length()); + return true; +} + +static void +GetCompilationStatusAndLog(gl::GLContext* gl, GLuint shader, bool* const out_success, + nsACString* const out_log) +{ + GLint compileStatus = LOCAL_GL_FALSE; + gl->fGetShaderiv(shader, LOCAL_GL_COMPILE_STATUS, &compileStatus); + + // It's simpler if we always get the log. + GLint lenWithNull = 0; + gl->fGetShaderiv(shader, LOCAL_GL_INFO_LOG_LENGTH, &lenWithNull); + + if (lenWithNull > 1) { + // SetLength takes the length without the null. + out_log->SetLength(lenWithNull - 1); + gl->fGetShaderInfoLog(shader, lenWithNull, nullptr, out_log->BeginWriting()); + } else { + out_log->SetLength(0); + } + + *out_success = (compileStatus == LOCAL_GL_TRUE); +} + +//////////////////////////////////////////////////////////////////////////////// + +static GLuint +CreateShader(gl::GLContext* gl, GLenum type) +{ + gl->MakeCurrent(); + return gl->fCreateShader(type); } WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type) : WebGLContextBoundObject(webgl) + , mGLName(CreateShader(webgl->GL(), type)) , mType(type) - , mNeedsTranslation(true) - , mAttribMaxNameLength(0) - , mCompileStatus(false) +{ + mContext->mShaders.insertBack(this); +} + +WebGLShader::~WebGLShader() +{ + DeleteOnce(); +} + +void +WebGLShader::ShaderSource(const nsAString& source) +{ + StripComments stripComments(source); + const nsAString& cleanSource = Substring(stripComments.result().Elements(), + stripComments.length()); + if (!ValidateGLSLString(cleanSource, mContext, "shaderSource")) + return; + + // We checked that the source stripped of comments is in the + // 7-bit ASCII range, so we can skip the NS_IsAscii() check. + NS_LossyConvertUTF16toASCII sourceCString(cleanSource); + + if (mContext->gl->WorkAroundDriverBugs()) { + const size_t maxSourceLength = 0x3ffff; + if (sourceCString.Length() > maxSourceLength) { + mContext->ErrorInvalidValue("shaderSource: Source has more than %d" + " characters. (Driver workaround)", + maxSourceLength); + return; + } + } + + // HACK - dump shader source + { +/* + printf_stderr("//-*- glsl -*-\n"); + // Wow - Roll Your Own For Each Lines because printf_stderr has a hard-coded internal size, so long strings are truncated. + const nsString& src = shader->Source(); + int32_t start = 0; + int32_t end = src.Find("\n", false, start, -1); + while (end > -1) { + printf_stderr("%s\n", NS_ConvertUTF16toUTF8(nsDependentSubstring(src, start, end - start)).get()); + start = end + 1; + end = src.Find("\n", false, start, -1); + } + printf_stderr("//\n"); +*/ + } + // HACK + + mSource = source; + mCleanSource = sourceCString; +} + +void +WebGLShader::CompileShader() +{ + mValidator = nullptr; + mTranslationSuccessful = false; + mCompilationSuccessful = false; + + gl::GLContext* gl = mContext->gl; + + mValidator.reset(mContext->CreateShaderValidator(mType)); + + bool success; + if (mValidator) { + success = Translate(mCleanSource, mValidator.get(), &mValidationLog, + &mTranslatedSource); + } else { + success = TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(), + &mValidationLog, &mTranslatedSource); + } + + if (!success) + return; + + mTranslationSuccessful = true; + + gl->MakeCurrent(); + + const char* const parts[] = { + mTranslatedSource.BeginReading() + }; + gl->fShaderSource(mGLName, ArrayLength(parts), parts, nullptr); + + gl->fCompileShader(mGLName); + + GetCompilationStatusAndLog(gl, mGLName, &mCompilationSuccessful, &mCompilationLog); +} + +void +WebGLShader::GetShaderInfoLog(nsAString* out) const +{ + const nsCString& log = !mTranslationSuccessful ? mValidationLog + : mCompilationLog; + CopyASCIItoUTF16(log, *out); +} + +JS::Value +WebGLShader::GetShaderParameter(GLenum pname) const { - mContext->MakeContextCurrent(); - mGLName = mContext->gl->fCreateShader(mType); - mContext->mShaders.insertBack(this); + switch (pname) { + case LOCAL_GL_SHADER_TYPE: + return JS::NumberValue(mType); + + case LOCAL_GL_DELETE_STATUS: + return JS::BooleanValue(IsDeleteRequested()); + + case LOCAL_GL_COMPILE_STATUS: + return JS::BooleanValue(mCompilationSuccessful); + + default: + mContext->ErrorInvalidEnumInfo("getShaderParameter: `pname`", pname); + return JS::NullValue(); + } +} + +void +WebGLShader::GetShaderSource(nsAString* out) const +{ + out->SetIsVoid(false); + *out = mSource; +} + +void +WebGLShader::GetShaderTranslatedSource(nsAString* out) const +{ + if (!mCompilationSuccessful) { + mContext->ErrorInvalidOperation("getShaderTranslatedSource: Shader has" + " not been successfully compiled."); + return; + } + + out->SetIsVoid(false); + CopyASCIItoUTF16(mTranslatedSource, *out); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool +WebGLShader::CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const +{ + if (!mValidator) + return true; + + return mValidator->CanLinkTo(prev->mValidator.get(), out_log); +} + +size_t +WebGLShader::CalcNumSamplerUniforms() const +{ + if (mValidator) + return mValidator->CalcNumSamplerUniforms(); + + // TODO + return 0; +} + +void +WebGLShader::BindAttribLocation(GLuint prog, const nsCString& userName, + GLuint index) const +{ + std::string userNameStr(userName.BeginReading()); + + const std::string* mappedNameStr = &userNameStr; + if (mValidator) + mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr); + + mContext->gl->fBindAttribLocation(prog, index, mappedNameStr->c_str()); +} + +bool +WebGLShader::FindAttribUserNameByMappedName(const nsACString& mappedName, + nsDependentCString* const out_userName) const +{ + if (!mValidator) + return false; + + const std::string mappedNameStr(mappedName.BeginReading()); + const std::string* userNameStr; + if (!mValidator->FindAttribUserNameByMappedName(mappedNameStr, &userNameStr)) + return false; + + out_userName->Rebind(userNameStr->c_str()); + return true; +} + +bool +WebGLShader::FindUniformByMappedName(const nsACString& mappedName, + nsCString* const out_userName, + bool* const out_isArray) const +{ + if (!mValidator) + return false; + + const std::string mappedNameStr(mappedName.BeginReading(), mappedName.Length()); + std::string userNameStr; + if (!mValidator->FindUniformByMappedName(mappedNameStr, &userNameStr, out_isArray)) + return false; + + *out_userName = userNameStr.c_str(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Boilerplate + +JSObject* +WebGLShader::WrapObject(JSContext* js) +{ + return dom::WebGLShaderBinding::Wrap(js, this); +} + +size_t +WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const +{ + size_t validatorSize = mValidator ? mallocSizeOf(mValidator.get()) + : 0; + return mallocSizeOf(this) + + mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) + + mCleanSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) + + validatorSize + + mValidationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf) + + mTranslatedSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) + + mCompilationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf); } void WebGLShader::Delete() { - mSource.Truncate(); - mTranslationLog.Truncate(); - mContext->MakeContextCurrent(); - mContext->gl->fDeleteShader(mGLName); - LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders); -} + gl::GLContext* gl = mContext->GL(); -size_t -WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const -{ - return mallocSizeOf(this) + - mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) + - mTranslationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf); -} + gl->MakeCurrent(); + gl->fDeleteShader(mGLName); -void -WebGLShader::SetTranslationSuccess() -{ - mTranslationLog.SetIsVoid(true); - mNeedsTranslation = false; + LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders); } NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShader) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLShader, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLShader, Release) } // namespace mozilla
--- a/dom/canvas/WebGLShader.h +++ b/dom/canvas/WebGLShader.h @@ -1,106 +1,88 @@ /* -*- 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 WEBGL_SHADER_H_ #define WEBGL_SHADER_H_ +#include "GLDefs.h" #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" #include "nsWrapperCache.h" #include "WebGLObjectModel.h" -#include "WebGLUniformInfo.h" namespace mozilla { -struct WebGLMappedIdentifier -{ - // ASCII strings - nsCString original; - nsCString mapped; - - WebGLMappedIdentifier(const nsACString& o, const nsACString& m) - : original(o) - , mapped(m) - {} -}; +namespace webgl { +class ShaderValidator; +} // namespace webgl class WebGLShader MOZ_FINAL : public nsWrapperCache , public WebGLRefCountedObject<WebGLShader> , public LinkedListElement<WebGLShader> , public WebGLContextBoundObject { friend class WebGLContext; friend class WebGLProgram; public: WebGLShader(WebGLContext* webgl, GLenum type); - size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; +protected: + ~WebGLShader(); - GLuint GLName() { return mGLName; } - sh::GLenum ShaderType() { return mType; } +public: + // GL funcs + void CompileShader(); + JS::Value GetShaderParameter(GLenum pname) const; + void GetShaderInfoLog(nsAString* out) const; + void GetShaderSource(nsAString* out) const; + void GetShaderTranslatedSource(nsAString* out) const; + void ShaderSource(const nsAString& source); - void SetSource(const nsAString& src) { - // TODO: Do some quick gzip here maybe? Getting this will be very rare, - // and we have to keep it forever. - mSource.Assign(src); - } + // Util funcs + bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const; + size_t CalcNumSamplerUniforms() const; + void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const; + bool FindAttribUserNameByMappedName(const nsACString& mappedName, + nsDependentCString* const out_userName) const; + bool FindUniformByMappedName(const nsACString& mappedName, + nsCString* const out_userName, + bool* const out_isArray) const; - const nsString& Source() const { return mSource; } - - void SetNeedsTranslation() { mNeedsTranslation = true; } - bool NeedsTranslation() const { return mNeedsTranslation; } - - void SetCompileStatus (bool status) { - mCompileStatus = status; + bool IsCompiled() const { + return mTranslationSuccessful && mCompilationSuccessful; } + // Other funcs + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; void Delete(); - bool CompileStatus() const { - return mCompileStatus; - } - - void SetTranslationSuccess(); - - void SetTranslationFailure(const nsCString& msg) { - mTranslationLog.Assign(msg); - } + WebGLContext* GetParentObject() const { return Context(); } - const nsCString& TranslationLog() const { return mTranslationLog; } - - const nsString& TranslatedSource() const { return mTranslatedSource; } - - WebGLContext* GetParentObject() const { - return Context(); - } - - virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE; + virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE; NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLShader) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLShader) +public: + const GLuint mGLName; + const GLenum mType; protected: - ~WebGLShader() { - DeleteOnce(); - } - - GLuint mGLName; - GLenum mType; nsString mSource; - nsString mTranslatedSource; - nsCString mTranslationLog; // The translation log should contain only ASCII characters - bool mNeedsTranslation; - nsTArray<WebGLMappedIdentifier> mAttributes; - nsTArray<WebGLMappedIdentifier> mUniforms; - nsTArray<WebGLUniformInfo> mUniformInfos; - int mAttribMaxNameLength; - bool mCompileStatus; + nsCString mCleanSource; + + UniquePtr<webgl::ShaderValidator> mValidator; + nsCString mValidationLog; + bool mTranslationSuccessful; + nsCString mTranslatedSource; + + bool mCompilationSuccessful; + nsCString mCompilationLog; }; } // namespace mozilla #endif // WEBGL_SHADER_H_
new file mode 100644 --- /dev/null +++ b/dom/canvas/WebGLShaderValidator.cpp @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#include "WebGLShaderValidator.h" + +#include "angle/ShaderLang.h" +#include "GLContext.h" +#include "MurmurHash3.h" +#include "nsPrintfCString.h" +#include <string> +#include <vector> +#include "WebGLContext.h" + +namespace mozilla { +namespace webgl { + +uint64_t +IdentifierHashFunc(const char* name, size_t len) +{ + // NB: we use the x86 function everywhere, even though it's suboptimal perf + // on x64. They return different results; not sure if that's a requirement. + uint64_t hash[2]; + MurmurHash3_x86_128(name, len, 0, hash); + return hash[0]; +} + +static int +ChooseValidatorCompileOptions(const ShBuiltInResources& resources, + const mozilla::gl::GLContext* gl) +{ + int options = SH_VARIABLES | + SH_ENFORCE_PACKING_RESTRICTIONS | + SH_INIT_VARYINGS_WITHOUT_STATIC_USE | + SH_OBJECT_CODE | + SH_LIMIT_CALL_STACK_DEPTH; + + if (resources.MaxExpressionComplexity > 0) { + options |= SH_LIMIT_EXPRESSION_COMPLEXITY; + } + +#ifndef XP_MACOSX + // We want to do this everywhere, but to do this on Mac, we need + // to do it only on Mac OSX > 10.6 as this causes the shader + // compiler in 10.6 to crash + options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS; +#endif + +#ifdef XP_MACOSX + if (gl->WorkAroundDriverBugs()) { + // Work around https://bugs.webkit.org/show_bug.cgi?id=124684, + // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb + options |= SH_UNFOLD_SHORT_CIRCUIT; + + // Work around bug 665578 and bug 769810 + if (gl->Vendor() == gl::GLVendor::ATI) { + options |= SH_EMULATE_BUILT_IN_FUNCTIONS; + } + + // Work around bug 735560 + if (gl->Vendor() == gl::GLVendor::Intel) { + options |= SH_EMULATE_BUILT_IN_FUNCTIONS; + } + + // Work around bug 636926 + if (gl->Vendor() == gl::GLVendor::NVIDIA) { + options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX; + } + } +#endif + + return options; +} + +} // namespace webgl + +//////////////////////////////////////// + +webgl::ShaderValidator* +WebGLContext::CreateShaderValidator(GLenum shaderType) const +{ + if (mBypassShaderValidation) + return nullptr; + + ShShaderSpec spec = SH_WEBGL_SPEC; + ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT + : SH_GLSL_OUTPUT; + + ShBuiltInResources resources; + memset(&resources, 0, sizeof(resources)); + ShInitBuiltInResources(&resources); + + resources.HashFunction = webgl::IdentifierHashFunc; + + resources.MaxVertexAttribs = mGLMaxVertexAttribs; + resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors; + resources.MaxVaryingVectors = mGLMaxVaryingVectors; + resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits; + resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits; + resources.MaxTextureImageUnits = mGLMaxTextureImageUnits; + resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; + resources.MaxDrawBuffers = mGLMaxDrawBuffers; + + if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) + resources.EXT_frag_depth = 1; + + if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) + resources.OES_standard_derivatives = 1; + + if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) + resources.EXT_draw_buffers = 1; + + if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) + resources.EXT_shader_texture_lod = 1; + + // Tell ANGLE to allow highp in frag shaders. (unless disabled) + // If underlying GLES doesn't have highp in frag shaders, it should complain anyways. + resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1; + + if (gl->WorkAroundDriverBugs()) { +#ifdef XP_MACOSX + if (gl->Vendor() == gl::GLVendor::NVIDIA) { + // Work around bug 890432 + resources.MaxExpressionComplexity = 1000; + } +#endif + } + + int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl); + + return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources, + compileOptions); +} + +//////////////////////////////////////// + +namespace webgl { + +/*static*/ ShaderValidator* +ShaderValidator::Create(GLenum shaderType, ShShaderSpec spec, + ShShaderOutput outputLanguage, + const ShBuiltInResources& resources, int compileOptions) +{ + ShHandle handle = ShConstructCompiler(shaderType, spec, outputLanguage, &resources); + if (!handle) + return nullptr; + + return new ShaderValidator(handle, compileOptions); +} + +ShaderValidator::~ShaderValidator() +{ + ShDestruct(mHandle); +} + +bool +ShaderValidator::ValidateAndTranslate(const char* source) +{ + MOZ_ASSERT(!mHasRun); + mHasRun = true; + + const char* const parts[] = { + source + }; + return ShCompile(mHandle, parts, ArrayLength(parts), mCompileOptions); +} + +void +ShaderValidator::GetInfo(ShShaderInfo pname, size_t* params) const +{ + MOZ_ASSERT(mHasRun); + + ShGetInfo(mHandle, pname, params); +} + +void +ShaderValidator::GetInfoLog(nsACString* out) const +{ + MOZ_ASSERT(mHasRun); + + size_t lenWithNull = 0; + GetInfo(SH_INFO_LOG_LENGTH, &lenWithNull); + MOZ_ASSERT(lenWithNull >= 1); + + // SetLength allocates len+1, for the null-term. + out->SetLength(lenWithNull - 1); + + ShGetInfoLog(mHandle, out->BeginWriting()); +} + +void +ShaderValidator::GetOutput(nsACString* out) const +{ + MOZ_ASSERT(mHasRun); + + size_t lenWithNull = 0; + GetInfo(SH_OBJECT_CODE_LENGTH, &lenWithNull); + MOZ_ASSERT(lenWithNull >= 1); + + // SetLength allocates len+1, for the null-term. + out->SetLength(lenWithNull - 1); + + ShGetObjectCode(mHandle, out->BeginWriting()); +} + +template<size_t N> +static bool +StartsWith(const std::string& haystack, const char (&needle)[N]) +{ + return haystack.compare(0, N - 1, needle) == 0; +} + +bool +ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const +{ + { + const std::vector<sh::Uniform>& vertList = *ShGetUniforms(prev->mHandle); + const std::vector<sh::Uniform>& fragList = *ShGetUniforms(mHandle); + + for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) { + for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) { + if (itrVert->name != itrFrag->name) + continue; + + if (!itrVert->isSameUniformAtLinkTime(*itrFrag)) { + nsPrintfCString error("Uniform `%s`is not linkable between" + " attached shaders.", + itrFrag->name.c_str()); + *out_log = error; + return false; + } + + break; + } + } + } + { + const std::vector<sh::Varying>& vertList = *ShGetVaryings(prev->mHandle); + const std::vector<sh::Varying>& fragList = *ShGetVaryings(mHandle); + + for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) { + static const char prefix[] = "gl_"; + if (StartsWith(itrFrag->name, prefix)) + continue; + + bool definedInVertShader = false; + + for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) { + if (itrVert->name != itrFrag->name) + continue; + + if (!itrVert->isSameVaryingAtLinkTime(*itrFrag)) { + nsPrintfCString error("Varying `%s`is not linkable between" + " attached shaders.", + itrFrag->name.c_str()); + *out_log = error; + return false; + } + + definedInVertShader = true; + break; + } + + if (!definedInVertShader && itrFrag->staticUse) { + nsPrintfCString error("Varying `%s` has static-use in the frag" + " shader, but is undeclared in the vert" + " shader.", itrFrag->name.c_str()); + *out_log = error; + return false; + } + } + } + + return true; +} + +size_t +ShaderValidator::CalcNumSamplerUniforms() const +{ + size_t accum = 0; + + const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle); + + for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) { + GLenum type = itr->type; + if (type == LOCAL_GL_SAMPLER_2D || + type == LOCAL_GL_SAMPLER_CUBE) + { + accum += itr->arraySize; + } + } + + return accum; +} + +// Attribs cannot be structs or arrays, and neither can vertex inputs in ES3. +// Therefore, attrib names are always simple. +bool +ShaderValidator::FindAttribUserNameByMappedName(const std::string& mappedName, + const std::string** const out_userName) const +{ + const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle); + for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) { + if (itr->mappedName == mappedName) { + *out_userName = &(itr->name); + return true; + } + } + + return false; +} + +bool +ShaderValidator::FindAttribMappedNameByUserName(const std::string& userName, + const std::string** const out_mappedName) const +{ + const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle); + for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) { + if (itr->name == userName) { + *out_mappedName = &(itr->mappedName); + return true; + } + } + + return false; +} + +// This must handle names like "foo.bar[0]". +bool +ShaderValidator::FindUniformByMappedName(const std::string& mappedName, + std::string* const out_userName, + bool* const out_isArray) const +{ + const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle); + for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) { + const sh::ShaderVariable* found; + if (!itr->findInfoByMappedName(mappedName, &found, out_userName)) + continue; + + *out_isArray = found->isArray(); + return true; + } + + return false; +} + +} // namespace webgl +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/canvas/WebGLShaderValidator.h @@ -0,0 +1,60 @@ +/* -*- 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 WEBGL_SHADER_VALIDATOR_H_ +#define WEBGL_SHADER_VALIDATOR_H_ + +#include "angle/ShaderLang.h" +#include "GLDefs.h" +#include "nsString.h" +#include <string> + +namespace mozilla { +namespace webgl { + +class ShaderValidator MOZ_FINAL +{ + const ShHandle mHandle; + const int mCompileOptions; + bool mHasRun; + +public: + static ShaderValidator* Create(GLenum shaderType, ShShaderSpec spec, + ShShaderOutput outputLanguage, + const ShBuiltInResources& resources, + int compileOptions); + +private: + ShaderValidator(ShHandle handle, int compileOptions) + : mHandle(handle) + , mCompileOptions(compileOptions) + , mHasRun(false) + { } + +public: + ~ShaderValidator(); + + bool ValidateAndTranslate(const char* source); + void GetInfo(ShShaderInfo pname, size_t* params) const; + void GetInfoLog(nsACString* out) const; + void GetOutput(nsACString* out) const; + bool CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const; + size_t CalcNumSamplerUniforms() const; + + bool FindAttribUserNameByMappedName(const std::string& mappedName, + const std::string** const out_userName) const; + + bool FindAttribMappedNameByUserName(const std::string& userName, + const std::string** const out_mappedName) const; + + bool FindUniformByMappedName(const std::string& mappedName, + std::string* const out_userName, + bool* const out_isArray) const; +}; + +} // namespace webgl +} // namespace mozilla + +#endif // WEBGL_SHADER_VALIDATOR_H_
deleted file mode 100644 --- a/dom/canvas/WebGLUniformInfo.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- 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 WEBGLUNIFORMINFO_H_ -#define WEBGLUNIFORMINFO_H_ - -#include "GLDefs.h" -#include "angle/ShaderLang.h" - -namespace mozilla { - -struct WebGLUniformInfo { - uint32_t arraySize; - bool isArray; - sh::GLenum type; - - explicit WebGLUniformInfo(uint32_t s = 0, bool a = false, sh::GLenum t = LOCAL_GL_NONE) - : arraySize(s), isArray(a), type(t) {} - - int ElementSize() const { - switch (type) { - case LOCAL_GL_FLOAT: - case LOCAL_GL_INT: - case LOCAL_GL_UNSIGNED_INT: - case LOCAL_GL_BOOL: - case LOCAL_GL_SAMPLER_2D: - case LOCAL_GL_SAMPLER_3D: - case LOCAL_GL_SAMPLER_CUBE: - case LOCAL_GL_SAMPLER_2D_SHADOW: - case LOCAL_GL_SAMPLER_2D_ARRAY: - case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: - case LOCAL_GL_SAMPLER_CUBE_SHADOW: - case LOCAL_GL_INT_SAMPLER_2D: - case LOCAL_GL_INT_SAMPLER_3D: - case LOCAL_GL_INT_SAMPLER_CUBE: - case LOCAL_GL_INT_SAMPLER_2D_ARRAY: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: - case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - return 1; - case LOCAL_GL_FLOAT_VEC2: - case LOCAL_GL_INT_VEC2: - case LOCAL_GL_UNSIGNED_INT_VEC2: - case LOCAL_GL_BOOL_VEC2: - return 2; - case LOCAL_GL_FLOAT_VEC3: - case LOCAL_GL_INT_VEC3: - case LOCAL_GL_UNSIGNED_INT_VEC3: - case LOCAL_GL_BOOL_VEC3: - return 3; - case LOCAL_GL_FLOAT_VEC4: - case LOCAL_GL_INT_VEC4: - case LOCAL_GL_UNSIGNED_INT_VEC4: - case LOCAL_GL_BOOL_VEC4: - case LOCAL_GL_FLOAT_MAT2: - return 4; - case LOCAL_GL_FLOAT_MAT2x3: - case LOCAL_GL_FLOAT_MAT3x2: - return 6; - case LOCAL_GL_FLOAT_MAT2x4: - case LOCAL_GL_FLOAT_MAT4x2: - return 8; - case LOCAL_GL_FLOAT_MAT3: - return 9; - case LOCAL_GL_FLOAT_MAT3x4: - case LOCAL_GL_FLOAT_MAT4x3: - return 12; - case LOCAL_GL_FLOAT_MAT4: - return 16; - default: - MOZ_ASSERT(false); // should never get here - return 0; - } - } -}; - -} // namespace mozilla - -#endif
--- a/dom/canvas/WebGLUniformLocation.cpp +++ b/dom/canvas/WebGLUniformLocation.cpp @@ -1,39 +1,278 @@ /* -*- Mode: C++; tab-width: 20; 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/. */ #include "WebGLUniformLocation.h" +#include "GLContext.h" +#include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" +#include "WebGLActiveInfo.h" #include "WebGLContext.h" #include "WebGLProgram.h" -#include "WebGLShader.h" namespace mozilla { -JSObject* -WebGLUniformLocation::WrapObject(JSContext* cx) +WebGLUniformLocation::WebGLUniformLocation(WebGLContext* webgl, + const webgl::LinkedProgramInfo* linkInfo, + GLuint loc, const WebGLActiveInfo* activeInfo) + : WebGLContextBoundObject(webgl) + , mLinkInfo(linkInfo) + , mLoc(loc) + , mActiveInfo(activeInfo) +{ } + +WebGLUniformLocation::~WebGLUniformLocation() +{ } + +bool +WebGLUniformLocation::ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl, + const char* funcName) const +{ + // Check the weak-pointer. + if (!mLinkInfo) { + webgl->ErrorInvalidOperation("%s: This uniform location is obsolete because its" + " program has been successfully relinked.", + funcName); + return false; + } + + if (mLinkInfo->prog != prog) { + webgl->ErrorInvalidOperation("%s: This uniform location corresponds to a" + " different program.", funcName); + return false; + } + + return true; +} + +static bool +IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType) { - return dom::WebGLUniformLocationBinding::Wrap(cx, this); + switch (uniformType) { + case LOCAL_GL_BOOL: + case LOCAL_GL_BOOL_VEC2: + case LOCAL_GL_BOOL_VEC3: + case LOCAL_GL_BOOL_VEC4: + return setterType == LOCAL_GL_INT || + setterType == LOCAL_GL_FLOAT; // GLfloat(0.0) sets a bool to false. + + case LOCAL_GL_INT: + case LOCAL_GL_INT_SAMPLER_2D: + case LOCAL_GL_INT_SAMPLER_2D_ARRAY: + case LOCAL_GL_INT_SAMPLER_3D: + case LOCAL_GL_INT_SAMPLER_CUBE: + case LOCAL_GL_INT_VEC2: + case LOCAL_GL_INT_VEC3: + case LOCAL_GL_INT_VEC4: + case LOCAL_GL_SAMPLER_2D: + case LOCAL_GL_SAMPLER_2D_ARRAY: + case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: + case LOCAL_GL_SAMPLER_2D_SHADOW: + case LOCAL_GL_SAMPLER_CUBE: + case LOCAL_GL_SAMPLER_CUBE_SHADOW: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: + case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: + return setterType == LOCAL_GL_INT; + + case LOCAL_GL_FLOAT: + case LOCAL_GL_FLOAT_MAT2: + case LOCAL_GL_FLOAT_MAT2x3: + case LOCAL_GL_FLOAT_MAT2x4: + case LOCAL_GL_FLOAT_MAT3: + case LOCAL_GL_FLOAT_MAT3x2: + case LOCAL_GL_FLOAT_MAT3x4: + case LOCAL_GL_FLOAT_MAT4: + case LOCAL_GL_FLOAT_MAT4x2: + case LOCAL_GL_FLOAT_MAT4x3: + case LOCAL_GL_FLOAT_VEC2: + case LOCAL_GL_FLOAT_VEC3: + case LOCAL_GL_FLOAT_VEC4: + return setterType == LOCAL_GL_FLOAT; + + default: + MOZ_CRASH("Bad `uniformType`."); + } +} + +bool +WebGLUniformLocation::ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType, + WebGLContext* webgl, const char* funcName) const +{ + MOZ_ASSERT(mLinkInfo); + + if (setterElemSize != mActiveInfo->mElemSize) { + webgl->ErrorInvalidOperation("%s: Bad uniform size: %i", funcName, + mActiveInfo->mElemSize); + return false; + } + + if (!IsUniformSetterTypeValid(setterType, mActiveInfo->mElemType)) { + webgl->ErrorInvalidOperation("%s: Bad uniform type: %i", funcName, + mActiveInfo->mElemSize); + return false; + } + + return true; } -WebGLUniformLocation::WebGLUniformLocation(WebGLContext* context, - WebGLProgram* program, - GLint location, - const WebGLUniformInfo& info) - : WebGLContextBoundObject(context) - , mProgram(program) - , mProgramGeneration(program->Generation()) - , mLocation(location) - , mInfo(info) +bool +WebGLUniformLocation::ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize, + WebGLContext* webgl, const char* funcName) const { - mElementSize = info.ElementSize(); + MOZ_ASSERT(mLinkInfo); + + if (setterArraySize == 0 || + setterArraySize % setterElemSize) + { + webgl->ErrorInvalidValue("%s: expected an array of length a multiple of" + " %d, got an array of length %d.", + funcName, setterElemSize, setterArraySize); + return false; + } + + /* GLES 2.0.25, Section 2.10, p38 + * When loading `N` elements starting at an arbitrary position `k` in a uniform + * declared as an array, elements `k` through `k + N - 1` in the array will be + * replaced with the new values. Values for any array element that exceeds the + * highest array element index used, as reported by `GetActiveUniform`, will be + * ignored by GL. + */ + if (!mActiveInfo->mIsArray && + setterArraySize != setterElemSize) + { + webgl->ErrorInvalidOperation("%s: expected an array of length exactly %d" + " (since this uniform is not an array" + " uniform), got an array of length %d.", + funcName, setterElemSize, setterArraySize); + return false; + } + + return true; +} + +bool +WebGLUniformLocation::ValidateSamplerSetter(GLint value, WebGLContext* webgl, + const char* funcName) const +{ + MOZ_ASSERT(mLinkInfo); + + if (mActiveInfo->mElemType != LOCAL_GL_SAMPLER_2D && + mActiveInfo->mElemType != LOCAL_GL_SAMPLER_CUBE) + { + return true; + } + + if (value >= 0 && value < (GLint)webgl->GLMaxTextureUnits()) + return true; + + webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a" + " valid texture unit.", + funcName, value); + return false; } -NS_IMPL_CYCLE_COLLECTION(WebGLUniformLocation, mProgram) +JS::Value +WebGLUniformLocation::GetUniform(JSContext* js, WebGLContext* webgl) const +{ + MOZ_ASSERT(mLinkInfo); + + uint8_t elemSize = mActiveInfo->mElemSize; + static const uint8_t kMaxElemSize = 16; + MOZ_ASSERT(elemSize <= kMaxElemSize); + + GLuint prog = mLinkInfo->prog->mGLName; + + gl::GLContext* gl = webgl->GL(); + gl->MakeCurrent(); + + switch (mActiveInfo->mElemType) { + case LOCAL_GL_INT: + case LOCAL_GL_INT_VEC2: + case LOCAL_GL_INT_VEC3: + case LOCAL_GL_INT_VEC4: + case LOCAL_GL_SAMPLER_2D: + case LOCAL_GL_SAMPLER_CUBE: + { + GLint buffer[kMaxElemSize] = {0}; + gl->fGetUniformiv(prog, mLoc, buffer); + + if (elemSize == 1) + return JS::Int32Value(buffer[0]); + + JSObject* obj = dom::Int32Array::Create(js, webgl, elemSize, buffer); + if (!obj) { + webgl->ErrorOutOfMemory("getUniform: out of memory"); + return JS::NullValue(); + } + return JS::ObjectOrNullValue(obj); + } + + case LOCAL_GL_BOOL: + case LOCAL_GL_BOOL_VEC2: + case LOCAL_GL_BOOL_VEC3: + case LOCAL_GL_BOOL_VEC4: + { + GLint buffer[kMaxElemSize] = {0}; + gl->fGetUniformiv(prog, mLoc, buffer); + + if (elemSize == 1) + return JS::BooleanValue(buffer[0]); + + bool boolBuffer[kMaxElemSize]; + for (uint8_t i = 0; i < kMaxElemSize; i++) + boolBuffer[i] = buffer[i]; + + JS::RootedValue val(js); + // Be careful: we don't want to convert all of |uv|! + if (!dom::ToJSValue(js, boolBuffer, elemSize, &val)) { + webgl->ErrorOutOfMemory("getUniform: out of memory"); + return JS::NullValue(); + } + return val; + } + + case LOCAL_GL_FLOAT: + case LOCAL_GL_FLOAT_VEC2: + case LOCAL_GL_FLOAT_VEC3: + case LOCAL_GL_FLOAT_VEC4: + case LOCAL_GL_FLOAT_MAT2: + case LOCAL_GL_FLOAT_MAT3: + case LOCAL_GL_FLOAT_MAT4: + { + GLfloat buffer[16] = {0.0f}; + gl->fGetUniformfv(prog, mLoc, buffer); + + if (elemSize == 1) + return JS::DoubleValue(buffer[0]); + + JSObject* obj = dom::Float32Array::Create(js, webgl, elemSize, buffer); + if (!obj) { + webgl->ErrorOutOfMemory("getUniform: out of memory"); + return JS::NullValue(); + } + return JS::ObjectOrNullValue(obj); + } + + default: + MOZ_CRASH("Invalid elemType."); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +JSObject* +WebGLUniformLocation::WrapObject(JSContext* js) +{ + return dom::WebGLUniformLocationBinding::Wrap(js, this); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocation) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLUniformLocation, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLUniformLocation, Release) } // namespace mozilla
--- a/dom/canvas/WebGLUniformLocation.h +++ b/dom/canvas/WebGLUniformLocation.h @@ -1,55 +1,63 @@ /* -*- 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 WEBGL_UNIFORM_LOCATION_H_ #define WEBGL_UNIFORM_LOCATION_H_ +#include "GLDefs.h" +#include "mozilla/WeakPtr.h" +#include "nsWrapperCache.h" #include "WebGLObjectModel.h" -#include "WebGLUniformInfo.h" + +struct JSContext; namespace mozilla { +class WebGLActiveInfo; +class WebGLContext; +class WebGLProgram; -class WebGLProgram; +namespace webgl { +struct LinkedProgramInfo; +} class WebGLUniformLocation MOZ_FINAL - : public WebGLContextBoundObject + : public nsWrapperCache + , public WebGLContextBoundObject { public: - WebGLUniformLocation(WebGLContext* context, WebGLProgram* program, - GLint location, const WebGLUniformInfo& info); + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLUniformLocation) + + const WeakPtr<const webgl::LinkedProgramInfo> mLinkInfo; + const GLuint mLoc; + const WebGLActiveInfo* const mActiveInfo; + + WebGLUniformLocation(WebGLContext* webgl, const webgl::LinkedProgramInfo* linkInfo, + GLuint loc, const WebGLActiveInfo* activeInfo); - // needed for certain helper functions like ValidateObject. - // WebGLUniformLocation's can't be 'Deleted' in the WebGL sense. + bool ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl, + const char* funcName) const; + bool ValidateSamplerSetter(GLint value, WebGLContext* webgl, + const char* funcName) const; + bool ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType, + WebGLContext* webgl, const char* funcName) const; + bool ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize, + WebGLContext* webgl, const char* funcName) const; + + JS::Value GetUniform(JSContext* js, WebGLContext* webgl) const; + + // 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; } - - JSObject* WrapObject(JSContext* cx); - - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation) - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WebGLUniformLocation) + virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE; protected: - ~WebGLUniformLocation() { } - - // 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<WebGLProgram> mProgram; - - uint32_t mProgramGeneration; - GLint mLocation; - WebGLUniformInfo mInfo; - int mElementSize; - friend class WebGLProgram; + ~WebGLUniformLocation(); }; } // namespace mozilla #endif // WEBGL_UNIFORM_LOCATION_H_
new file mode 100644 --- /dev/null +++ b/dom/canvas/WebGLValidateStrings.cpp @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#include "WebGLValidateStrings.h" + +#include "nsString.h" +#include "WebGLContext.h" + +namespace mozilla { +// The following code was taken from the WebKit WebGL implementation, +// which can be found here: +// http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121 +// Note that some modifications were done to adapt it to Mozilla. +/****** BEGIN CODE TAKEN FROM WEBKIT ******/ +bool IsValidGLSLCharacter(char16_t c) +{ + // Printing characters are valid except " $ ` @ \ ' DEL. + if (c >= 32 && c <= 126 && + c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'') + { + return true; + } + + // Horizontal tab, line feed, vertical tab, form feed, carriage return + // are also valid. + if (c >= 9 && c <= 13) { + return true; + } + + return false; +} + +void StripComments::process(char16_t c) +{ + if (isNewline(c)) { + // No matter what state we are in, pass through newlines + // so we preserve line numbers. + emit(c); + + if (m_parseState != InMultiLineComment) + m_parseState = BeginningOfLine; + + return; + } + + char16_t temp = 0; + switch (m_parseState) { + case BeginningOfLine: + // If it's an ASCII space. + if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) { + emit(c); + break; + } + + if (c == '#') { + m_parseState = InPreprocessorDirective; + emit(c); + break; + } + + // Transition to normal state and re-handle character. + m_parseState = MiddleOfLine; + process(c); + break; + + case MiddleOfLine: + if (c == '/' && peek(temp)) { + if (temp == '/') { + m_parseState = InSingleLineComment; + emit(' '); + advance(); + break; + } + + if (temp == '*') { + m_parseState = InMultiLineComment; + // Emit the comment start in case the user has + // an unclosed comment and we want to later + // signal an error. + emit('/'); + emit('*'); + advance(); + break; + } + } + + emit(c); + break; + + case InPreprocessorDirective: + // No matter what the character is, just pass it + // through. Do not parse comments in this state. This + // might not be the right thing to do long term, but it + // should handle the #error preprocessor directive. + emit(c); + break; + + case InSingleLineComment: + // The newline code at the top of this function takes care + // of resetting our state when we get out of the + // single-line comment. Swallow all other characters. + break; + + case InMultiLineComment: + if (c == '*' && peek(temp) && temp == '/') { + emit('*'); + emit('/'); + m_parseState = MiddleOfLine; + advance(); + break; + } + + // Swallow all other characters. Unclear whether we may + // want or need to just emit a space per character to try + // to preserve column numbers for debugging purposes. + break; + } +} + +/****** END CODE TAKEN FROM WEBKIT ******/ + +bool +ValidateGLSLString(const nsAString& string, WebGLContext* webgl, const char* funcName) +{ + for (size_t i = 0; i < string.Length(); ++i) { + if (!IsValidGLSLCharacter(string.CharAt(i))) { + webgl->ErrorInvalidValue("%s: String contains the illegal character '%d'", + funcName, string.CharAt(i)); + return false; + } + } + + return true; +} + +bool +ValidateGLSLVariableName(const nsAString& name, WebGLContext* webgl, const char* funcName) +{ + if (name.IsEmpty()) + return false; + + const uint32_t maxSize = 256; + if (name.Length() > maxSize) { + webgl->ErrorInvalidValue("%s: Identifier is %d characters long, exceeds the" + " maximum allowed length of %d characters.", + funcName, name.Length(), maxSize); + return false; + } + + if (!ValidateGLSLString(name, webgl, funcName)) + return false; + + nsString prefix1 = NS_LITERAL_STRING("webgl_"); + nsString prefix2 = NS_LITERAL_STRING("_webgl_"); + + if (Substring(name, 0, prefix1.Length()).Equals(prefix1) || + Substring(name, 0, prefix2.Length()).Equals(prefix2)) + { + webgl->ErrorInvalidOperation("%s: String contains a reserved GLSL prefix.", + funcName); + return false; + } + + return true; +} + +} // namespace mozilla
--- a/dom/canvas/WebGLValidateStrings.h +++ b/dom/canvas/WebGLValidateStrings.h @@ -22,231 +22,135 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef WEBGL_VALIDATE_STRINGS_H_ #define WEBGL_VALIDATE_STRINGS_H_ -#include "WebGLContext.h" +#include "nsString.h" +#include "nsTArray.h" namespace mozilla { +class WebGLContext; + // The following code was taken from the WebKit WebGL implementation, // which can be found here: // http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121 // Note that some modifications were done to adapt it to Mozilla. /****** BEGIN CODE TAKEN FROM WEBKIT ******/ - bool WebGLContext::ValidateGLSLCharacter(char16_t c) +// Strips comments from shader text. This allows non-ASCII characters +// to be used in comments without potentially breaking OpenGL +// implementations not expecting characters outside the GLSL ES set. +class StripComments { +public: + explicit StripComments(const nsAString& str) + : m_parseState(BeginningOfLine) + , m_end(str.EndReading()) + , m_current(str.BeginReading()) + , m_position(0) { - // Printing characters are valid except " $ ` @ \ ' DEL. - if (c >= 32 && c <= 126 && - c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'') - { - return true; - } - - // Horizontal tab, line feed, vertical tab, form feed, carriage return - // are also valid. - if (c >= 9 && c <= 13) { - return true; - } - - return false; + m_result.SetLength(str.Length()); + parse(); } - // Strips comments from shader text. This allows non-ASCII characters - // to be used in comments without potentially breaking OpenGL - // implementations not expecting characters outside the GLSL ES set. - class StripComments { - public: - explicit StripComments(const nsAString& str) - : m_parseState(BeginningOfLine) - , m_end(str.EndReading()) - , m_current(str.BeginReading()) - , m_position(0) - { - m_result.SetLength(str.Length()); - parse(); - } - - const nsTArray<char16_t>& result() - { - return m_result; - } - - size_t length() - { - return m_position; - } - - private: - bool hasMoreCharacters() - { - return (m_current < m_end); - } - - void parse() - { - while (hasMoreCharacters()) { - process(current()); - // process() might advance the position. - if (hasMoreCharacters()) - advance(); - } - } - - void process(char16_t); + const nsTArray<char16_t>& result() + { + return m_result; + } - bool peek(char16_t& character) - { - if (m_current + 1 >= m_end) - return false; - character = *(m_current + 1); - return true; - } - - char16_t current() - { - //ASSERT(m_position < m_length); - return *m_current; - } - - void advance() - { - ++m_current; - } - - bool isNewline(char16_t character) - { - // Don't attempt to canonicalize newline related characters. - return (character == '\n' || character == '\r'); - } - - void emit(char16_t character) - { - m_result[m_position++] = character; - } - - enum ParseState { - // Have not seen an ASCII non-whitespace character yet on - // this line. Possible that we might see a preprocessor - // directive. - BeginningOfLine, - - // Have seen at least one ASCII non-whitespace character - // on this line. - MiddleOfLine, - - // Handling a preprocessor directive. Passes through all - // characters up to the end of the line. Disables comment - // processing. - InPreprocessorDirective, - - // Handling a single-line comment. The comment text is - // replaced with a single space. - InSingleLineComment, + size_t length() + { + return m_position; + } - // Handling a multi-line comment. Newlines are passed - // through to preserve line numbers. - InMultiLineComment - }; - - ParseState m_parseState; - const char16_t* m_end; - const char16_t* m_current; - size_t m_position; - nsTArray<char16_t> m_result; - }; - - void StripComments::process(char16_t c) +private: + bool hasMoreCharacters() { - if (isNewline(c)) { - // No matter what state we are in, pass through newlines - // so we preserve line numbers. - emit(c); - - if (m_parseState != InMultiLineComment) - m_parseState = BeginningOfLine; - - return; - } - - char16_t temp = 0; - switch (m_parseState) { - case BeginningOfLine: - // If it's an ASCII space. - if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) { - emit(c); - break; - } - - if (c == '#') { - m_parseState = InPreprocessorDirective; - emit(c); - break; - } - - // Transition to normal state and re-handle character. - m_parseState = MiddleOfLine; - process(c); - break; + return (m_current < m_end); + } - case MiddleOfLine: - if (c == '/' && peek(temp)) { - if (temp == '/') { - m_parseState = InSingleLineComment; - emit(' '); - advance(); - break; - } - - if (temp == '*') { - m_parseState = InMultiLineComment; - // Emit the comment start in case the user has - // an unclosed comment and we want to later - // signal an error. - emit('/'); - emit('*'); - advance(); - break; - } - } - - emit(c); - break; - - case InPreprocessorDirective: - // No matter what the character is, just pass it - // through. Do not parse comments in this state. This - // might not be the right thing to do long term, but it - // should handle the #error preprocessor directive. - emit(c); - break; - - case InSingleLineComment: - // The newline code at the top of this function takes care - // of resetting our state when we get out of the - // single-line comment. Swallow all other characters. - break; - - case InMultiLineComment: - if (c == '*' && peek(temp) && temp == '/') { - emit('*'); - emit('/'); - m_parseState = MiddleOfLine; + void parse() + { + while (hasMoreCharacters()) { + process(current()); + // process() might advance the position. + if (hasMoreCharacters()) advance(); - break; - } - - // Swallow all other characters. Unclear whether we may - // want or need to just emit a space per character to try - // to preserve column numbers for debugging purposes. - break; } } + void process(char16_t); + + bool peek(char16_t& character) + { + if (m_current + 1 >= m_end) + return false; + character = *(m_current + 1); + return true; + } + + char16_t current() + { + //ASSERT(m_position < m_length); + return *m_current; + } + + void advance() + { + ++m_current; + } + + bool isNewline(char16_t character) + { + // Don't attempt to canonicalize newline related characters. + return (character == '\n' || character == '\r'); + } + + void emit(char16_t character) + { + m_result[m_position++] = character; + } + + enum ParseState { + // Have not seen an ASCII non-whitespace character yet on + // this line. Possible that we might see a preprocessor + // directive. + BeginningOfLine, + + // Have seen at least one ASCII non-whitespace character + // on this line. + MiddleOfLine, + + // Handling a preprocessor directive. Passes through all + // characters up to the end of the line. Disables comment + // processing. + InPreprocessorDirective, + + // Handling a single-line comment. The comment text is + // replaced with a single space. + InSingleLineComment, + + // Handling a multi-line comment. Newlines are passed + // through to preserve line numbers. + InMultiLineComment + }; + + ParseState m_parseState; + const char16_t* m_end; + const char16_t* m_current; + size_t m_position; + nsTArray<char16_t> m_result; +}; + /****** END CODE TAKEN FROM WEBKIT ******/ +bool ValidateGLSLString(const nsAString& string, WebGLContext* webgl, + const char* funcName); + +bool ValidateGLSLVariableName(const nsAString& name, WebGLContext* webgl, + const char* funcName); + } // namespace mozilla #endif // WEBGL_VALIDATE_STRINGS_H_
--- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -103,21 +103,23 @@ UNIFIED_SOURCES += [ 'WebGLFramebufferAttachable.cpp', 'WebGLObjectModel.cpp', 'WebGLProgram.cpp', 'WebGLQuery.cpp', 'WebGLRenderbuffer.cpp', 'WebGLSampler.cpp', 'WebGLShader.cpp', 'WebGLShaderPrecisionFormat.cpp', + 'WebGLShaderValidator.cpp', 'WebGLSync.cpp', 'WebGLTexelConversions.cpp', 'WebGLTexture.cpp', 'WebGLTransformFeedback.cpp', 'WebGLUniformLocation.cpp', + 'WebGLValidateStrings.cpp', 'WebGLVertexArray.cpp', 'WebGLVertexArrayFake.cpp', 'WebGLVertexArrayGL.cpp', ] LOCAL_INCLUDES += [ '/js/xpconnect/wrappers', ]
--- a/dom/canvas/test/webgl-conformance/conformance/misc/bad-arguments-test.html +++ b/dom/canvas/test/webgl-conformance/conformance/misc/bad-arguments-test.html @@ -63,25 +63,23 @@ var arguments = [ var argument; function shouldBeEmptyString(command) { shouldBe(command, "''"); } for (var i = 0; i < arguments.length; ++i) { - var func, func2, func3; + var func, func2; if (arguments[i].throws) { func = shouldThrow; func2 = shouldThrow; - func3 = shouldThrow; } else { func = shouldBeUndefined; func2 = shouldBeNull; - func3 = shouldBeEmptyString; } argument = arguments[i].value; func("context.compileShader(argument)"); func("context.linkProgram(argument)"); func("context.attachShader(program, argument)"); func("context.attachShader(argument, shader)"); func("context.detachShader(program, argument)"); func("context.detachShader(argument, shader)"); @@ -93,25 +91,24 @@ for (var i = 0; i < arguments.length; ++ func("context.bindRenderbuffer(context.RENDERBUFFER, argument)"); func("context.bindTexture(context.TEXTURE_2D, argument)"); func("context.framebufferRenderbuffer(context.FRAMEBUFFER, context.DEPTH_ATTACHMENT, context.RENDERBUFFER, argument)"); func("context.framebufferTexture2D(context.FRAMEBUFFER, context.COLOR_ATTACHMENT0, context.TEXTURE_2D, argument, 0)"); func("context.uniform2fv(argument, new Float32Array([0.0, 0.0]))"); func("context.uniform2iv(argument, new Int32Array([0, 0]))"); func("context.uniformMatrix2fv(argument, false, new Float32Array([0.0, 0.0, 0.0, 0.0]))"); + func2("context.getProgramInfoLog(argument)"); func2("context.getProgramParameter(argument, 0)"); + func2("context.getShaderInfoLog(argument)"); func2("context.getShaderParameter(argument, 0)"); + func2("context.getShaderSource(argument)"); func2("context.getUniform(argument, loc)"); func2("context.getUniform(program, argument)"); func2("context.getUniformLocation(argument, 'u_modelViewProjMatrix')"); - - func3("context.getProgramInfoLog(argument)"); - func3("context.getShaderInfoLog(argument)"); - func3("context.getShaderSource(argument)"); } successfullyParsed = true; </script> <script>finishTest();</script> </body> </html>
--- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -2010,17 +2010,17 @@ public: } void fGetShaderSource(GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source) { BEFORE_GL_CALL; mSymbols.fGetShaderSource(obj, maxLength, length, source); AFTER_GL_CALL; } - void fShaderSource(GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths) { + void fShaderSource(GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths) { BEFORE_GL_CALL; mSymbols.fShaderSource(shader, count, strings, lengths); AFTER_GL_CALL; } private: void raw_fBindFramebuffer(GLenum target, GLuint framebuffer) { BEFORE_GL_CALL;
--- a/gfx/gl/GLContextSymbols.h +++ b/gfx/gl/GLContextSymbols.h @@ -303,17 +303,17 @@ struct GLContextSymbols typedef void (GLAPIENTRY * PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint* param); PFNGLGETSHADERIVPROC fGetShaderiv; typedef void (GLAPIENTRY * PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); PFNGLGETSHADERINFOLOGPROC fGetShaderInfoLog; typedef void (GLAPIENTRY * PFNGETSHADERPRECISIONFORMAT) (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); PFNGETSHADERPRECISIONFORMAT fGetShaderPrecisionFormat; typedef void (GLAPIENTRY * PFNGLGETSHADERSOURCEPROC) (GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source); PFNGLGETSHADERSOURCEPROC fGetShaderSource; - typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths); + typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths); PFNGLSHADERSOURCEPROC fShaderSource; typedef void (GLAPIENTRY * PFNGLBINDFRAMEBUFFER) (GLenum target, GLuint framebuffer); PFNGLBINDFRAMEBUFFER fBindFramebuffer; typedef void (GLAPIENTRY * PFNGLBINDRENDERBUFFER) (GLenum target, GLuint renderbuffer); PFNGLBINDRENDERBUFFER fBindRenderbuffer; typedef GLenum (GLAPIENTRY * PFNGLCHECKFRAMEBUFFERSTATUS) (GLenum target); PFNGLCHECKFRAMEBUFFERSTATUS fCheckFramebufferStatus;
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3823,30 +3823,30 @@ pref("image.onload.decode.limit", 0); #ifdef ANDROID // Disable MSAA on mobile. pref("gl.msaa-level", 0); #else pref("gl.msaa-level", 2); #endif pref("webgl.force-enabled", false); pref("webgl.disabled", false); -pref("webgl.shader_validator", true); pref("webgl.disable-angle", false); pref("webgl.min_capability_mode", false); pref("webgl.disable-extensions", false); pref("webgl.msaa-force", false); pref("webgl.prefer-16bpp", false); pref("webgl.default-no-alpha", false); pref("webgl.force-layers-readback", false); pref("webgl.lose-context-on-memory-pressure", false); pref("webgl.can-lose-context-in-foreground", true); pref("webgl.restore-context-when-visible", true); pref("webgl.max-warnings-per-context", 32); pref("webgl.enable-draft-extensions", false); pref("webgl.enable-privileged-extensions", false); +pref("webgl.bypass-shader-validation", false); #ifdef XP_WIN pref("webgl.angle.try-d3d11", true); pref("webgl.angle.force-d3d11", false); #endif #ifdef MOZ_WIDGET_GONK pref("gfx.gralloc.fence-with-readpixels", false); #endif