Bug 1109945 - Rewrite shader/program handling. - r=kamidphish
This commit is contained in:
@@ -7,364 +7,690 @@
|
||||
|
||||
#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();
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->gl->fDeleteProgram(mGLName);
|
||||
gl::GLContext* gl = mContext->GL();
|
||||
|
||||
gl->MakeCurrent();
|
||||
gl->fDeleteProgram(mGLName);
|
||||
|
||||
mVertShader = nullptr;
|
||||
mFragShader = nullptr;
|
||||
|
||||
mMostRecentLinkInfo = nullptr;
|
||||
|
||||
LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
|
||||
}
|
||||
|
||||
bool
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GL funcs
|
||||
|
||||
void
|
||||
WebGLProgram::AttachShader(WebGLShader* shader)
|
||||
{
|
||||
if (ContainsShader(shader))
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
mAttachedShaders.AppendElement(shader);
|
||||
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(GLName(), shader->GLName());
|
||||
|
||||
return true;
|
||||
mContext->gl->fAttachShader(mGLName, shader->mGLName);
|
||||
}
|
||||
|
||||
bool
|
||||
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)
|
||||
{
|
||||
if (!mAttachedShaders.RemoveElement(shader))
|
||||
return false;
|
||||
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->fDetachShader(GLName(), shader->GLName());
|
||||
|
||||
return true;
|
||||
mContext->gl->fDetachShader(mGLName, shader->mGLName);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLProgram::HasAttachedShaderOfType(GLenum shaderType)
|
||||
already_AddRefed<WebGLActiveInfo>
|
||||
WebGLProgram::GetActiveAttrib(GLuint index) const
|
||||
{
|
||||
for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
|
||||
if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType)
|
||||
return true;
|
||||
if (!mMostRecentLinkInfo) {
|
||||
nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid();
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
return false;
|
||||
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();
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLProgram::HasBadShaderAttached()
|
||||
already_AddRefed<WebGLActiveInfo>
|
||||
WebGLProgram::GetActiveUniform(GLuint index) const
|
||||
{
|
||||
for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
|
||||
if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus())
|
||||
return true;
|
||||
if (!mMostRecentLinkInfo) {
|
||||
nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid();
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
const auto& activeList = mMostRecentLinkInfo->activeUniforms;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (index >= activeList.size()) {
|
||||
mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
|
||||
index, "ACTIVE_UNIFORMS", activeList.size());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return numSamplerUniforms;
|
||||
nsRefPtr<WebGLActiveInfo> ret = activeList[index];
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLProgram::MapIdentifier(const nsACString& name,
|
||||
nsCString* const out_mappedName)
|
||||
WebGLProgram::GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const
|
||||
{
|
||||
MOZ_ASSERT(mIdentifierMap);
|
||||
out->TruncateLength(0);
|
||||
|
||||
nsCString mutableName(name);
|
||||
nsCString bracketPart;
|
||||
bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
|
||||
if (hadBracketPart)
|
||||
mutableName.AppendLiteral("[0]");
|
||||
if (mVertShader)
|
||||
out->AppendElement(mVertShader);
|
||||
|
||||
if (mIdentifierMap->Get(mutableName, out_mappedName)) {
|
||||
if (hadBracketPart) {
|
||||
nsCString mappedBracketPart;
|
||||
bool mappedHadBracketPart = SplitLastSquareBracket(*out_mappedName,
|
||||
mappedBracketPart);
|
||||
if (mappedHadBracketPart)
|
||||
out_mappedName->Append(bracketPart);
|
||||
}
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
const NS_LossyConvertUTF16toASCII userName(userName_wide);
|
||||
|
||||
/* 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);
|
||||
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::ReverseMapIdentifier(const nsACString& name,
|
||||
nsCString* const out_reverseMappedName)
|
||||
WebGLProgram::GetProgramInfoLog(nsAString* const out) const
|
||||
{
|
||||
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);
|
||||
CopyASCIItoUTF16(mLinkLog, *out);
|
||||
}
|
||||
|
||||
WebGLUniformInfo
|
||||
WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name)
|
||||
static GLint
|
||||
GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname)
|
||||
{
|
||||
MOZ_ASSERT(mUniformInfoMap);
|
||||
GLint ret = 0;
|
||||
gl->fGetProgramiv(program, pname, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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]");
|
||||
JS::Value
|
||||
WebGLProgram::GetProgramParameter(GLenum pname) const
|
||||
{
|
||||
gl::GLContext* gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
|
||||
WebGLUniformInfo info;
|
||||
mUniformInfoMap->Get(mutableName, &info);
|
||||
// We don't check if that Get failed, as if it did, it left info with
|
||||
// default values.
|
||||
if (mContext->IsWebGL2()) {
|
||||
switch (pname) {
|
||||
case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
|
||||
return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
|
||||
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::UpdateInfo()
|
||||
WebGLProgram::LinkProgram()
|
||||
{
|
||||
mAttribMaxNameLength = 0;
|
||||
for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
|
||||
mAttribMaxNameLength = std::max(mAttribMaxNameLength,
|
||||
mAttachedShaders[i]->mAttribMaxNameLength);
|
||||
}
|
||||
mContext->InvalidateBufferFetching(); // we do it early in this function
|
||||
// as some of the validation below changes program state
|
||||
|
||||
GLint attribCount;
|
||||
mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount);
|
||||
mLinkLog.Truncate();
|
||||
mMostRecentLinkInfo = nullptr;
|
||||
|
||||
if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) {
|
||||
mContext->ErrorOutOfMemory("updateInfo: Out of memory to allocate %d"
|
||||
" attribs.", mContext->mGLMaxVertexAttribs);
|
||||
if (!mVertShader || !mVertShader->IsCompiled()) {
|
||||
mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mAttribsInUse.Length(); i++)
|
||||
mAttribsInUse[i] = false;
|
||||
if (!mFragShader || !mFragShader->IsCompiled()) {
|
||||
mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]);
|
||||
if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) {
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
gl::GLContext* gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
// 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];
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
bool
|
||||
WebGLProgram::UseProgram() const
|
||||
{
|
||||
if (!mMostRecentLinkInfo) {
|
||||
mContext->ErrorInvalidOperation("useProgram: Program has not been successfully"
|
||||
" linked.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mActiveAttribMap.clear();
|
||||
mContext->MakeContextCurrent();
|
||||
|
||||
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)));
|
||||
}
|
||||
mContext->InvalidateBufferFetching();
|
||||
|
||||
mContext->gl->fUseProgram(mGLName);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static*/ uint64_t
|
||||
WebGLProgram::IdentifierHashFunction(const char* ident, size_t size)
|
||||
void
|
||||
WebGLProgram::ValidateProgram() const
|
||||
{
|
||||
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];
|
||||
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);
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
WebGLProgram::HashMapIdentifier(const nsACString& name,
|
||||
nsCString* const out_hashedName)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool
|
||||
WebGLProgram::LinkAndUpdate()
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mAttachedShaders)
|
||||
bool
|
||||
WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
|
||||
nsDependentCString* const out_userName) const
|
||||
{
|
||||
if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
|
||||
nsCString* const out_userName,
|
||||
bool* const out_isArray) const
|
||||
{
|
||||
if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
|
||||
return true;
|
||||
|
||||
if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JSObject*
|
||||
WebGLProgram::WrapObject(JSContext* js)
|
||||
{
|
||||
return dom::WebGLProgramBinding::Wrap(js, this);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
|
||||
|
||||
Reference in New Issue
Block a user