blob: 1027e9ebee342ee832a96ecab04ffb49b6add44b [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ProgramData.h"
#include "apigen-codec-common/glUtils.h"
#include "aemu/base/containers/Lookup.h"
#include "aemu/base/files/StreamSerializing.h"
#include "ANGLEShaderParser.h"
#include "GLcommon/GLutils.h"
#include "GLcommon/GLESmacros.h"
#include "GLcommon/ShareGroup.h"
#include <GLES3/gl31.h>
#include <string.h>
#include <unordered_set>
GLUniformDesc::GLUniformDesc(const char* name, GLint location, GLsizei count, GLboolean transpose,
GLenum type, GLsizei size, unsigned char* val)
: mCount(count), mTranspose(transpose), mType(type)
, mVal(val, val + size), mGuestName(name) { }
GLUniformDesc::GLUniformDesc(android::base::Stream* stream) {
mCount = stream->getBe32();
mTranspose = stream->getByte();
mType = stream->getBe32();
loadBuffer(stream, &mVal);
mGuestName = stream->getString();
}
void GLUniformDesc::onSave(android::base::Stream* stream) const {
stream->putBe32(mCount);
stream->putByte(mTranspose);
stream->putBe32(mType);
saveBuffer(stream, mVal);
stream->putString(mGuestName);
}
static int s_glShaderType2ShaderType(GLenum type) {
switch (type) {
case GL_VERTEX_SHADER:
return ProgramData::VERTEX;
break;
case GL_FRAGMENT_SHADER:
return ProgramData::FRAGMENT;
break;
case GL_COMPUTE_SHADER:
return ProgramData::COMPUTE;
break;
default:
assert(0);
break;
}
return ProgramData::NUM_SHADER_TYPE;
}
ProgramData::ProgramData(int glesMaj, int glesMin)
: ObjectData(PROGRAM_DATA),
ValidateStatus(false),
LinkStatus(false),
HostLinkStatus(false),
IsInUse(false),
DeleteStatus(false),
mGlesMajorVersion(glesMaj),
mGlesMinorVersion(glesMin) {}
ProgramData::ProgramData(android::base::Stream* stream) :
ObjectData(stream) {
auto loadAttribLocs = [](android::base::Stream* stream) {
std::string attrib = stream->getString();
GLuint loc = stream->getBe32();
return std::make_pair(std::move(attrib), loc);
};
loadCollection(stream, &boundAttribLocs, loadAttribLocs);
loadCollection(stream, &linkedAttribLocs, loadAttribLocs);
loadCollection(stream, &uniforms, [](android::base::Stream* stream) {
GLuint loc = stream->getBe32();
GLUniformDesc desc(stream);
return std::make_pair(loc, std::move(desc));
});
loadCollection(stream, &mUniformBlockBinding,
[](android::base::Stream* stream) {
GLuint block = stream->getBe32();
GLuint binding = stream->getBe32();
return std::make_pair(block, binding);
});
int transformFeedbackCount = stream->getBe32();
mTransformFeedbacks.resize(transformFeedbackCount);
for (auto& feedback : mTransformFeedbacks) {
feedback = stream->getString();
}
mTransformFeedbackBufferMode = stream->getBe32();
for (auto& s : attachedShaders) {
s.localName = stream->getBe32();
s.linkedSource = stream->getString();
}
validationInfoLog = stream->getString();
infoLog = stream->getString();
stream->getBe16(); /* padding to maintain snapshot file compatibility */
ValidateStatus = stream->getByte();
LinkStatus = stream->getByte();
IsInUse = stream->getByte();
DeleteStatus = stream->getByte();
mGlesMajorVersion = stream->getByte();
mGlesMinorVersion = stream->getByte();
loadCollection(stream, &mUniNameToGuestLoc,
[](android::base::Stream* stream) {
std::string name = stream->getString();
int loc = stream->getBe32();
return std::make_pair(name, loc);
});
}
void ProgramData::getUniformValue(const GLchar *name, GLenum type,
std::unordered_map<GLuint, GLUniformDesc> &uniformsOnSave) const {
alignas(double) unsigned char val[256]; //Large enought to hold MAT4x4
GLDispatch& dispatcher = GLEScontext::dispatcher();
GLint location = dispatcher.glGetUniformLocation(ProgramName, name);
if (location < 0) {
return;
}
switch(type) {
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
dispatcher.glGetUniformfv(ProgramName, location, (GLfloat *)val);
break;
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_CUBE_SHADOW:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
dispatcher.glGetUniformiv(ProgramName, location, (GLint *)val);
break;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
dispatcher.glGetUniformuiv(ProgramName, location, (GLuint *)val);
break;
default:
fprintf(stderr, "ProgramData::gtUniformValue: warning: "
"unsupported uniform type 0x%x\n", type);
return;
}
GLUniformDesc uniformDesc(name, location, 1, 0, /*transpose*/
type, glSizeof(type), val);
if (!isGles2Gles()) {
uniformDesc.mGuestName = getDetranslatedName(uniformDesc.mGuestName);
}
uniformsOnSave[location] = std::move(uniformDesc);
}
static std::string getBaseName(const std::string& name) {
std::string baseName;
int length = name.length();
if (length < 3) return name;
if (name.compare(length - 3, 1, "[") == 0) {
baseName = name.substr(0, length - 3);
} else {
baseName = name;
}
return baseName;
}
// Query uniform variables from driver
std::unordered_map<GLuint, GLUniformDesc> ProgramData::collectUniformInfo() const {
GLint uniform_count = 0;
GLint nameLength = 0;
std::unordered_map<GLuint, GLUniformDesc> uniformsOnSave;
GLDispatch& dispatcher = GLEScontext::dispatcher();
dispatcher.glGetProgramiv(ProgramName, GL_ACTIVE_UNIFORM_MAX_LENGTH, &nameLength);
if (nameLength == 0) {
// No active uniform variables exist.
return uniformsOnSave;
}
dispatcher.glGetProgramiv(ProgramName, GL_ACTIVE_UNIFORMS, &uniform_count);
std::vector<char> name(nameLength, 0);
for (int i = 0; i < uniform_count; i++) {
GLint size;
GLenum type;
GLsizei length;
dispatcher.glGetActiveUniform(ProgramName, i, nameLength, &length,
&size, &type, name.data());
if (size > 1) {
// Uniform array, drivers may return 'arrayName' or 'arrayName[0]'
// as the name of the array.
// Need to append '[arrayIndex]' after 'arrayName' to query the
// value for each array member.
std::string baseName = getBaseName(std::string(name.data()));
for (int arrayIndex = 0; arrayIndex < size; arrayIndex++) {
std::ostringstream oss;
oss << baseName << '[' << arrayIndex << ']';
std::string toSaveName = oss.str();
getUniformValue(toSaveName.c_str(), type, uniformsOnSave);
}
}
else {
getUniformValue(name.data(), type, uniformsOnSave);
}
}
return uniformsOnSave;
}
static std::unordered_map<GLuint, GLuint> collectUniformBlockInfo(GLuint pname) {
GLint uniformBlockCount = 0;
std::unordered_map<GLuint, GLuint> uniformBlocks;
GLDispatch& dispatcher = GLEScontext::dispatcher();
dispatcher.glGetProgramiv(pname, GL_ACTIVE_UNIFORM_BLOCKS,
&uniformBlockCount);
for (int i = 0; i < uniformBlockCount; i++) {
GLint binding = 0;
dispatcher.glGetActiveUniformBlockiv(pname, i, GL_UNIFORM_BLOCK_BINDING,
&binding);
uniformBlocks.emplace(i, binding);
}
return uniformBlocks;
}
static std::vector<std::string> collectTransformFeedbackInfo(GLuint pname) {
GLint transformFeedbackCount = 0;
GLint transformFeedbakMaxLength = 0;
GLDispatch& dispatcher = GLEScontext::dispatcher();
dispatcher.glGetProgramiv(pname, GL_TRANSFORM_FEEDBACK_VARYINGS,
&transformFeedbackCount);
dispatcher.glGetProgramiv(pname, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
&transformFeedbakMaxLength);
std::vector<std::string> transformFeedbacks(transformFeedbackCount);
std::unique_ptr<char[]> nameBuffer(new char [transformFeedbakMaxLength]);
for (int i = 0; i < transformFeedbackCount; i++) {
GLsizei size;
GLenum type;
dispatcher.glGetTransformFeedbackVarying(pname, i,
transformFeedbakMaxLength, nullptr, &size, &type,
nameBuffer.get());
transformFeedbacks[i] = nameBuffer.get();
}
return transformFeedbacks;
}
void ProgramData::onSave(android::base::Stream* stream, unsigned int globalName) const {
// The first byte is used to distinguish between program and shader object.
// It will be loaded outside of this class
stream->putByte(LOAD_PROGRAM);
ObjectData::onSave(stream, globalName);
auto saveAttribLocs = [](android::base::Stream* stream,
const std::pair<std::string, GLuint>& attribLoc) {
stream->putString(attribLoc.first);
stream->putBe32(attribLoc.second);
};
saveCollection(stream, boundAttribLocs, saveAttribLocs);
saveCollection(stream, linkedAttribLocs, saveAttribLocs);
auto saveUniform = [](android::base::Stream* stream,
const std::pair<const GLuint, GLUniformDesc>& uniform) {
stream->putBe32(uniform.first);
uniform.second.onSave(stream);
};
auto saveUniformBlock = [](android::base::Stream* stream,
const std::pair<const GLuint, GLuint>& uniformBlock) {
stream->putBe32(uniformBlock.first);
stream->putBe32(uniformBlock.second);
};
auto saveTransformFeedbacks = [](android::base::Stream* stream,
const std::vector<std::string>& transformFeedbacks) {
stream->putBe32((int)transformFeedbacks.size());
for (const auto& feedback : transformFeedbacks) {
stream->putString(feedback);
}
};
if (needRestore()) {
saveCollection(stream, uniforms, saveUniform);
saveCollection(stream, mUniformBlockBinding, saveUniformBlock);
saveTransformFeedbacks(stream, mTransformFeedbacks);
stream->putBe32(mTransformFeedbackBufferMode);
} else {
std::unordered_map<GLuint, GLUniformDesc> uniformsOnSave =
collectUniformInfo();
std::unordered_map<GLuint, GLuint> uniformBlocks;
std::vector<std::string> transformFeedbacks;
if (mGlesMajorVersion >= 3) {
uniformBlocks = collectUniformBlockInfo(ProgramName);
transformFeedbacks = collectTransformFeedbackInfo(ProgramName);
GLEScontext::dispatcher().glGetProgramiv(ProgramName,
GL_TRANSFORM_FEEDBACK_BUFFER_MODE,
(GLint*)&mTransformFeedbackBufferMode);
}
saveCollection(stream, uniformsOnSave, saveUniform);
saveCollection(stream, uniformBlocks, saveUniformBlock);
saveTransformFeedbacks(stream, transformFeedbacks);
stream->putBe32(mTransformFeedbackBufferMode);
}
for (const auto& s : attachedShaders) {
stream->putBe32(s.localName);
stream->putString(s.linkedSource);
// s.linkedInfo will be regenerated on restore
// This is for compatibility over different rendering backends
}
stream->putString(validationInfoLog);
stream->putString(infoLog);
stream->putBe16(0 /* padding to maintain snapshot file compatibility */);
stream->putByte(ValidateStatus);
stream->putByte(LinkStatus);
stream->putByte(IsInUse);
stream->putByte(DeleteStatus);
stream->putByte(mGlesMajorVersion);
stream->putByte(mGlesMinorVersion);
saveCollection(stream, mUniNameToGuestLoc, [](android::base::Stream* stream,
const std::pair<std::string, int>& uniNameLoc) {
stream->putString(uniNameLoc.first);
stream->putBe32(uniNameLoc.second);
});
}
void ProgramData::postLoad(const getObjDataPtr_t& getObjDataPtr) {
for (auto& s : attachedShaders) {
if (s.localName) {
s.shader = (ShaderParser*)getObjDataPtr(
NamedObjectType::SHADER_OR_PROGRAM, s.localName).get();
}
}
}
void ProgramData::restore(ObjectLocalName localName,
const getGlobalName_t& getGlobalName) {
ObjectData::restore(localName, getGlobalName);
int globalName = getGlobalName(NamedObjectType::SHADER_OR_PROGRAM,
localName);
assert(globalName);
ProgramName = globalName;
GLDispatch& dispatcher = GLEScontext::dispatcher();
mGuestLocToHostLoc.add(-1, -1);
bool shoudLoadLinked = LinkStatus;
#if defined(TOLERATE_PROGRAM_LINK_ERROR) && TOLERATE_PROGRAM_LINK_ERROR == 1
shoudLoadLinked = 1;
#endif
if (shoudLoadLinked) {
// Really, each program name corresponds to 2 programs:
// the one that is already linked, and the one that is not yet linked.
// We need to restore both.
GLint tmpShaders[NUM_SHADER_TYPE];
for (int i = 0; i < NUM_SHADER_TYPE; i++) {
AttachedShader& s = attachedShaders[i];
if (s.linkedSource.empty()) {
tmpShaders[i] = 0;
continue;
}
GLenum type = 0;
switch (i) {
case VERTEX:
type = GL_VERTEX_SHADER;
break;
case FRAGMENT:
type = GL_FRAGMENT_SHADER;
break;
case COMPUTE:
type = GL_COMPUTE_SHADER;
break;
default:
assert(0);
}
tmpShaders[i] = dispatcher.glCreateShader(type);
const GLchar* src = (const GLchar *)s.linkedSource.c_str();
std::string parsedSrc;
if (!isGles2Gles()) {
#ifdef USE_ANGLE_SHADER_PARSER
std::string infoLog;
ANGLEShaderParser::translate(
isCoreProfile(),
src,
type,
&infoLog,
&parsedSrc,
&s.linkInfo);
src = parsedSrc.c_str();
#endif
}
dispatcher.glShaderSource(tmpShaders[i], 1, &src, NULL);
dispatcher.glCompileShader(tmpShaders[i]);
dispatcher.glAttachShader(globalName, tmpShaders[i]);
}
for (const auto& attribLocs : linkedAttribLocs) {
// Prefix "gl_" is reserved, we should skip those.
// https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBindAttribLocation.xhtml
if (strncmp(attribLocs.first.c_str(), "gl_", 3) == 0) {
continue;
}
dispatcher.glBindAttribLocation(globalName, attribLocs.second,
attribLocs.first.c_str());
}
if (mGlesMajorVersion >= 3) {
std::vector<const char*> varyings;
varyings.resize(mTransformFeedbacks.size());
for (size_t i = 0; i < mTransformFeedbacks.size(); i++) {
varyings[i] = mTransformFeedbacks[i].c_str();
}
dispatcher.glTransformFeedbackVaryings(
globalName, mTransformFeedbacks.size(), varyings.data(),
mTransformFeedbackBufferMode);
mTransformFeedbacks.clear();
}
dispatcher.glLinkProgram(globalName);
dispatcher.glUseProgram(globalName);
#ifdef DEBUG
for (const auto& attribLocs : linkedAttribLocs) {
assert(dispatcher.glGetAttribLocation(globalName,
attribLocs.first.c_str()) == attribLocs.second);
}
#endif // DEBUG
for (const auto& uniform : mUniNameToGuestLoc) {
GLint hostLoc = dispatcher.glGetUniformLocation(
globalName, getTranslatedName(uniform.first).c_str());
if (hostLoc != -1) {
mGuestLocToHostLoc.add(uniform.second, hostLoc);
}
}
for (const auto& uniformEntry : uniforms) {
const auto& uniform = uniformEntry.second;
GLint location = dispatcher.glGetUniformLocation(
globalName, getTranslatedName(uniform.mGuestName).c_str());
if (location == -1) {
// Location changed after loading from a snapshot.
// likely loading from different GPU backend (and they
// optimize out different stuff)
continue;
}
switch (uniform.mType) {
case GL_FLOAT:
dispatcher.glUniform1fv(location, uniform.mCount,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_VEC2:
dispatcher.glUniform2fv(location, uniform.mCount,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_VEC3:
dispatcher.glUniform3fv(location, uniform.mCount,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_VEC4:
dispatcher.glUniform4fv(location, uniform.mCount,
(const GLfloat*)uniform.mVal.data());
break;
case GL_BOOL:
case GL_INT:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_CUBE_SHADOW:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
dispatcher.glUniform1iv(location, uniform.mCount,
(const GLint*)uniform.mVal.data());
break;
case GL_BOOL_VEC2:
case GL_INT_VEC2:
dispatcher.glUniform2iv(location, uniform.mCount,
(const GLint*)uniform.mVal.data());
break;
case GL_BOOL_VEC3:
case GL_INT_VEC3:
dispatcher.glUniform3iv(location, uniform.mCount,
(const GLint*)uniform.mVal.data());
break;
case GL_BOOL_VEC4:
case GL_INT_VEC4:
dispatcher.glUniform4iv(location, uniform.mCount,
(const GLint*)uniform.mVal.data());
break;
case GL_UNSIGNED_INT:
dispatcher.glUniform1uiv(location, uniform.mCount,
(const GLuint*)uniform.mVal.data());
break;
case GL_UNSIGNED_INT_VEC2:
dispatcher.glUniform2uiv(location, uniform.mCount,
(const GLuint*)uniform.mVal.data());
break;
case GL_UNSIGNED_INT_VEC3:
dispatcher.glUniform3uiv(location, uniform.mCount,
(const GLuint*)uniform.mVal.data());
break;
case GL_UNSIGNED_INT_VEC4:
dispatcher.glUniform4uiv(location, uniform.mCount,
(const GLuint*)uniform.mVal.data());
break;
case GL_FLOAT_MAT2:
dispatcher.glUniformMatrix2fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT3:
dispatcher.glUniformMatrix3fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT4:
dispatcher.glUniformMatrix4fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT2x3:
dispatcher.glUniformMatrix2x3fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT2x4:
dispatcher.glUniformMatrix2x4fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT3x2:
dispatcher.glUniformMatrix3x2fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT3x4:
dispatcher.glUniformMatrix3x4fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT4x2:
dispatcher.glUniformMatrix4x2fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
case GL_FLOAT_MAT4x3:
dispatcher.glUniformMatrix4x3fv(location,
uniform.mCount, uniform.mTranspose,
(const GLfloat*)uniform.mVal.data());
break;
default:
fprintf(stderr, "ProgramData::restore: warning: "
"unsupported uniform type 0x%x\n", uniform.mType);
}
}
for (const auto& uniformBlock : mUniformBlockBinding) {
dispatcher.glUniformBlockBinding(globalName, uniformBlock.first,
uniformBlock.second);
}
for (auto s : tmpShaders) {
if (s != 0) {
dispatcher.glDetachShader(globalName, s);
dispatcher.glDeleteShader(s);
}
}
}
uniforms.clear();
mUniformBlockBinding.clear();
// We are done with the "linked" program, now we handle the one
// that is yet to compile
for (const auto& s : attachedShaders) {
if (s.localName) {
int shaderGlobalName = getGlobalName(
NamedObjectType::SHADER_OR_PROGRAM, s.localName);
assert(shaderGlobalName);
dispatcher.glAttachShader(globalName, shaderGlobalName);
}
}
for (const auto& attribLocs : boundAttribLocs) {
dispatcher.glBindAttribLocation(globalName, attribLocs.second,
attribLocs.first.c_str());
}
}
GenNameInfo ProgramData::getGenNameInfo() const {
return GenNameInfo(ShaderProgramType::PROGRAM);
}
void ProgramData::setErrInfoLog() {
infoLog.clear();
infoLog = std::string(validationInfoLog);
}
void ProgramData::setInfoLog(const GLchar* log) {
infoLog = std::string(log);
}
const GLchar* ProgramData::getInfoLog() const {
return infoLog.c_str();
}
GLuint ProgramData::getAttachedVertexShader() const {
return attachedShaders[VERTEX].localName;
}
GLuint ProgramData::getAttachedFragmentShader() const {
return attachedShaders[FRAGMENT].localName;
}
GLuint ProgramData::getAttachedComputeShader() const {
return attachedShaders[COMPUTE].localName;
}
GLuint ProgramData::getAttachedShader(GLenum type) const {
return attachedShaders[s_glShaderType2ShaderType(type)].localName;
}
std::string
ProgramData::getTranslatedName(const std::string& userVarName) const {
if (isGles2Gles()) {
return userVarName;
}
// TODO: translate uniform array names
#ifdef USE_ANGLE_SHADER_PARSER
for (int i = 0; i < NUM_SHADER_TYPE; i++) {
if (const auto name = android::base::find(
attachedShaders[i].linkInfo.nameMap, userVarName)) {
return *name;
}
}
#endif
return userVarName;
}
std::string
ProgramData::getDetranslatedName(const std::string& driverName) const {
if (isGles2Gles()) {
return driverName;
}
#ifdef USE_ANGLE_SHADER_PARSER
// TODO: detranslate uniform array names
for (int i = 0; i < NUM_SHADER_TYPE; i++) {
if (const auto name = android::base::find(
attachedShaders[i].linkInfo.nameMapReverse, driverName)) {
return *name;
}
}
#endif
return driverName;
}
bool ProgramData::attachShader(GLuint shader, ShaderParser* shaderData,
GLenum type) {
AttachedShader& s = attachedShaders[s_glShaderType2ShaderType(type)];
if (s.localName == 0) {
s.localName = shader;
s.shader = shaderData;
return true;
}
return false;
}
bool ProgramData::isAttached(GLuint shader) const {
for (const auto& s : attachedShaders) {
if (s.localName == shader) return true;
}
return false;
}
bool ProgramData::detachShader(GLuint shader) {
for (auto& s : attachedShaders) {
if (s.localName == shader) {
s.localName = 0;
s.shader = nullptr;
return true;
}
}
return false;
}
void ProgramData::bindAttribLocation(const std::string& var, GLuint loc) {
boundAttribLocs[var] = loc;
}
void ProgramData::linkedAttribLocation(const std::string& var, GLuint loc) {
linkedAttribLocs[var] = loc;
}
// Link-time validation
void ProgramData::appendValidationErrMsg(std::ostringstream& ss) {
validationInfoLog += "Error: " + ss.str() + "\n";
}
#ifdef USE_ANGLE_SHADER_PARSER
static bool sCheckUndecl(ProgramData* pData,
const ANGLEShaderParser::ShaderLinkInfo& fragLinkInfo,
const ANGLEShaderParser::ShaderLinkInfo& vertLinkInfo);
static bool sCheckLimits(ProgramData* pData,
const ST_BuiltInResources& resources,
const ANGLEShaderParser::ShaderLinkInfo& fragLinkInfo,
const ANGLEShaderParser::ShaderLinkInfo& vertLinkInfo);
static bool sCheckVariables(ProgramData* pData,
const ANGLEShaderParser::ShaderLinkInfo& a,
const ANGLEShaderParser::ShaderLinkInfo& b);
static void sInitializeUniformLocs(ProgramData* pData,
const std::vector<ST_ShaderVariable>& uniforms);
#endif
bool ProgramData::validateLink(ShaderParser* frag, ShaderParser* vert) {
bool res = true;
#ifdef USE_ANGLE_SHADER_PARSER
const ANGLEShaderParser::ShaderLinkInfo& fragLinkInfo =
frag->getShaderLinkInfo();
const ANGLEShaderParser::ShaderLinkInfo& vertLinkInfo =
vert->getShaderLinkInfo();
res = res && sCheckUndecl(this, fragLinkInfo, vertLinkInfo);
res = res && sCheckLimits(this, ANGLEShaderParser::kResources,
fragLinkInfo, vertLinkInfo);
res = res && sCheckVariables(this, fragLinkInfo, vertLinkInfo);
#endif
return res;
}
void ProgramData::setHostLinkStatus(GLint status) {
HostLinkStatus = (status == GL_FALSE) ? false : true;
}
void ProgramData::setLinkStatus(GLint status) {
LinkStatus = (status == GL_FALSE) ? false : true;
mUniNameToGuestLoc.clear();
mGuestLocToHostLoc.clear();
mGuestLocToHostLoc.add(-1, -1);
#if defined(TOLERATE_PROGRAM_LINK_ERROR) && TOLERATE_PROGRAM_LINK_ERROR == 1
status = 1;
#endif
if (status && HostLinkStatus) {
bool is310 = false;
#ifdef USE_ANGLE_SHADER_PARSER
std::vector<ST_ShaderVariable> allUniforms;
#endif
for (auto& s : attachedShaders) {
if (s.localName) {
assert(s.shader);
s.linkedSource = s.shader->getOriginalSrc();
#ifdef USE_ANGLE_SHADER_PARSER
s.linkInfo = s.shader->getShaderLinkInfo();
is310 = is310 || (s.linkInfo.esslVersion == 310);
for (const auto& var: s.linkInfo.uniforms) {
allUniforms.push_back(var);
}
#endif
}
}
if (is310 || isGles2Gles()) {
mUseDirectDriverUniformInfo = true;
} else {
#ifdef USE_ANGLE_SHADER_PARSER
sInitializeUniformLocs(this, allUniforms);
#endif
}
for (const auto &attribLoc : boundAttribLocs) {
// overwrite
linkedAttribLocs[attribLoc.first] = attribLoc.second;
}
} else {
for (auto& s : attachedShaders) {
s.linkedSource.clear();
}
}
}
bool ProgramData::getLinkStatus() const {
return LinkStatus;
}
static const char kDifferentPrecisionErr[] =
"specified with different precision in different shaders.";
static const char kDifferentTypeErr[] =
"specified with different type in different shaders.";
static const char kDifferentLayoutQualifierErr[] =
"specified with different layout qualifiers in different shaders.";
static const char kExceededMaxVertexAttribs[] =
"exceeded max vertex attribs.";
static const char kUsedUndeclaredErr[] =
"used, but not declared.";
static const char kUniformQualifier[] = "uniform";
static const char kVaryingQualifier[] = "varying";
static const char kUnknownQualifier[] = "[unknown qualifier]";
enum ValidationQualifier {
UNIFORM,
VARYING,
};
static const char* sQualifierString(ValidationQualifier q) {
switch (q) {
case ValidationQualifier::UNIFORM:
return kUniformQualifier;
case ValidationQualifier::VARYING:
return kVaryingQualifier;
}
return kUnknownQualifier;
}
#ifdef USE_ANGLE_SHADER_PARSER
static bool sVarCheck(ProgramData* pData,
ValidationQualifier qualifier,
const ST_ShaderVariable& a,
const ST_ShaderVariable& b) {
bool res = true;
if (qualifier == ValidationQualifier::UNIFORM &&
a.precision != b.precision) {
std::ostringstream err;
err << sQualifierString(qualifier) << " " << a.name << " ";
err << kDifferentPrecisionErr;
pData->appendValidationErrMsg(err);
res = false;
}
bool aIsStruct = a.fieldsCount > 0;
bool bIsStruct = b.fieldsCount > 0;
if (aIsStruct != bIsStruct ||
a.type != b.type) {
std::ostringstream err;
err << sQualifierString(qualifier) << " " << a.name << " ";
err << kDifferentTypeErr;
pData->appendValidationErrMsg(err);
res = false;
}
if (aIsStruct) {
for (unsigned int i = 0; i < a.fieldsCount; ++i) {
for (unsigned int j = 0; j < b.fieldsCount; ++j) {
if (strcmp(a.pFields[i].name, b.pFields[j].name)) continue;
res = res && sVarCheck(pData, qualifier, a.pFields[i], b.pFields[j]);
}
}
}
return res;
}
static bool sInterfaceBlockCheck(ProgramData* pData,
const ST_InterfaceBlock& a,
const ST_InterfaceBlock& b) {
bool res = true;
if (a.layout != b.layout ||
a.isRowMajorLayout != b.isRowMajorLayout) {
std::ostringstream err;
err << "interface block " << a.name << " ";
err << kDifferentLayoutQualifierErr;
pData->appendValidationErrMsg(err);
res = false;
}
if (a.fieldsCount != b.fieldsCount) {
std::ostringstream err;
err << "interface block " << a.name << " ";
err << kDifferentTypeErr;
pData->appendValidationErrMsg(err);
res = false;
}
for (unsigned int i = 0; i < a.fieldsCount; ++i) {
for (unsigned int j = 0; j < b.fieldsCount; ++j) {
const auto afield = a.pFields[i];
const auto bfield = b.pFields[j];
if (strcmp(afield.name, bfield.name)) continue;
res = res && sVarCheck(pData, ValidationQualifier::VARYING,
afield, bfield);
if (afield.isRowMajorLayout != bfield.isRowMajorLayout) {
std::ostringstream err;
err << "interface block field ";
err << a.name << "." << afield.name << " ";
err << kDifferentLayoutQualifierErr;
pData->appendValidationErrMsg(err);
res = false;
}
}
}
return res;
}
static bool sIsBuiltInShaderVariable(const ST_ShaderVariable& var) {
if (!var.name || strlen(var.name) < 4) return false;
const char* name = var.name;
return (name[0] == 'g' && name[1] == 'l' && name[2] == '_');
}
static bool sCheckUndecl(
ProgramData* pData,
const ANGLEShaderParser::ShaderLinkInfo& fragLinkInfo,
const ANGLEShaderParser::ShaderLinkInfo& vertLinkInfo) {
bool res = true;
for (const auto& felt : fragLinkInfo.varyings) {
if (sIsBuiltInShaderVariable(felt)) continue;
bool declaredInVertShader = false;
for (const auto& velt : vertLinkInfo.varyings) {
if (!strcmp(velt.name, felt.name)) {
declaredInVertShader = true;
break;
}
}
if (!declaredInVertShader && felt.staticUse) {
std::ostringstream err;
err << "varying " << felt.name << " ";
err << kUsedUndeclaredErr;
pData->appendValidationErrMsg(err);
res = false;
}
}
return res;
}
static bool sCheckLimits(
ProgramData* pData,
const ST_BuiltInResources& resources,
const ANGLEShaderParser::ShaderLinkInfo& fragShaderLinkInfo,
const ANGLEShaderParser::ShaderLinkInfo& vertShaderLinkInfo) {
bool res = true;
size_t maxAttribs = (size_t)resources.MaxVertexAttribs;
std::unordered_set<GLuint> explicitlyBound;
int numImplicitlyBound = 0;
for (const auto& elt : vertShaderLinkInfo.attributes) {
if (const auto loc = android::base::find(pData->boundAttribLocs, elt.name)) {
explicitlyBound.insert(*loc);
} else {
numImplicitlyBound++;
}
}
int numExplicitlyBound = explicitlyBound.size();
if ((int)maxAttribs - numExplicitlyBound - numImplicitlyBound < 0) {
std::ostringstream err;
err << kExceededMaxVertexAttribs;
err << " Wanted (from vertex shader): ";
err << numExplicitlyBound + numImplicitlyBound << " ";
err << " Limit: " << maxAttribs << " ";
pData->appendValidationErrMsg(err);
res = false;
}
return res;
}
static bool sCheckVariables(ProgramData* pData,
const ANGLEShaderParser::ShaderLinkInfo& a,
const ANGLEShaderParser::ShaderLinkInfo& b) {
bool res = true;
for (const auto& aelt : a.uniforms) {
for (const auto& belt : b.uniforms) {
if (strcmp(aelt.name, belt.name)) continue;
res = res && sVarCheck(pData, ValidationQualifier::UNIFORM, aelt, belt);
}
}
for (const auto& aelt : a.varyings) {
for (const auto& belt : b.varyings) {
if (strcmp(aelt.name, belt.name)) continue;
res = res && sVarCheck(pData, ValidationQualifier::VARYING, aelt, belt);
}
}
for (const auto& aelt : a.interfaceBlocks) {
for (const auto& belt : b.interfaceBlocks) {
if (strcmp(aelt.name, belt.name)) continue;
res = res && sInterfaceBlockCheck(pData, aelt, belt);
}
}
return res;
}
static void sRecursiveLocInitalize(ProgramData* pData, const std::string& keyBase, const ST_ShaderVariable& var) {
// fprintf(stderr, "%s: call. name: %s\n", __func__, var.name);
bool isArr = var.arraySizeCount > 0;
int baseSize = isArr ? var.pArraySizes[0] : 1;
bool isStruct = var.fieldsCount > 0;
if (isStruct) {
// fprintf(stderr, "%s: is struct\n", __func__);
if (isArr) {
// fprintf(stderr, "%s: is arr\n", __func__);
for (int k = 0; k < var.pArraySizes[0]; k++) {
for (uint32_t i = 0; i < var.fieldsCount; ++i) {
std::vector<char> keyBuf(keyBase.length() + strlen(var.pFields[i].name) + 20, 0);
snprintf(keyBuf.data(), keyBuf.size(), "%s[%d].%s", keyBase.c_str(), k, var.pFields[i].name);
sRecursiveLocInitalize(pData, std::string(keyBuf.data()), var.pFields[i]);
}
}
} else {
// fprintf(stderr, "%s: is plain struct\n", __func__);
for (uint32_t i = 0; i < var.fieldsCount; ++i) {
std::vector<char> keyBuf(keyBase.length() + strlen(var.pFields[i].name) + 20, 0);
snprintf(keyBuf.data(), keyBuf.size(), "%s.%s", keyBase.c_str(), var.pFields[i].name);
// fprintf(stderr, "%s: keyBuf: %s\n", __func__, keyBuf.data());
sRecursiveLocInitalize(pData, std::string(keyBuf.data()), var.pFields[i]);
}
}
} else {
// fprintf(stderr, "%s: is not struct\n", __func__);
for (int k = 0; k < baseSize; k++) {
if (k == 0) {
std::vector<char> keyBuf(keyBase.length() + 20, 0);
std::vector<char> keyBuf2(keyBase.length() + 20, 0);
snprintf(keyBuf.data(), keyBuf.size(), "%s", keyBase.c_str());
snprintf(keyBuf2.data(), keyBuf.size(), "%s[%d]", keyBase.c_str(), k);
// fprintf(stderr, "%s: initGuestUniformLocForKey. keyBuf2 %s\n", __func__, keyBuf2.data());
pData->initGuestUniformLocForKey(keyBuf.data(), keyBuf2.data());
} else {
std::vector<char> keyBuf(keyBase.length() + 20, 0);
snprintf(keyBuf.data(), keyBuf.size(), "%s[%d]", keyBase.c_str(), k);
// fprintf(stderr, "%s: initGuestUniformLocForKey. keyBu2 %s\n", __func__, keyBuf.data());
pData->initGuestUniformLocForKey(keyBuf.data());
}
}
}
}
static void sInitializeUniformLocs(ProgramData* pData,
const std::vector<ST_ShaderVariable>& uniforms) {
// fprintf(stderr, "%s: call\n", __func__);
// initialize in order of indices
std::vector<std::string> orderedUniforms;
GLint uniform_count;
GLint nameLength;
GLDispatch& gl = GLEScontext::dispatcher();
gl.glGetProgramiv(pData->getProgramName(), GL_ACTIVE_UNIFORM_MAX_LENGTH, &nameLength);
gl.glGetProgramiv(pData->getProgramName(), GL_ACTIVE_UNIFORMS, &uniform_count);
std::vector<char> name(nameLength, 0);
for (int i = 0; i < uniform_count; i++) {
GLint size;
GLenum type;
GLsizei length;
gl.glGetActiveUniform(pData->getProgramName(), i, nameLength, &length,
&size, &type, name.data());
// fprintf(stderr, "%s: host uniform: %s\n", __func__, name.data());
orderedUniforms.push_back(pData->getDetranslatedName(name.data()));
// fprintf(stderr, "%s: host uniform detranslated: %s\n", __func__, pData->getDetranslatedName(name.data()).str().c_str());
}
std::unordered_map<std::string, size_t> linkInfoUniformsByName;
size_t i = 0;
for (const auto& var : uniforms) {
linkInfoUniformsByName[var.name] = i;
i++;
}
for (const auto& str : orderedUniforms) {
// fprintf(stderr, "%s: do ordered uniforms\n", __func__);
if (linkInfoUniformsByName.find(str) != linkInfoUniformsByName.end()) {
sRecursiveLocInitalize(pData, str, uniforms[linkInfoUniformsByName[str]]);
} else {
// fprintf(stderr, "%s: found in link info uniforms\n", __func__);
}
}
for (const auto& var : uniforms) {
sRecursiveLocInitalize(pData, var.name, var);
}
}
#endif
void ProgramData::initGuestUniformLocForKey(const std::string& key) {
// fprintf(stderr, "%s: for %s\n", __func__, key.str().c_str());
if (mUniNameToGuestLoc.find(key) == mUniNameToGuestLoc.end()) {
mUniNameToGuestLoc[key] = mCurrUniformBaseLoc;
// Emplace host location beforehand to workaround Unreal bug
// BUG: 120548998
GLDispatch& dispatcher = GLEScontext::dispatcher();
std::string translatedName = getTranslatedName(key);
// fprintf(stderr, "%s: trname: %s\n", __func__, translatedName.c_str());
int hostLoc = dispatcher.glGetUniformLocation(ProgramName,
translatedName.c_str());
if (hostLoc != -1) {
mGuestLocToHostLoc.add(mCurrUniformBaseLoc, hostLoc);
}
mCurrUniformBaseLoc++;
}
}
void ProgramData::initGuestUniformLocForKey(const std::string& key, const std::string& key2) {
bool newUniform = false;
if (mUniNameToGuestLoc.find(key) == mUniNameToGuestLoc.end()) {
mUniNameToGuestLoc[key] = mCurrUniformBaseLoc;
newUniform = true;
}
if (mUniNameToGuestLoc.find(key2) == mUniNameToGuestLoc.end()) {
mUniNameToGuestLoc[key2] = mCurrUniformBaseLoc;
newUniform = true;
}
if (newUniform) {
// Emplace host location beforehand to workaround Unreal bug
// BUG: 120548998
GLDispatch& dispatcher = GLEScontext::dispatcher();
std::string translatedName = getTranslatedName(key);
int hostLoc = dispatcher.glGetUniformLocation(ProgramName,
translatedName.c_str());
if (hostLoc != -1) {
mGuestLocToHostLoc.add(mCurrUniformBaseLoc, hostLoc);
}
mCurrUniformBaseLoc++;
}
}
int ProgramData::getGuestUniformLocation(const char* uniName) {
GLDispatch& dispatcher = GLEScontext::dispatcher();
if (mUseUniformLocationVirtualization) {
if (mUseDirectDriverUniformInfo) {
int guestLoc;
const auto& activeLoc = mUniNameToGuestLoc.find(uniName);
// If using direct driver uniform info, don't overwrite any
// previously known guest location.
if (activeLoc != mUniNameToGuestLoc.end()) {
guestLoc = activeLoc->second;
} else {
guestLoc =
dispatcher.glGetUniformLocation(ProgramName, uniName);
if (guestLoc == -1) {
return -1;
} else {
mUniNameToGuestLoc[uniName] = guestLoc;
mGuestLocToHostLoc.add(guestLoc, guestLoc);
}
}
return guestLoc;
} else {
int guestLoc;
const auto& activeLoc = mUniNameToGuestLoc.find(uniName);
if (activeLoc != mUniNameToGuestLoc.end()) {
guestLoc = activeLoc->second;
} else {
guestLoc = -1;
}
std::string translatedName = getTranslatedName(uniName);
int hostLoc = dispatcher.glGetUniformLocation(ProgramName,
translatedName.c_str());
if (hostLoc == -1) {
return -1;
}
mGuestLocToHostLoc.add(guestLoc, hostLoc);
return guestLoc;
}
} else {
return dispatcher.glGetUniformLocation(
ProgramName, getTranslatedName(uniName).c_str());
}
}
int ProgramData::getHostUniformLocation(int guestLocation) {
if (mUseUniformLocationVirtualization) {
if (guestLocation == -1) return -1;
auto locPtr = mGuestLocToHostLoc.get_const(guestLocation);
if (!locPtr) return -2;
return *locPtr;
} else {
return guestLocation;
}
}