diff -r 000000000000 -r 6474c204b198 content/canvas/src/WebGLProgram.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/canvas/src/WebGLProgram.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,243 @@ +/* -*- 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 "WebGLContext.h" +#include "WebGLShader.h" +#include "WebGLProgram.h" +#include "mozilla/dom/WebGLRenderingContextBinding.h" +#include "GLContext.h" + +using 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. + */ +static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart) +{ + MOZ_ASSERT(bracketPart.IsEmpty(), "SplitLastSquareBracket must be called with empty bracketPart string"); + + if (string.IsEmpty()) + return false; + + char *string_start = string.BeginWriting(); + char *s = string_start + string.Length() - 1; + + if (*s != ']') + return false; + + while (*s != '[' && s != string_start) + s--; + + if (*s != '[') + return false; + + bracketPart.Assign(s); + *s = 0; + string.EndWriting(); + string.SetLength(s - string_start); + return true; +} + +JSObject* +WebGLProgram::WrapObject(JSContext *cx) { + return dom::WebGLProgramBinding::Wrap(cx, this); +} + +WebGLProgram::WebGLProgram(WebGLContext *context) + : WebGLContextBoundObject(context) + , mLinkStatus(false) + , mGeneration(0) + , mAttribMaxNameLength(0) +{ + SetIsDOMBinding(); + mContext->MakeContextCurrent(); + mGLName = mContext->gl->fCreateProgram(); + mContext->mPrograms.insertBack(this); +} + +void +WebGLProgram::Delete() { + DetachShaders(); + mContext->MakeContextCurrent(); + mContext->gl->fDeleteProgram(mGLName); + LinkedListElement::removeFrom(mContext->mPrograms); +} + +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) { + if (!mAttachedShaders.RemoveElement(shader)) + return false; + + mContext->MakeContextCurrent(); + mContext->gl->fDetachShader(GLName(), shader->GLName()); + + return true; +} + +bool +WebGLProgram::HasAttachedShaderOfType(GLenum shaderType) { + for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { + if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) { + return true; + } + } + return false; +} + +bool +WebGLProgram::HasBadShaderAttached() { + for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { + if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus()) { + return true; + } + } + return false; +} + +size_t +WebGLProgram::UpperBoundNumSamplerUniforms() { + 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 == SH_SAMPLER_2D || + u.type == SH_SAMPLER_CUBE) + { + numSamplerUniforms += u.arraySize; + } + } + } + return numSamplerUniforms; +} + +void +WebGLProgram::MapIdentifier(const nsACString& name, nsCString *mappedName) { + if (!mIdentifierMap) { + // if the identifier map doesn't exist yet, build it now + mIdentifierMap = new CStringMap; + for (size_t i = 0; i < mAttachedShaders.Length(); i++) { + for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { + const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; + mIdentifierMap->Put(attrib.original, attrib.mapped); + } + for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { + const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; + mIdentifierMap->Put(uniform.original, uniform.mapped); + } + } + } + + nsCString mutableName(name); + nsCString bracketPart; + bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); + if (hadBracketPart) + mutableName.AppendLiteral("[0]"); + + if (mIdentifierMap->Get(mutableName, mappedName)) { + if (hadBracketPart) { + nsCString mappedBracketPart; + bool mappedHadBracketPart = SplitLastSquareBracket(*mappedName, mappedBracketPart); + if (mappedHadBracketPart) + mappedName->Append(bracketPart); + } + return; + } + + // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform + // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. + mutableName.AppendLiteral("[0]"); + if (mIdentifierMap->Get(mutableName, mappedName)) + return; + + // not found? return name unchanged. This case happens e.g. on bad user input, or when + // we're not using identifier mapping, or if we didn't store an identifier in the map because + // e.g. its mapping is trivial (as happens for short identifiers) + mappedName->Assign(name); +} + +void +WebGLProgram::ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName) { + if (!mIdentifierReverseMap) { + // if the identifier reverse map doesn't exist yet, build it now + mIdentifierReverseMap = new CStringMap; + for (size_t i = 0; i < mAttachedShaders.Length(); i++) { + for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { + const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; + mIdentifierReverseMap->Put(attrib.mapped, attrib.original); + } + for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { + const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; + mIdentifierReverseMap->Put(uniform.mapped, uniform.original); + } + } + } + + nsCString mutableName(name); + nsCString bracketPart; + bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); + if (hadBracketPart) + mutableName.AppendLiteral("[0]"); + + if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) { + if (hadBracketPart) { + nsCString reverseMappedBracketPart; + bool reverseMappedHadBracketPart = SplitLastSquareBracket(*reverseMappedName, reverseMappedBracketPart); + if (reverseMappedHadBracketPart) + reverseMappedName->Append(bracketPart); + } + return; + } + + // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform + // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. + mutableName.AppendLiteral("[0]"); + if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) + return; + + // not found? return name unchanged. This case happens e.g. on bad user input, or when + // we're not using identifier mapping, or if we didn't store an identifier in the map because + // e.g. its mapping is trivial (as happens for short identifiers) + reverseMappedName->Assign(name); +} + +WebGLUniformInfo +WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name) { + 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; +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(WebGLProgram, mAttachedShaders) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)