michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WebGLContext.h" michael@0: #include "WebGLShader.h" michael@0: #include "WebGLProgram.h" michael@0: #include "mozilla/dom/WebGLRenderingContextBinding.h" michael@0: #include "GLContext.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]" in bracketPart michael@0: * michael@0: * \param string input/output: the string to split, becomes the string without the bracket part michael@0: * \param bracketPart output: gets the bracket part. michael@0: * michael@0: * Notice that if there are multiple brackets like "foo[i].bar[j]", only the last bracket is split. michael@0: */ michael@0: static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart) michael@0: { michael@0: MOZ_ASSERT(bracketPart.IsEmpty(), "SplitLastSquareBracket must be called with empty bracketPart string"); michael@0: michael@0: if (string.IsEmpty()) michael@0: return false; michael@0: michael@0: char *string_start = string.BeginWriting(); michael@0: char *s = string_start + string.Length() - 1; michael@0: michael@0: if (*s != ']') michael@0: return false; michael@0: michael@0: while (*s != '[' && s != string_start) michael@0: s--; michael@0: michael@0: if (*s != '[') michael@0: return false; michael@0: michael@0: bracketPart.Assign(s); michael@0: *s = 0; michael@0: string.EndWriting(); michael@0: string.SetLength(s - string_start); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: WebGLProgram::WrapObject(JSContext *cx) { michael@0: return dom::WebGLProgramBinding::Wrap(cx, this); michael@0: } michael@0: michael@0: WebGLProgram::WebGLProgram(WebGLContext *context) michael@0: : WebGLContextBoundObject(context) michael@0: , mLinkStatus(false) michael@0: , mGeneration(0) michael@0: , mAttribMaxNameLength(0) michael@0: { michael@0: SetIsDOMBinding(); michael@0: mContext->MakeContextCurrent(); michael@0: mGLName = mContext->gl->fCreateProgram(); michael@0: mContext->mPrograms.insertBack(this); michael@0: } michael@0: michael@0: void michael@0: WebGLProgram::Delete() { michael@0: DetachShaders(); michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fDeleteProgram(mGLName); michael@0: LinkedListElement::removeFrom(mContext->mPrograms); michael@0: } michael@0: michael@0: bool michael@0: WebGLProgram::AttachShader(WebGLShader *shader) { michael@0: if (ContainsShader(shader)) michael@0: return false; michael@0: mAttachedShaders.AppendElement(shader); michael@0: michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fAttachShader(GLName(), shader->GLName()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLProgram::DetachShader(WebGLShader *shader) { michael@0: if (!mAttachedShaders.RemoveElement(shader)) michael@0: return false; michael@0: michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fDetachShader(GLName(), shader->GLName()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLProgram::HasAttachedShaderOfType(GLenum shaderType) { michael@0: for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { michael@0: if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: WebGLProgram::HasBadShaderAttached() { michael@0: for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { michael@0: if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: size_t michael@0: WebGLProgram::UpperBoundNumSamplerUniforms() { michael@0: size_t numSamplerUniforms = 0; michael@0: for (size_t i = 0; i < mAttachedShaders.Length(); ++i) { michael@0: const WebGLShader *shader = mAttachedShaders[i]; michael@0: if (!shader) michael@0: continue; michael@0: for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) { michael@0: WebGLUniformInfo u = shader->mUniformInfos[j]; michael@0: if (u.type == SH_SAMPLER_2D || michael@0: u.type == SH_SAMPLER_CUBE) michael@0: { michael@0: numSamplerUniforms += u.arraySize; michael@0: } michael@0: } michael@0: } michael@0: return numSamplerUniforms; michael@0: } michael@0: michael@0: void michael@0: WebGLProgram::MapIdentifier(const nsACString& name, nsCString *mappedName) { michael@0: if (!mIdentifierMap) { michael@0: // if the identifier map doesn't exist yet, build it now michael@0: mIdentifierMap = new CStringMap; michael@0: for (size_t i = 0; i < mAttachedShaders.Length(); i++) { michael@0: for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { michael@0: const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; michael@0: mIdentifierMap->Put(attrib.original, attrib.mapped); michael@0: } michael@0: for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { michael@0: const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; michael@0: mIdentifierMap->Put(uniform.original, uniform.mapped); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsCString mutableName(name); michael@0: nsCString bracketPart; michael@0: bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); michael@0: if (hadBracketPart) michael@0: mutableName.AppendLiteral("[0]"); michael@0: michael@0: if (mIdentifierMap->Get(mutableName, mappedName)) { michael@0: if (hadBracketPart) { michael@0: nsCString mappedBracketPart; michael@0: bool mappedHadBracketPart = SplitLastSquareBracket(*mappedName, mappedBracketPart); michael@0: if (mappedHadBracketPart) michael@0: mappedName->Append(bracketPart); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform michael@0: // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. michael@0: mutableName.AppendLiteral("[0]"); michael@0: if (mIdentifierMap->Get(mutableName, mappedName)) michael@0: return; michael@0: michael@0: // not found? return name unchanged. This case happens e.g. on bad user input, or when michael@0: // we're not using identifier mapping, or if we didn't store an identifier in the map because michael@0: // e.g. its mapping is trivial (as happens for short identifiers) michael@0: mappedName->Assign(name); michael@0: } michael@0: michael@0: void michael@0: WebGLProgram::ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName) { michael@0: if (!mIdentifierReverseMap) { michael@0: // if the identifier reverse map doesn't exist yet, build it now michael@0: mIdentifierReverseMap = new CStringMap; michael@0: for (size_t i = 0; i < mAttachedShaders.Length(); i++) { michael@0: for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { michael@0: const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; michael@0: mIdentifierReverseMap->Put(attrib.mapped, attrib.original); michael@0: } michael@0: for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { michael@0: const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; michael@0: mIdentifierReverseMap->Put(uniform.mapped, uniform.original); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsCString mutableName(name); michael@0: nsCString bracketPart; michael@0: bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); michael@0: if (hadBracketPart) michael@0: mutableName.AppendLiteral("[0]"); michael@0: michael@0: if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) { michael@0: if (hadBracketPart) { michael@0: nsCString reverseMappedBracketPart; michael@0: bool reverseMappedHadBracketPart = SplitLastSquareBracket(*reverseMappedName, reverseMappedBracketPart); michael@0: if (reverseMappedHadBracketPart) michael@0: reverseMappedName->Append(bracketPart); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform michael@0: // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. michael@0: mutableName.AppendLiteral("[0]"); michael@0: if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) michael@0: return; michael@0: michael@0: // not found? return name unchanged. This case happens e.g. on bad user input, or when michael@0: // we're not using identifier mapping, or if we didn't store an identifier in the map because michael@0: // e.g. its mapping is trivial (as happens for short identifiers) michael@0: reverseMappedName->Assign(name); michael@0: } michael@0: michael@0: WebGLUniformInfo michael@0: WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name) { michael@0: nsCString mutableName(name); michael@0: nsCString bracketPart; michael@0: bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); michael@0: // if there is a bracket, we're either an array or an entry in an array. michael@0: if (hadBracketPart) michael@0: mutableName.AppendLiteral("[0]"); michael@0: michael@0: WebGLUniformInfo info; michael@0: mUniformInfoMap->Get(mutableName, &info); michael@0: // we don't check if that Get failed, as if it did, it left info with default values michael@0: michael@0: return info; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(WebGLProgram, mAttachedShaders) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef) michael@0: NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)