blob: 5a0cd7871228cb213bd663e03e1d1a27923562ae [file] [log] [blame]
// Copyright 2020 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.
#ifdef USE_ANGLE_SHADER_PARSER
#include "ANGLEShaderParser.h"
#include "ShaderTranslator.h"
#include "aemu/base/SharedLibrary.h"
#include "aemu/base/synchronization/Lock.h"
#include "host-common/logging.h"
#include <map>
#include <string>
#include <string.h>
#define GL_COMPUTE_SHADER 0x91B9
namespace ANGLEShaderParser {
ST_BuiltInResources kResources;
bool kInitialized = false;
bool sIsGles2Gles = false;
class LazyLoadedSTDispatch {
public:
LazyLoadedSTDispatch() {
memset(&mDispatch, 0, sizeof(STDispatch));
#ifdef __APPLE__
const char kLibName[] = "libshadertranslator.dylib";
#elif defined(_WIN32)
const char kLibName[] = "libshadertranslator.dll";
#else
const char kLibName[] = "libshadertranslator.so";
#endif
char error[256];
mLib = android::base::SharedLibrary::open(kLibName, error, sizeof(error));
if (!mLib) {
ERR("%s: Could not open shader translator library %s [%s]\n",
__func__, kLibName, error);
return;
}
mDispatch.initialize =
(STInitialize_t)mLib->findSymbol("STInitialize");
mDispatch.finalize =
(STFinalize_t)mLib->findSymbol("STFinalize");
mDispatch.generateResources =
(STGenerateResources_t)mLib->findSymbol("STGenerateResources");
mDispatch.compileAndResolve =
(STCompileAndResolve_t)mLib->findSymbol("STCompileAndResolve");
mDispatch.freeShaderResolveState =
(STFreeShaderResolveState_t)mLib->findSymbol("STFreeShaderResolveState");
mDispatch.copyVariable =
(STCopyVariable_t)mLib->findSymbol("STCopyVariable");
mDispatch.copyInterfaceBlock =
(STCopyInterfaceBlock_t)mLib->findSymbol("STCopyInterfaceBlock");
mDispatch.destroyVariable =
(STDestroyVariable_t)mLib->findSymbol("STDestroyVariable");
mDispatch.destroyInterfaceBlock =
(STDestroyInterfaceBlock_t)mLib->findSymbol("STDestroyInterfaceBlock");
mValid = dispatchValid();
if (!mValid) {
ERR("%s: error, shader translator dispatch not valid\n", __func__);
}
}
STDispatch* getDispatch() {
if (!mValid) return nullptr;
return &mDispatch;
}
private:
bool dispatchValid() {
return (nullptr != mDispatch.initialize) &&
(nullptr != mDispatch.finalize) &&
(nullptr != mDispatch.generateResources) &&
(nullptr != mDispatch.compileAndResolve) &&
(nullptr != mDispatch.copyVariable) &&
(nullptr != mDispatch.copyInterfaceBlock) &&
(nullptr != mDispatch.destroyVariable) &&
(nullptr != mDispatch.destroyInterfaceBlock);
}
android::base::SharedLibrary* mLib = nullptr;
bool mValid = false;
STDispatch mDispatch;
};
static STDispatch* getSTDispatch() {
static LazyLoadedSTDispatch* dispatch = new LazyLoadedSTDispatch;
return dispatch->getDispatch();
}
ShaderLinkInfo::ShaderLinkInfo() = default;
ShaderLinkInfo::ShaderLinkInfo(const ShaderLinkInfo& other) {
clear();
copyFromOther(other);
}
ShaderLinkInfo& ShaderLinkInfo::operator=(const ShaderLinkInfo& other) {
if (this != &other) {
ShaderLinkInfo tmp(other);
*this = std::move(tmp);
}
return *this;
}
ShaderLinkInfo::ShaderLinkInfo(ShaderLinkInfo&& other) {
*this = std::move(other);
}
ShaderLinkInfo& ShaderLinkInfo::operator=(ShaderLinkInfo&& other) {
esslVersion = other.esslVersion;
uniforms = std::move(other.uniforms);
varyings = std::move(other.varyings);
attributes = std::move(other.attributes);
outputVars = std::move(other.outputVars);
nameMap = std::move(other.nameMap);
nameMapReverse = std::move(other.nameMapReverse);
return *this;
}
ShaderLinkInfo::~ShaderLinkInfo() {
clear();
}
void ShaderLinkInfo::copyFromOther(const ShaderLinkInfo& other) {
esslVersion = other.esslVersion;
if (!sIsGles2Gles) {
auto dispatch = getSTDispatch();
for (const auto& var: other.uniforms) { uniforms.push_back(dispatch->copyVariable(&var)); }
for (const auto& var: other.varyings) { varyings.push_back(dispatch->copyVariable(&var)); }
for (const auto& var: other.attributes) { attributes.push_back(dispatch->copyVariable(&var)); }
for (const auto& var: other.outputVars) { outputVars.push_back(dispatch->copyVariable(&var)); }
for (const auto& var: other.interfaceBlocks) { interfaceBlocks.push_back(dispatch->copyInterfaceBlock(&var)); }
}
nameMap = other.nameMap;
nameMapReverse = other.nameMapReverse;
}
void ShaderLinkInfo::clear() {
if (!sIsGles2Gles) {
auto dispatch = getSTDispatch();
for (auto& var: uniforms) { dispatch->destroyVariable(&var); }
for (auto& var: varyings) { dispatch->destroyVariable(&var); }
for (auto& var: attributes) { dispatch->destroyVariable(&var); }
for (auto& var: outputVars) { dispatch->destroyVariable(&var); }
for (auto& var: interfaceBlocks) { dispatch->destroyInterfaceBlock(&var); }
}
uniforms.clear();
varyings.clear();
attributes.clear();
outputVars.clear();
interfaceBlocks.clear();
nameMap.clear();
nameMapReverse.clear();
}
struct ShaderSpecKey {
GLenum shaderType;
int esslVersion;
};
static ST_ShaderSpec sInputSpecForVersion(int esslVersion) {
switch (esslVersion) {
case 100:
return ST_GLES2_SPEC;
case 300:
return ST_GLES3_SPEC;
case 310:
return ST_GLES3_1_SPEC;
}
return ST_GLES3_1_SPEC;
}
static ST_ShaderOutput sOutputSpecForVersion(bool coreProfileHost, int esslVersion) {
switch (esslVersion) {
case 100:
if (coreProfileHost) {
return ST_GLSL_330_CORE_OUTPUT;
} else {
return ST_GLSL_COMPATIBILITY_OUTPUT;
}
case 300:
if (coreProfileHost) {
return ST_GLSL_330_CORE_OUTPUT;
} else {
return ST_GLSL_150_CORE_OUTPUT;
}
case 310:
return ST_GLSL_430_CORE_OUTPUT;
}
return ST_GLSL_430_CORE_OUTPUT;
}
struct ShaderSpecKeyCompare {
bool operator() (const ShaderSpecKey& a,
const ShaderSpecKey& b) const {
if (a.shaderType != b.shaderType)
return a.shaderType < b.shaderType;
if (a.esslVersion != b.esslVersion)
return a.esslVersion < b.esslVersion;
return false;
}
};
typedef std::map<ShaderSpecKey, ST_Handle, ShaderSpecKeyCompare> ShaderCompilerMap;
static ShaderCompilerMap* sCompilerMap() {
static ShaderCompilerMap* m = new ShaderCompilerMap;
return m;
}
static ST_Handle getShaderCompiler(bool coreProfileHost, ShaderSpecKey key) {
auto it = sCompilerMap()->find(key);
if (it == sCompilerMap()->end()) return (ST_Handle)nullptr;
return it->second;
}
android::base::Lock kCompilerLock;
void initializeResources(
BuiltinResourcesEditCallback callback) {
if (!sIsGles2Gles) {
getSTDispatch()->generateResources(&kResources);
}
callback(kResources);
}
bool globalInitialize(
bool isGles2Gles,
BuiltinResourcesEditCallback editCallback) {
sIsGles2Gles = isGles2Gles;
if (!sIsGles2Gles) {
getSTDispatch()->initialize();
}
initializeResources(editCallback);
kInitialized = true;
return true;
}
template <class T>
static std::vector<T> convertArrayToVecWithCopy(
unsigned int count, const T* pItems, T (*copyFunc)(const T*)) {
std::vector<T> res;
for (uint32_t i = 0; i < count; ++i) {
res.push_back(copyFunc(pItems + i));
}
return res;
}
static void getShaderLinkInfo(int esslVersion,
const ST_ShaderCompileResult* compileResult,
ShaderLinkInfo* linkInfo) {
linkInfo->esslVersion = esslVersion;
linkInfo->uniforms.clear();
linkInfo->varyings.clear();
linkInfo->attributes.clear();
linkInfo->outputVars.clear();
linkInfo->interfaceBlocks.clear();
for (uint32_t i = 0; i < compileResult->nameHashingMap->entryCount; ++i) {
linkInfo->nameMap[compileResult->nameHashingMap->ppUserNames[i]] =
compileResult->nameHashingMap->ppCompiledNames[i];
}
for (const auto& elt : linkInfo->nameMap) {
linkInfo->nameMapReverse[elt.second] = elt.first;
}
auto st = getSTDispatch();
auto stCopyVariable = st->copyVariable;
auto stCopyInterfaceBlock = st->copyInterfaceBlock;
linkInfo->uniforms = convertArrayToVecWithCopy(
compileResult->uniformsCount,
compileResult->pUniforms,
stCopyVariable);
std::vector<ST_ShaderVariable> inputVaryings =
convertArrayToVecWithCopy(
compileResult->inputVaryingsCount, compileResult->pInputVaryings,
stCopyVariable);
std::vector<ST_ShaderVariable> outputVaryings =
convertArrayToVecWithCopy(
compileResult->outputVaryingsCount, compileResult->pOutputVaryings,
stCopyVariable);
linkInfo->varyings.clear();
linkInfo->varyings.insert(
linkInfo->varyings.begin(),
inputVaryings.begin(),
inputVaryings.end());
linkInfo->varyings.insert(
linkInfo->varyings.begin(),
outputVaryings.begin(),
outputVaryings.end());
linkInfo->attributes =
convertArrayToVecWithCopy(
compileResult->allAttributesCount,
compileResult->pAllAttributes,
stCopyVariable);
linkInfo->outputVars =
convertArrayToVecWithCopy(
compileResult->activeOutputVariablesCount,
compileResult->pActiveOutputVariables,
stCopyVariable);
linkInfo->interfaceBlocks =
convertArrayToVecWithCopy(
compileResult->uniformBlocksCount,
compileResult->pUniformBlocks,
stCopyInterfaceBlock);
// todo: split to uniform and ssbo
}
static int detectShaderESSLVersion(const char* const* strings) {
// Just look at the first line of the first string for now
const char* pos = strings[0];
const char* linePos = strstr(pos, "\n");
const char* versionPos = strstr(pos, "#version");
if (!linePos || !versionPos) {
// default to ESSL 100
return 100;
}
const char* version_end = versionPos + strlen("#version");
int wantedESSLVersion;
sscanf(version_end, " %d", &wantedESSLVersion);
return wantedESSLVersion;
}
bool translate(bool hostUsesCoreProfile,
const char* src,
GLenum shaderType,
std::string* outInfolog,
std::string* outObjCode,
ShaderLinkInfo* outShaderLinkInfo) {
int esslVersion = detectShaderESSLVersion(&src);
// Leverage ARB_ES3_1_compatibility for ESSL 310 for now.
// Use translator after rest of dEQP-GLES31.functional is in a better state.
if (esslVersion == 310) {
// Don't try to get obj code just yet.
// At least on NVIDIA Quadro K2200 Linux (361.xx),
// ARB_ES3_1_compatibility seems to assume incorrectly
// that atomic_uint must catch a precision qualifier in ESSL 310.
std::string origSrc(src);
outShaderLinkInfo->esslVersion = esslVersion;
size_t versionStart = origSrc.find("#version");
size_t versionEnd = origSrc.find("\n", versionStart);
size_t extensionStart = origSrc.rfind("#extension");
size_t extensionEnd = origSrc.find("\n", extensionStart);
if (extensionStart == std::string::npos) {
std::string versionPart = origSrc.substr(versionStart, versionEnd - versionStart + 1);
std::string src2 =
versionPart + "precision highp atomic_uint;\n" +
origSrc.substr(versionEnd + 1, origSrc.size() - (versionEnd + 1));
*outObjCode = src2;
} else {
std::string uptoExtensionPart = origSrc.substr(0, extensionEnd + 1);
std::string src2 =
uptoExtensionPart + "precision highp atomic_uint;\n" +
origSrc.substr(extensionEnd + 1, origSrc.size() - (extensionEnd + 1));
*outObjCode = src2;
}
return true;
}
if (!kInitialized) {
return false;
}
// ANGLE may crash if multiple RenderThreads attempt to compile shaders
// at the same time.
android::base::AutoLock autolock(kCompilerLock);
ShaderSpecKey key;
key.shaderType = shaderType;
key.esslVersion = esslVersion;
ST_ShaderCompileInfo ci = {
(ST_Handle)getShaderCompiler(hostUsesCoreProfile, key),
shaderType,
sInputSpecForVersion(esslVersion),
sOutputSpecForVersion(hostUsesCoreProfile, esslVersion),
ST_OBJECT_CODE | ST_VARIABLES,
&kResources,
src,
};
ST_ShaderCompileResult* res = nullptr;
auto st = getSTDispatch();
st->compileAndResolve(&ci, &res);
sCompilerMap()->emplace(key, res->outputHandle);
*outInfolog = std::string(res->infoLog);
*outObjCode = std::string(res->translatedSource);
if (outShaderLinkInfo) getShaderLinkInfo(esslVersion, res, outShaderLinkInfo);
bool ret = res->compileStatus == 1;
st->freeShaderResolveState(res);
return ret;
}
} // namespace ANGLEShaderParser
#endif