blob: 6ee3f56d76a17de0d2f68e736e9fd92c910fd9cd [file]
//
// Copyright 2002 The ANGLE Project 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 "compiler/translator/glsl/TranslatorGLSL.h"
#include "angle_gl.h"
#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/glsl/BuiltInFunctionEmulatorGLSL.h"
#include "compiler/translator/glsl/ExtensionGLSL.h"
#include "compiler/translator/glsl/OutputGLSL.h"
#include "compiler/translator/glsl/VersionGLSL.h"
#include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
#include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h"
#include "compiler/translator/tree_ops/PreTransformTextureCubeGradDerivatives.h"
#include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
#include "compiler/translator/tree_ops/RemoveInvariantDeclaration.h"
#include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h"
#include "compiler/translator/tree_ops/glsl/ExpandFragmentOutputsToVec4.h"
#include "compiler/translator/tree_ops/glsl/apple/RewriteRowMajorMatrices.h"
namespace sh
{
TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
: TCompiler(type, spec, output)
{}
bool TranslatorGLSL::translate(TIntermBlock *root,
const ShCompileOptions &compileOptions,
PerformanceDiagnostics * /*perfDiagnostics*/)
{
ASSERT(sh::IsGLSL150OrNewer(getOutputType()));
TInfoSinkBase &sink = getInfoSink().obj;
// Write GLSL version.
writeVersion(root);
// Write extension behaviour as needed
writeExtensionBehavior(root, compileOptions);
// Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens.
WritePragma(sink, compileOptions, getPragma());
// If flattening the global invariant pragma, write invariant declarations for built-in
// variables. It should be harmless to do this twice in the case that the shader also explicitly
// did this. However, it's important to emit invariant qualifiers only for those built-in
// variables that are actually used, to avoid affecting the behavior of the shader.
const bool removeInvariant =
RemoveInvariant(getShaderType(), getShaderVersion(), getOutputType(), compileOptions);
if (removeInvariant)
{
if (!RemoveInvariantDeclaration(this, root))
{
return false;
}
}
else if (compileOptions.flattenPragmaSTDGLInvariantAll && getPragma().stdgl.invariantAll)
{
switch (getShaderType())
{
case GL_VERTEX_SHADER:
sink << "invariant gl_Position;\n";
// gl_PointSize should be declared invariant in both ESSL 1.00 and 3.00 fragment
// shaders if it's statically referenced.
conditionallyOutputInvariantDeclaration("gl_PointSize");
break;
case GL_FRAGMENT_SHADER:
// The preprocessor will reject this pragma if it's used in ESSL 3.00 fragment
// shaders, so we can use simple logic to determine whether to declare these
// variables invariant.
conditionallyOutputInvariantDeclaration("gl_FragCoord");
conditionallyOutputInvariantDeclaration("gl_PointCoord");
break;
default:
// Currently not reached, but leave this in for future expansion.
ASSERT(false);
break;
}
}
if (getShaderVersion() >= 300 ||
IsExtensionEnabled(getExtensionBehavior(), TExtension::EXT_shader_texture_lod))
{
if (compileOptions.preTransformTextureCubeGradDerivatives)
{
if (!sh::PreTransformTextureCubeGradDerivatives(this, root, &getSymbolTable(),
getShaderVersion()))
{
return false;
}
}
}
if (!compileOptions.useIR)
{
if (!sh::AddDefaultReturnStatements(this, root))
{
return false;
}
// anglebug.com/42265954: The ESSL spec has a bug with images as function arguments. The
// recommended workaround is to inline functions that accept image arguments.
if (getShaderVersion() >= 310 &&
!MonomorphizeUnsupportedFunctions(
this, root, &getSymbolTable(),
UnsupportedFunctionArgsBitSet{UnsupportedFunctionArgs::Image}))
{
return false;
}
}
if (compileOptions.rewriteTexelFetchOffsetToTexelFetch)
{
if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion()))
{
return false;
}
}
if (compileOptions.rewriteRowMajorMatrices && getShaderVersion() >= 300)
{
if (!RewriteRowMajorMatrices(this, root, &getSymbolTable()))
{
return false;
}
}
if (compileOptions.removeDynamicIndexingOfSwizzledVector)
{
if (!sh::RemoveDynamicIndexingOfSwizzledVector(this, root, &getSymbolTable(), nullptr))
{
return false;
}
}
if (compileOptions.expandFragmentOutputsToVec4)
{
if (!ExpandFragmentOutputsToVec4(this, root, &getSymbolTable()))
{
return false;
}
}
// Write emulated built-in functions if needed.
BuiltInFunctionEmulator builtInFunctionEmulator;
if (compileOptions.emulateAbsIntFunction)
{
InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(&builtInFunctionEmulator, getShaderType());
}
if (compileOptions.emulateAtan2FloatFunction)
{
InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(&builtInFunctionEmulator);
}
int targetGLSLVersion = ShaderOutputTypeToGLSLVersion(getOutputType());
InitBuiltInFunctionEmulatorForGLSLMissingFunctions(&builtInFunctionEmulator, getShaderType(),
targetGLSLVersion);
builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
if (!builtInFunctionEmulator.isOutputEmpty())
{
sink << "// BEGIN: Generated code for built-in function emulation\n\n";
sink << "#define emu_precision\n\n";
builtInFunctionEmulator.outputEmulatedFunctions(sink);
sink << "// END: Generated code for built-in function emulation\n\n";
}
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
if (getShaderType() == GL_FRAGMENT_SHADER)
{
const bool mayHaveESSL1SecondaryOutputs =
IsExtensionEnabled(getExtensionBehavior(), TExtension::EXT_blend_func_extended) &&
getShaderVersion() == 100;
bool hasGLFragColor = false;
bool hasGLFragData = false;
bool hasGLSecondaryFragColor = false;
bool hasGLSecondaryFragData = false;
for (const auto &outputVar : mOutputVariables)
{
if (outputVar.name == "gl_FragColor")
{
ASSERT(!hasGLFragColor);
hasGLFragColor = true;
continue;
}
else if (outputVar.name == "gl_FragData")
{
ASSERT(!hasGLFragData);
hasGLFragData = true;
continue;
}
if (mayHaveESSL1SecondaryOutputs)
{
if (outputVar.name == "gl_SecondaryFragColorEXT")
{
ASSERT(!hasGLSecondaryFragColor);
hasGLSecondaryFragColor = true;
continue;
}
else if (outputVar.name == "gl_SecondaryFragDataEXT")
{
ASSERT(!hasGLSecondaryFragData);
hasGLSecondaryFragData = true;
continue;
}
}
}
ASSERT(!((hasGLFragColor || hasGLSecondaryFragColor) &&
(hasGLFragData || hasGLSecondaryFragData)));
if (hasGLFragColor)
{
sink << "out vec4 webgl_FragColor;\n";
}
if (hasGLFragData)
{
sink << "out vec4 webgl_FragData["
<< (hasGLSecondaryFragData ? getResources().MaxDualSourceDrawBuffers
: getResources().MaxDrawBuffers)
<< "];\n";
}
if (hasGLSecondaryFragColor)
{
sink << "out vec4 webgl_SecondaryFragColor;\n";
}
if (hasGLSecondaryFragData)
{
sink << "out vec4 webgl_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers
<< "];\n";
}
EmitEarlyFragmentTestsGLSL(*this, sink);
WriteFragmentShaderLayoutQualifiers(sink, getAdvancedBlendEquations());
}
if (getShaderType() == GL_COMPUTE_SHADER)
{
EmitWorkGroupSizeGLSL(*this, sink);
}
if (getShaderType() == GL_GEOMETRY_SHADER_EXT)
{
WriteGeometryShaderLayoutQualifiers(
sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
}
// Write translated shader.
TOutputGLSL outputGLSL(this, sink, compileOptions);
root->traverse(&outputGLSL);
return true;
}
bool TranslatorGLSL::shouldFlattenPragmaStdglInvariantAll()
{
// Required when outputting to any GLSL version greater than 1.20, but since ANGLE always
// translates to at least version 1.50, return true.
return true;
}
void TranslatorGLSL::writeVersion(TIntermNode *root)
{
int version = ShaderOutputTypeToGLSLVersion(getOutputType());
ASSERT(version >= GLSL_VERSION_150);
TInfoSinkBase &sink = getInfoSink().obj;
sink << "#version " << version << "\n";
}
void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root,
const ShCompileOptions &compileOptions)
{
bool usesTextureCubeMapArray = false;
bool usesTextureBuffer = false;
bool usesGPUShader5 = false;
TInfoSinkBase &sink = getInfoSink().obj;
const TExtensionBehavior &extBehavior = getExtensionBehavior();
for (const auto &iter : extBehavior)
{
if (iter.second == EBhUndefined)
{
continue;
}
const bool isMultiview =
(iter.first == TExtension::OVR_multiview) || (iter.first == TExtension::OVR_multiview2);
if (isMultiview)
{
// Only either OVR_multiview or OVR_multiview2 should be emitted.
if ((iter.first != TExtension::OVR_multiview) ||
!IsExtensionEnabled(extBehavior, TExtension::OVR_multiview2))
{
EmitMultiviewGLSL(*this, compileOptions, iter.first, iter.second, sink);
}
}
// Support ANGLE_texture_multisample extension on GLSL300
if (getShaderVersion() >= 300 && iter.first == TExtension::ANGLE_texture_multisample &&
getOutputType() < SH_GLSL_330_CORE_OUTPUT)
{
sink << "#extension GL_ARB_texture_multisample : " << GetBehaviorString(iter.second)
<< "\n";
}
if (getOutputType() != SH_ESSL_OUTPUT &&
(iter.first == TExtension::EXT_clip_cull_distance ||
(iter.first == TExtension::ANGLE_clip_cull_distance &&
getResources().MaxCullDistances > 0)) &&
getOutputType() < SH_GLSL_450_CORE_OUTPUT)
{
sink << "#extension GL_ARB_cull_distance : " << GetBehaviorString(iter.second) << "\n";
}
if (getOutputType() != SH_ESSL_OUTPUT && iter.first == TExtension::EXT_conservative_depth &&
getOutputType() < SH_GLSL_420_CORE_OUTPUT)
{
sink << "#extension GL_ARB_conservative_depth : " << GetBehaviorString(iter.second)
<< "\n";
}
if (iter.first == TExtension::EXT_texture_shadow_lod)
{
sink << "#extension " << GetExtensionNameString(iter.first) << " : "
<< GetBehaviorString(iter.second) << "\n";
}
if (iter.first == TExtension::KHR_blend_equation_advanced)
{
sink << "#ifdef GL_KHR_blend_equation_advanced\n"
<< "#extension GL_KHR_blend_equation_advanced : " << GetBehaviorString(iter.second)
<< "\n"
<< "#elif defined GL_NV_blend_equation_advanced\n"
<< "#extension GL_NV_blend_equation_advanced : " << GetBehaviorString(iter.second)
<< "\n";
if (iter.second == EBhRequire)
{
sink << "#else\n" << "#error \"No advanced blend equation extensions available.\n";
}
sink << "#endif\n";
}
if ((iter.first == TExtension::OES_texture_cube_map_array ||
iter.first == TExtension::EXT_texture_cube_map_array) &&
(iter.second == EBhRequire || iter.second == EBhEnable))
{
usesTextureCubeMapArray = true;
}
if ((iter.first == TExtension::OES_texture_buffer ||
iter.first == TExtension::EXT_texture_buffer) &&
(iter.second == EBhRequire || iter.second == EBhEnable))
{
usesTextureBuffer = true;
}
if ((iter.first == TExtension::OES_gpu_shader5 ||
iter.first == TExtension::EXT_gpu_shader5) &&
(iter.second == EBhRequire || iter.second == EBhEnable))
{
usesGPUShader5 = true;
}
}
// GLSL ES 3 explicit location qualifiers need to use an extension before GLSL 330
if (getShaderVersion() >= 300 && getOutputType() < SH_GLSL_330_CORE_OUTPUT &&
getShaderType() != GL_COMPUTE_SHADER)
{
sink << "#extension GL_ARB_explicit_attrib_location : require\n";
}
// Need to enable gpu_shader5 to have index constant sampler array indexing
if (usesGPUShader5)
{
if (getOutputType() >= SH_GLSL_150_CORE_OUTPUT &&
getOutputType() < SH_GLSL_400_CORE_OUTPUT && getShaderVersion() == 100)
{
// Don't use "require" on to avoid breaking WebGL 1 on drivers that silently
// support index constant sampler array indexing, but don't have the extension or
// on drivers that don't have the extension at all as it would break WebGL 1 for
// some users.
sink << "#extension GL_ARB_gpu_shader5 : enable\n";
sink << "#extension GL_OES_gpu_shader5 : enable\n";
sink << "#extension GL_EXT_gpu_shader5 : enable\n";
}
else if (getOutputType() == SH_ESSL_OUTPUT && getShaderVersion() < 320)
{
sink << "#extension GL_OES_gpu_shader5 : enable\n";
sink << "#extension GL_EXT_gpu_shader5 : enable\n";
}
}
if (usesTextureCubeMapArray)
{
if (getOutputType() >= SH_GLSL_150_CORE_OUTPUT && getOutputType() < SH_GLSL_400_CORE_OUTPUT)
{
sink << "#extension GL_ARB_texture_cube_map_array : enable\n";
}
else if (getOutputType() == SH_ESSL_OUTPUT && getShaderVersion() < 320)
{
sink << "#extension GL_OES_texture_cube_map_array : enable\n";
sink << "#extension GL_EXT_texture_cube_map_array : enable\n";
}
}
if (usesTextureBuffer)
{
if (getOutputType() >= SH_GLSL_150_CORE_OUTPUT && getOutputType() < SH_GLSL_400_CORE_OUTPUT)
{
sink << "#extension GL_ARB_texture_buffer_objects : enable\n";
}
else if (getOutputType() == SH_ESSL_OUTPUT && getShaderVersion() < 320)
{
sink << "#extension GL_OES_texture_buffer : enable\n";
sink << "#extension GL_EXT_texture_buffer : enable\n";
}
}
TExtensionGLSL extensionGLSL(getOutputType());
root->traverse(&extensionGLSL);
for (const auto &ext : extensionGLSL.getEnabledExtensions())
{
sink << "#extension " << ext << " : enable\n";
}
for (const auto &ext : extensionGLSL.getRequiredExtensions())
{
sink << "#extension " << ext << " : require\n";
}
}
void TranslatorGLSL::conditionallyOutputInvariantDeclaration(const char *builtinVaryingName)
{
if (isVaryingDefined(builtinVaryingName))
{
TInfoSinkBase &sink = getInfoSink().obj;
sink << "invariant " << builtinVaryingName << ";\n";
}
}
} // namespace sh