blob: ad7ef86303992720716feb761ca516216277cf46 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/shader_translator.h"
#include <stddef.h>
#include <string.h>
#include <algorithm>
#include "base/at_exit.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/observer_list.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_version_info.h"
namespace gpu {
namespace gles2 {
namespace {
class ShaderTranslatorInitializer {
public:
ShaderTranslatorInitializer() {
TRACE_EVENT0("gpu", "ShInitialize");
CHECK(sh::Initialize());
}
~ShaderTranslatorInitializer() {
TRACE_EVENT0("gpu", "ShFinalize");
sh::Finalize();
}
};
base::LazyInstance<ShaderTranslatorInitializer>::DestructorAtExit
g_translator_initializer = LAZY_INSTANCE_INITIALIZER;
void GetAttributes(ShHandle compiler, AttributeMap* var_map) {
if (!var_map)
return;
var_map->clear();
const std::vector<sh::Attribute>* attribs = sh::GetAttributes(compiler);
if (attribs) {
for (size_t ii = 0; ii < attribs->size(); ++ii)
(*var_map)[(*attribs)[ii].mappedName] = (*attribs)[ii];
}
}
void GetUniforms(ShHandle compiler, UniformMap* var_map) {
if (!var_map)
return;
var_map->clear();
const std::vector<sh::Uniform>* uniforms = sh::GetUniforms(compiler);
if (uniforms) {
for (size_t ii = 0; ii < uniforms->size(); ++ii)
(*var_map)[(*uniforms)[ii].mappedName] = (*uniforms)[ii];
}
}
void GetVaryings(ShHandle compiler, VaryingMap* var_map) {
if (!var_map)
return;
var_map->clear();
const std::vector<sh::Varying>* varyings = sh::GetVaryings(compiler);
if (varyings) {
for (size_t ii = 0; ii < varyings->size(); ++ii)
(*var_map)[(*varyings)[ii].mappedName] = (*varyings)[ii];
}
}
void GetOutputVariables(ShHandle compiler, OutputVariableList* var_list) {
if (!var_list)
return;
*var_list = *sh::GetOutputVariables(compiler);
}
void GetInterfaceBlocks(ShHandle compiler, InterfaceBlockMap* var_map) {
if (!var_map)
return;
var_map->clear();
const std::vector<sh::InterfaceBlock>* interface_blocks =
sh::GetInterfaceBlocks(compiler);
if (interface_blocks) {
for (const auto& block : *interface_blocks) {
(*var_map)[block.mappedName] = block;
}
}
}
} // namespace
ShShaderOutput ShaderTranslator::GetShaderOutputLanguageForContext(
const gl::GLVersionInfo& version_info) {
if (version_info.is_es) {
return SH_ESSL_OUTPUT;
}
// Determine the GLSL version based on OpenGL specification.
unsigned context_version =
version_info.major_version * 100 + version_info.minor_version * 10;
if (context_version >= 450) {
// OpenGL specs from 4.2 on specify that the core profile is "also
// guaranteed to support all previous versions of the OpenGL Shading
// Language back to version 1.40". For simplicity, we assume future
// specs do not unspecify this. If they did, they could unspecify
// glGetStringi(GL_SHADING_LANGUAGE_VERSION, k), too.
// Since current context >= 4.5, use GLSL 4.50 core.
return SH_GLSL_450_CORE_OUTPUT;
} else if (context_version == 440) {
return SH_GLSL_440_CORE_OUTPUT;
} else if (context_version == 430) {
return SH_GLSL_430_CORE_OUTPUT;
} else if (context_version == 420) {
return SH_GLSL_420_CORE_OUTPUT;
} else if (context_version == 410) {
return SH_GLSL_410_CORE_OUTPUT;
} else if (context_version == 400) {
return SH_GLSL_400_CORE_OUTPUT;
} else if (context_version == 330) {
return SH_GLSL_330_CORE_OUTPUT;
} else if (context_version == 320) {
return SH_GLSL_150_CORE_OUTPUT;
}
// Before OpenGL 3.2 we use the compatibility profile. Shading
// language version 130 restricted how sampler arrays can be indexed
// in loops, which causes problems like crbug.com/550487 .
//
// Also for any future specs that might be introduced between OpenGL
// 3.3 and OpenGL 4.0, at the time of writing, we use the
// compatibility profile.
return SH_GLSL_COMPATIBILITY_OUTPUT;
}
ShaderTranslator::DestructionObserver::DestructionObserver() = default;
ShaderTranslator::DestructionObserver::~DestructionObserver() = default;
ShaderTranslator::ShaderTranslator() : compiler_(nullptr), compile_options_{} {}
bool ShaderTranslator::Init(GLenum shader_type,
ShShaderSpec shader_spec,
const ShBuiltInResources* resources,
ShShaderOutput shader_output_language,
const ShCompileOptions& driver_bug_workarounds,
bool gl_shader_interm_output) {
// Make sure Init is called only once.
DCHECK(compiler_ == nullptr);
DCHECK(shader_type == GL_FRAGMENT_SHADER || shader_type == GL_VERTEX_SHADER);
DCHECK(shader_spec == SH_GLES2_SPEC || shader_spec == SH_WEBGL_SPEC ||
shader_spec == SH_GLES3_SPEC || shader_spec == SH_WEBGL2_SPEC);
DCHECK(resources != nullptr);
g_translator_initializer.Get();
{
TRACE_EVENT0("gpu", "ShConstructCompiler");
compiler_ = sh::ConstructCompiler(shader_type, shader_spec,
shader_output_language, resources);
}
compile_options_ = driver_bug_workarounds;
compile_options_.objectCode = true;
compile_options_.variables = true;
compile_options_.enforcePackingRestrictions = true;
compile_options_.limitExpressionComplexity = true;
compile_options_.limitCallStackDepth = true;
compile_options_.clampIndirectArrayBounds = true;
compile_options_.emulateGLDrawID = true;
compile_options_.emulateGLBaseVertexBaseInstance = true;
std::string compile_options_string =
"objectCode:variables:enforcePackingRestrictions:"
"limitExpressionComplexity:limitCallStackDepth:clampIndirectArrayBounds:"
"emulateGLDrawID:emulateGLBaseVertexBaseInstance";
if (gl_shader_interm_output) {
compile_options_.intermediateTree = true;
compile_options_string += ":intermediateTree";
}
switch (shader_spec) {
case SH_WEBGL_SPEC:
case SH_WEBGL2_SPEC:
compile_options_.initOutputVariables = true;
break;
default:
break;
}
// Build the options string for additional features that may be set by the
// caller. Note that this code is used by the validating command decoder,
// which is deprecated. No new features are expected to be enabled, neither
// is it expected for there to be new users of this code.
if (compile_options_.initOutputVariables)
compile_options_string += ":initOutputVariables";
if (compile_options_.initGLPosition)
compile_options_string += ":initGLPosition";
if (compile_options_.unfoldShortCircuit)
compile_options_string += ":unfoldShortCircuit";
if (compile_options_.scalarizeVecAndMatConstructorArgs)
compile_options_string += ":scalarizeVecAndMatConstructorArgs";
if (compile_options_.regenerateStructNames)
compile_options_string += ":regenerateStructNames";
if (compile_options_.emulateAbsIntFunction)
compile_options_string += ":emulateAbsIntFunction";
if (compile_options_.rewriteTexelFetchOffsetToTexelFetch)
compile_options_string += ":rewriteTexelFetchOffsetToTexelFetch";
if (compile_options_.addAndTrueToLoopCondition)
compile_options_string += ":addAndTrueToLoopCondition";
if (compile_options_.rewriteDoWhileLoops)
compile_options_string += ":rewriteDoWhileLoops";
if (compile_options_.emulateIsnanFloatFunction)
compile_options_string += ":emulateIsnanFloatFunction";
if (compile_options_.useUnusedStandardSharedBlocks)
compile_options_string += ":useUnusedStandardSharedBlocks";
if (compile_options_.removeInvariantAndCentroidForESSL3)
compile_options_string += ":removeInvariantAndCentroidForESSL3";
if (compile_options_.rewriteFloatUnaryMinusOperator)
compile_options_string += ":rewriteFloatUnaryMinusOperator";
if (compile_options_.dontUseLoopsToInitializeVariables)
compile_options_string += ":dontUseLoopsToInitializeVariables";
if (compile_options_.removeDynamicIndexingOfSwizzledVector)
compile_options_string += ":removeDynamicIndexingOfSwizzledVector";
if (compile_options_.initializeUninitializedLocals)
compile_options_string += ":initializeUninitializedLocals";
if (compiler_) {
options_affecting_compilation_ =
base::MakeRefCounted<OptionsAffectingCompilationString>(
":CompileOptions:" + compile_options_string +
sh::GetBuiltInResourcesString(compiler_));
}
return compiler_ != nullptr;
}
const ShCompileOptions& ShaderTranslator::GetCompileOptions() const {
return compile_options_;
}
bool ShaderTranslator::Translate(
const std::string& shader_source,
std::string* info_log,
std::string* translated_source,
int* shader_version,
AttributeMap* attrib_map,
UniformMap* uniform_map,
VaryingMap* varying_map,
InterfaceBlockMap* interface_block_map,
OutputVariableList* output_variable_list) const {
// Make sure this instance is initialized.
DCHECK(compiler_ != nullptr);
bool success = false;
{
TRACE_EVENT0("gpu", "ShCompile");
const char* const shader_strings[] = { shader_source.c_str() };
success = sh::Compile(compiler_, shader_strings, 1, GetCompileOptions());
}
if (success) {
// Get translated shader.
if (translated_source) {
*translated_source = sh::GetObjectCode(compiler_);
}
// Get shader version.
*shader_version = sh::GetShaderVersion(compiler_);
// Get info for attribs, uniforms, varyings and output variables.
GetAttributes(compiler_, attrib_map);
GetUniforms(compiler_, uniform_map);
GetVaryings(compiler_, varying_map);
GetInterfaceBlocks(compiler_, interface_block_map);
GetOutputVariables(compiler_, output_variable_list);
}
// Get info log.
if (info_log) {
*info_log = sh::GetInfoLog(compiler_);
}
// We don't need results in the compiler anymore.
sh::ClearResults(compiler_);
return success;
}
OptionsAffectingCompilationString*
ShaderTranslator::GetStringForOptionsThatWouldAffectCompilation() const {
return options_affecting_compilation_.get();
}
void ShaderTranslator::AddDestructionObserver(
DestructionObserver* observer) {
destruction_observers_.AddObserver(observer);
}
void ShaderTranslator::RemoveDestructionObserver(
DestructionObserver* observer) {
destruction_observers_.RemoveObserver(observer);
}
ShaderTranslator::~ShaderTranslator() {
for (auto& observer : destruction_observers_)
observer.OnDestruct(this);
if (compiler_ != nullptr)
sh::Destruct(compiler_);
}
} // namespace gles2
} // namespace gpu