gecko-dev/dom/canvas/WebGLProgram.cpp
2014-11-13 20:03:50 -08:00

373 lines
12 KiB
C++

/* -*- 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 "WebGLProgram.h"
#include "GLContext.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "MurmurHash3.h"
#include "WebGLContext.h"
#include "WebGLShader.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.
*/
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* webgl)
: WebGLContextBoundObject(webgl)
, mLinkStatus(false)
, mGeneration(0)
, mIdentifierMap(new CStringMap)
, mIdentifierReverseMap(new CStringMap)
, mUniformInfoMap(new CStringToUniformInfoMap)
, mAttribMaxNameLength(0)
{
mContext->MakeContextCurrent();
mGLName = mContext->gl->fCreateProgram();
mContext->mPrograms.insertBack(this);
}
void
WebGLProgram::Delete()
{
DetachShaders();
mContext->MakeContextCurrent();
mContext->gl->fDeleteProgram(mGLName);
LinkedListElement<WebGLProgram>::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 == 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;
/* 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;
}
// 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;
}
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;
}
/*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_ROOT_NATIVE(WebGLProgram, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
} // namespace mozilla