/* * 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 #include #include 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 &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 ProgramData::collectUniformInfo() const { GLint uniform_count = 0; GLint nameLength = 0; std::unordered_map 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 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 collectUniformBlockInfo(GLuint pname) { GLint uniformBlockCount = 0; std::unordered_map 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 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 transformFeedbacks(transformFeedbackCount); std::unique_ptr 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& 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& uniform) { stream->putBe32(uniform.first); uniform.second.onSave(stream); }; auto saveUniformBlock = [](android::base::Stream* stream, const std::pair& uniformBlock) { stream->putBe32(uniformBlock.first); stream->putBe32(uniformBlock.second); }; auto saveTransformFeedbacks = [](android::base::Stream* stream, const std::vector& 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 uniformsOnSave = collectUniformInfo(); std::unordered_map uniformBlocks; std::vector 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& 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 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& 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 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 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 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 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 keyBuf(keyBase.length() + 20, 0); std::vector 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 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& uniforms) { // fprintf(stderr, "%s: call\n", __func__); // initialize in order of indices std::vector 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 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 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; } }