Commit af1eed2e by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: Generate gl_FragColor/Data declarations in AST

gl_FragColor and gl_FragData are not available in Vulkan. Prior to this change, their declaration as webgl_FragColor and webgl_FragData was done in text. This change implements an AST transformation that declares a normal fragment output variable and replaces all references to these built-ins with those variables. Bug: angleproject:4889 Change-Id: If224e089dec25e4aa580beb135e1be2890de7887 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2953042Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 4fbb6f43
......@@ -189,7 +189,6 @@ angle_translator_sources = [
"src/compiler/translator/tree_ops/gl/mac/AddAndTrueToLoopCondition.h",
"src/compiler/translator/tree_ops/gl/mac/RewriteDoWhile.h",
"src/compiler/translator/tree_ops/gl/mac/UnfoldShortCircuitAST.h",
"src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h",
"src/compiler/translator/tree_ops/vulkan/EarlyFragmentTestsOptimization.h",
"src/compiler/translator/tree_util/AsNode.h",
"src/compiler/translator/tree_util/BuiltIn.h",
......@@ -335,7 +334,10 @@ angle_translator_lib_vulkan_sources = [
"src/compiler/translator/TranslatorVulkan.cpp",
"src/compiler/translator/glslang_wrapper.cpp",
"src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.cpp",
"src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h",
"src/compiler/translator/tree_ops/vulkan/EarlyFragmentTestsOptimization.cpp",
"src/compiler/translator/tree_ops/vulkan/EmulateFragColorData.cpp",
"src/compiler/translator/tree_ops/vulkan/EmulateFragColorData.h",
"src/compiler/translator/tree_ops/vulkan/FlagSamplersWithTexelFetch.cpp",
"src/compiler/translator/tree_ops/vulkan/FlagSamplersWithTexelFetch.h",
"src/compiler/translator/tree_ops/vulkan/MonomorphizeUnsupportedFunctionsInVulkanGLSL.cpp",
......
......@@ -61,11 +61,11 @@ void TOutputGLSL::visitSymbol(TIntermSymbol *node)
}
else if (name == "gl_SecondaryFragColorEXT")
{
out << "angle_SecondaryFragColor";
out << "webgl_SecondaryFragColor";
}
else if (name == "gl_SecondaryFragDataEXT")
{
out << "angle_SecondaryFragData";
out << "webgl_SecondaryFragData";
}
else
{
......
......@@ -126,11 +126,17 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermSymbol *symbol)
if (needsLocation)
{
const unsigned int locationCount =
CalculateVaryingLocationCount(symbol->getType(), getShaderType());
uint32_t location = IsShaderIn(type.getQualifier())
? nextUnusedInputLocation(locationCount)
: nextUnusedOutputLocation(locationCount);
uint32_t location = 0;
if (layoutQualifier.index <= 0)
{
// Note: for index == 1 (dual source blending), don't count locations as they are
// expected to alias the color output locations. Only one dual-source output is
// supported, so location will be always 0.
const unsigned int locationCount =
CalculateVaryingLocationCount(symbol->getType(), getShaderType());
location = IsShaderIn(type.getQualifier()) ? nextUnusedInputLocation(locationCount)
: nextUnusedOutputLocation(locationCount);
}
out << separator << "location=" << location;
separator = kCommaSeparator;
......
......@@ -189,11 +189,11 @@ bool TranslatorGLSL::translate(TIntermBlock *root,
}
if (hasGLSecondaryFragColor)
{
sink << "out vec4 angle_SecondaryFragColor;\n";
sink << "out vec4 webgl_SecondaryFragColor;\n";
}
if (hasGLSecondaryFragData)
{
sink << "out vec4 angle_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers
sink << "out vec4 webgl_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers
<< "];\n";
}
......
......@@ -859,7 +859,7 @@ bool TranslatorMetalDirect::translateImpl(TInfoSinkBase &sink,
}
}
SymbolEnv symbolEnv(*this, *root);
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// Declare gl_FragColor and gl_FragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
if (getShaderType() == GL_FRAGMENT_SHADER)
{
......
......@@ -28,6 +28,7 @@
#include "compiler/translator/tree_ops/RewriteDfdy.h"
#include "compiler/translator/tree_ops/RewriteStructSamplers.h"
#include "compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h"
#include "compiler/translator/tree_ops/vulkan/EmulateFragColorData.h"
#include "compiler/translator/tree_ops/vulkan/FlagSamplersWithTexelFetch.h"
#include "compiler/translator/tree_ops/vulkan/MonomorphizeUnsupportedFunctionsInVulkanGLSL.h"
#include "compiler/translator/tree_ops/vulkan/ReplaceForShaderFramebufferFetch.h"
......@@ -1048,67 +1049,24 @@ bool TranslatorVulkan::translateImpl(TInfoSinkBase &sink,
}
}
bool hasGLFragColor = false;
bool hasGLFragData = false;
bool usePreRotation = (compileOptions & SH_ADD_PRE_ROTATION) != 0;
bool hasGLSampleMask = false;
bool hasGLSecondaryFragColor = false;
bool hasGLSecondaryFragData = false;
bool usePreRotation = (compileOptions & SH_ADD_PRE_ROTATION) != 0;
bool hasGLSampleMask = false;
for (const ShaderVariable &outputVar : mOutputVariables)
{
if (outputVar.name == "gl_FragColor")
{
ASSERT(!hasGLFragColor);
hasGLFragColor = true;
continue;
}
else if (outputVar.name == "gl_FragData")
{
ASSERT(!hasGLFragData);
hasGLFragData = true;
continue;
}
else if (outputVar.name == "gl_SampleMask")
if (outputVar.name == "gl_SampleMask")
{
ASSERT(!hasGLSampleMask);
hasGLSampleMask = true;
continue;
}
else if (outputVar.name == "gl_SecondaryFragColorEXT")
{
ASSERT(!hasGLSecondaryFragColor);
hasGLSecondaryFragColor = true;
continue;
}
else if (outputVar.name == "gl_SecondaryFragDataEXT")
{
ASSERT(!hasGLSecondaryFragData);
hasGLSecondaryFragData = true;
continue;
}
}
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
ASSERT(!((hasGLFragColor || hasGLSecondaryFragColor) &&
(hasGLFragData || hasGLSecondaryFragData)));
if (hasGLFragColor)
// Emulate gl_FragColor and gl_FragData with normal output variables.
mValidateASTOptions.validateVariableReferences = false;
if (!EmulateFragColorData(this, root, &getSymbolTable()))
{
sink << "layout(location = 0) out vec4 webgl_FragColor;\n";
}
if (hasGLFragData)
{
sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
}
if (hasGLSecondaryFragColor)
{
sink << "layout(location = 0, index = 1) out vec4 angle_SecondaryFragColor;\n";
}
if (hasGLSecondaryFragData)
{
sink << "layout(location = 0, index = 1) out vec4 angle_SecondaryFragData["
<< getResources().MaxDualSourceDrawBuffers << "];\n";
return false;
}
if (usesPointCoord)
......
//
// Copyright 2021 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.
//
// EmulateFragColorData: Emulate gl_FragColor and gl_FragData.
//
#include "compiler/translator/tree_ops/vulkan/EmulateFragColorData.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/ImmutableStringBuilder.h"
#include "compiler/translator/StaticType.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/tree_util/ReplaceVariable.h"
namespace sh
{
namespace
{
// Traverser that:
//
// 1. Declares outputs corresponding to gl_FragColor and gl_FragData (and their corresponding
// Secondary versions for framebuffer fetch).
// 2. Replaces built-in references with these variables.
class EmulateFragColorDataTraverser : public TIntermTraverser
{
public:
EmulateFragColorDataTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
: TIntermTraverser(true, false, false, symbolTable), mResources(compiler->getResources())
{}
void visitSymbol(TIntermSymbol *symbol) override
{
const TVariable *variable = &symbol->variable();
const TType &type = variable->getType();
// If this built-in was already visited, reuse the variable defined for it.
auto replacement = mVariableMap.find(variable);
if (replacement != mVariableMap.end())
{
queueReplacement(replacement->second->deepCopy(), OriginalNode::IS_DROPPED);
return;
}
unsigned int arraySize = 0;
int index = 0;
const char *name = "";
// Replace the built-ins being emulated with a variable of the appropriate type.
switch (type.getQualifier())
{
case EvqFragColor:
name = "webgl_FragColor";
break;
case EvqFragData:
name = "webgl_FragData";
arraySize = mResources.MaxDrawBuffers;
break;
case EvqSecondaryFragColorEXT:
name = "webgl_SecondaryFragColor";
index = 1;
break;
case EvqSecondaryFragDataEXT:
name = "webgl_SecondaryFragData";
index = 1;
arraySize = mResources.MaxDualSourceDrawBuffers;
break;
default:
// Not the built-in we are looking for.
return;
}
TType *outputType = new TType(*StaticType::GetQualified<EbtFloat, EvqFragmentOut, 4, 1>());
if (arraySize > 0)
{
outputType->makeArray(arraySize);
}
if (index > 0)
{
TLayoutQualifier layoutQualifier = outputType->getLayoutQualifier();
layoutQualifier.index = index;
outputType->setLayoutQualifier(layoutQualifier);
}
TVariable *replacementVar = new TVariable(mSymbolTable, ImmutableString(name), outputType,
SymbolType::AngleInternal);
TIntermSymbol *newSymbol = new TIntermSymbol(replacementVar);
mVariableMap[variable] = newSymbol;
queueReplacement(newSymbol, OriginalNode::IS_DROPPED);
}
void addDeclarations(TIntermBlock *root)
{
// Insert the declaration before the first function.
size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
TIntermSequence declarations;
for (auto &replaced : mVariableMap)
{
TIntermDeclaration *decl = new TIntermDeclaration;
TIntermSymbol *symbol = replaced.second->deepCopy()->getAsSymbolNode();
decl->appendDeclarator(symbol);
declarations.push_back(decl);
}
root->insertChildNodes(firstFunctionIndex, declarations);
}
private:
const ShBuiltInResources &mResources;
// A map of already replaced built-in variables.
VariableReplacementMap mVariableMap;
};
} // anonymous namespace
bool EmulateFragColorData(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
{
if (compiler->getShaderType() != GL_FRAGMENT_SHADER)
{
return true;
}
EmulateFragColorDataTraverser traverser(compiler, symbolTable);
root->traverse(&traverser);
if (!traverser.updateTree(compiler, root))
{
return false;
}
traverser.addDeclarations(root);
return compiler->validateAST(root);
}
} // namespace sh
//
// Copyright 2021 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.
//
// EmulateFragColorData: Turn gl_FragColor and gl_FragData into normal fragment shader outputs.
// These built-ins are not supported by Vulkan.
//
#ifndef COMPILER_TRANSLATOR_TREEOPS_VULKAN_EMULATEFRAGCOLORDATA_H_
#define COMPILER_TRANSLATOR_TREEOPS_VULKAN_EMULATEFRAGCOLORDATA_H_
#include "common/angleutils.h"
namespace sh
{
class TCompiler;
class TIntermBlock;
class TSymbolTable;
ANGLE_NO_DISCARD bool EmulateFragColorData(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable);
} // namespace sh
#endif // COMPILER_TRANSLATOR_TREEOPS_VULKAN_EMULATEFRAGCOLORDATA_H_
......@@ -33,6 +33,9 @@ enum
IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS =
IMPLEMENTATION_MAX_DRAW_BUFFERS + 2, // 2 extra for depth and/or stencil buffers
// The vast majority of devices support only one dual-source draw buffer
IMPLEMENTATION_MAX_DUAL_SOURCE_DRAW_BUFFERS = 1,
IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS = 16,
IMPLEMENTATION_MAX_GEOMETRY_SHADER_UNIFORM_BUFFERS = 16,
IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS = 16,
......
......@@ -3732,6 +3732,9 @@ void Context::initCaps()
ANGLE_LIMIT_CAP(mState.mCaps.maxSampleMaskWords, MAX_SAMPLE_MASK_WORDS);
ANGLE_LIMIT_CAP(mState.mExtensions.maxDualSourceDrawBuffers,
IMPLEMENTATION_MAX_DUAL_SOURCE_DRAW_BUFFERS);
// WebGL compatibility
mState.mExtensions.webglCompatibility = mWebGLContext;
for (const auto &extensionInfo : GetExtensionInfoMap())
......
......@@ -324,12 +324,12 @@ std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 0,
"webgl_FragColor");
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 1,
"angle_SecondaryFragColor");
"webgl_SecondaryFragColor");
}
else if (output.name == "gl_SecondaryFragDataEXT")
{
// Basically we should have a loop here going over the output
// array binding "webgl_FragData[i]" and "angle_SecondaryFragData[i]" array
// array binding "webgl_FragData[i]" and "webgl_SecondaryFragData[i]" array
// indices to the correct color buffers and color indices.
// However I'm not sure if this construct is legal or not, neither ARB or
// EXT version of the spec mention this. They only mention that
......@@ -349,7 +349,7 @@ std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 0, "webgl_FragData");
mFunctions->bindFragDataLocationIndexed(mProgramID, 0, 1,
"angle_SecondaryFragData");
"webgl_SecondaryFragData");
}
}
}
......
......@@ -334,13 +334,13 @@ void AssignSecondaryOutputLocations(const gl::ProgramState &programState,
if (outputVar.name == "gl_SecondaryFragColorEXT")
{
AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment,
"angle_SecondaryFragColor", 0,
"webgl_SecondaryFragColor", 0,
ShaderInterfaceVariableInfo::kInvalid, 0, 0);
}
else if (outputVar.name == "gl_SecondaryFragDataEXT")
{
AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment,
"angle_SecondaryFragData", 0, ShaderInterfaceVariableInfo::kInvalid,
"webgl_SecondaryFragData", 0, ShaderInterfaceVariableInfo::kInvalid,
0, 0);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment