Files
filament/libs/filamat/src/GLSLPostProcessor.cpp
T
2018-11-20 12:30:26 -08:00

364 lines
13 KiB
C++

/*
* Copyright (C) 2017 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 "GLSLPostProcessor.h"
#include <iostream>
#include <sstream>
#include <vector>
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <localintermediate.h>
#include <spirv_glsl.hpp>
#include "sca/builtinResource.h"
#include "filamat/sca/GLSLTools.h"
using namespace glslang;
using namespace spirv_cross;
using namespace spvtools;
namespace filamat {
GLSLPostProcessor::GLSLPostProcessor(MaterialBuilder::Optimization optimization, bool printShaders)
: mOptimization(optimization), mPrintShaders(printShaders) {
}
GLSLPostProcessor::~GLSLPostProcessor() {
}
static uint32_t shaderVersionFromModel(filament::driver::ShaderModel model) {
switch (model) {
case filament::driver::ShaderModel::UNKNOWN:
case filament::driver::ShaderModel::GL_ES_30:
return 300;
case filament::driver::ShaderModel::GL_CORE_41:
return 410;
}
}
static void errorHandler(const std::string& str) {
std::cerr << str << std::endl;
}
static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
const char* levelString = nullptr;
switch (level) {
case SPV_MSG_FATAL:
levelString = "FATAL";
break;
case SPV_MSG_INTERNAL_ERROR:
levelString = "INTERNAL ERROR";
break;
case SPV_MSG_ERROR:
levelString = "ERROR";
break;
case SPV_MSG_WARNING:
levelString = "WARNING";
break;
case SPV_MSG_INFO:
levelString = "INFO";
break;
case SPV_MSG_DEBUG:
levelString = "DEBUG";
break;
}
std::ostringstream oss;
oss << levelString << ": ";
if (source) oss << source << ":";
oss << position.line << ":" << position.column << ":";
oss << position.index << ": ";
if (message) oss << message;
return oss.str();
}
/**
* Shrinks the specified string and returns a new string as the result.
* To shrink the string, this method performs the following transforms:
* - Remove leading white spaces at the beginning of each line
* - Remove empty lines
*/
static std::string shrinkString(const std::string& s) {
size_t cur = 0;
std::string r;
r.reserve(s.length());
while (cur < s.length()) {
size_t pos = cur;
size_t len = 0;
while (s[cur] != '\n') {
cur++;
len++;
}
size_t newPos = s.find_first_not_of(" \t", pos);
if (newPos == std::string::npos) newPos = pos;
r.append(s, newPos, len - (newPos - pos));
r += '\n';
while (s[cur] == '\n') {
cur++;
}
}
return r;
}
bool GLSLPostProcessor::process(const std::string& inputShader,
filament::driver::ShaderType shaderType, filament::driver::ShaderModel shaderModel,
std::string* outputGlsl, SpirvBlob* outputSpirv) {
// If TargetApi is Vulkan, then we need post-processing even if there's no optimization.
using TargetApi = MaterialBuilder::TargetApi;
const TargetApi targetApi = outputSpirv ? TargetApi::VULKAN : TargetApi::OPENGL;
if (targetApi == TargetApi::OPENGL && mOptimization == MaterialBuilder::Optimization::NONE) {
*outputGlsl = inputShader;
if (mPrintShaders) {
std::cout << *outputGlsl << std::endl;
}
return true;
}
mGlslOutput = outputGlsl;
mSpirvOutput = outputSpirv;
if (shaderType == filament::driver::VERTEX) {
mShLang = EShLangVertex;
} else {
mShLang = EShLangFragment;
}
TProgram program;
TShader tShader(mShLang);
// The cleaner must be declared after the TShader to prevent ASAN failures.
GLSLangCleaner cleaner;
const char* shaderCString = inputShader.c_str();
tShader.setStrings(&shaderCString, 1);
mLangVersion = GLSLTools::glslangVersionFromShaderModel(shaderModel);
GLSLTools::prepareShaderParser(tShader, mShLang, mLangVersion, mOptimization);
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi);
bool ok = tShader.parse(&DefaultTBuiltInResource, mLangVersion, false, msg);
if (!ok) {
std::cerr << tShader.getInfoLog() << std::endl;
return false;
}
program.addShader(&tShader);
// Even though we only have a single shader stage, linking is still necessary to finalize
// SPIR-V types
bool linkOk = program.link(msg);
if (!linkOk) {
std::cerr << tShader.getInfoLog() << std::endl;
return false;
}
switch (mOptimization) {
case MaterialBuilder::Optimization::NONE:
if (mSpirvOutput) {
GlslangToSpv(*program.getIntermediate(mShLang), *mSpirvOutput);
} else {
std::cerr << "GLSL post-processor invoked with optimization level NONE"
<< std::endl;
}
break;
case MaterialBuilder::Optimization::PREPROCESSOR:
preprocessOptimization(tShader, shaderModel);
break;
case MaterialBuilder::Optimization::SIZE:
case MaterialBuilder::Optimization::PERFORMANCE:
fullOptimization(tShader, shaderModel);
break;
}
if (mGlslOutput) {
*mGlslOutput = shrinkString(*mGlslOutput);
if (mPrintShaders) {
std::cout << *mGlslOutput << std::endl;
}
}
return true;
}
void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
const filament::driver::ShaderModel shaderModel) const {
using TargetApi = MaterialBuilder::TargetApi;
std::string glsl;
TShader::ForbidIncluder forbidIncluder;
int version = GLSLTools::glslangVersionFromShaderModel(shaderModel);
const TargetApi targetApi = mSpirvOutput ? TargetApi::VULKAN : TargetApi::OPENGL;
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi);
bool ok = tShader.preprocess(&DefaultTBuiltInResource, version, ENoProfile, false, false,
msg, &glsl, forbidIncluder);
if (!ok) {
std::cerr << tShader.getInfoLog() << std::endl;
}
if (mSpirvOutput) {
TProgram program;
TShader spirvShader(mShLang);
const char* shaderCString = glsl.c_str();
spirvShader.setStrings(&shaderCString, 1);
GLSLTools::prepareShaderParser(spirvShader, mShLang, mLangVersion, mOptimization);
ok = spirvShader.parse(&DefaultTBuiltInResource, mLangVersion, false, msg);
program.addShader(&spirvShader);
// Even though we only have a single shader stage, linking is still necessary to finalize
// SPIR-V types
bool linkOk = program.link(msg);
if (!ok || !linkOk) {
std::cerr << spirvShader.getInfoLog() << std::endl;
} else {
GlslangToSpv(*program.getIntermediate(mShLang), *mSpirvOutput);
}
}
if (mGlslOutput) {
*mGlslOutput = glsl;
}
}
void GLSLPostProcessor::fullOptimization(const TShader& tShader,
const filament::driver::ShaderModel shaderModel) const {
SpirvBlob spirv;
// Compile GLSL to to SPIR-V
GlslangToSpv(*tShader.getIntermediate(), spirv);
// Run the SPIR-V optimizer
Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
optimizer.SetMessageConsumer([](spv_message_level_t level,
const char* source, const spv_position_t& position, const char* message) {
std::cerr << stringifySpvOptimizerMessage(level, source, position, message) << std::endl;
});
if (mOptimization == MaterialBuilder::Optimization::SIZE) {
registerSizePasses(optimizer);
} else if (mOptimization == MaterialBuilder::Optimization::PERFORMANCE) {
registerPerformancePasses(optimizer);
}
if (!optimizer.Run(spirv.data(), spirv.size(), &spirv)) {
std::cerr << "SPIR-V optimizer pass failed" << std::endl;
return;
}
// Remove dead module-level objects: functions, types, vars
spv::spirvbin_t remapper(0);
remapper.registerErrorHandler(errorHandler);
remapper.remap(spirv, spv::spirvbin_base_t::DCE_ALL);
if (mSpirvOutput) {
*mSpirvOutput = spirv;
}
// Transpile back to GLSL
if (mGlslOutput) {
CompilerGLSL::Options glslOptions;
glslOptions.es = shaderModel == filament::driver::ShaderModel::GL_ES_30;
glslOptions.version = shaderVersionFromModel(shaderModel);
glslOptions.enable_420pack_extension = glslOptions.version >= 420;
glslOptions.fragment.default_float_precision = glslOptions.es ?
CompilerGLSL::Options::Precision::Mediump : CompilerGLSL::Options::Precision::Highp;
glslOptions.fragment.default_int_precision = glslOptions.es ?
CompilerGLSL::Options::Precision::Mediump : CompilerGLSL::Options::Precision::Highp;
CompilerGLSL glslCompiler(move(spirv));
glslCompiler.set_common_options(glslOptions);
*mGlslOutput = glslCompiler.compile();
}
}
void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer) const {
optimizer
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreatePrivateToLocalPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateScalarReplacementPass())
.RegisterPass(CreateLocalAccessChainConvertPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCombineAccessChainsPass())
.RegisterPass(CreateSimplificationPass())
.RegisterPass(CreateVectorDCEPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateSimplificationPass())
.RegisterPass(CreateIfConversionPass())
.RegisterPass(CreateCopyPropagateArraysPass())
.RegisterPass(CreateReduceLoadSizePass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateSimplificationPass());
}
void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer) const {
optimizer
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreatePrivateToLocalPass())
.RegisterPass(CreateScalarReplacementPass())
.RegisterPass(CreateLocalAccessChainConvertPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateSimplificationPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateIfConversionPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateSimplificationPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCFGCleanupPass())
.RegisterPass(CreateAggressiveDCEPass());
}
} // namespace filamat