Commit 4e94fea8 by Zhenyao Mo Committed by Commit Bot

Emulate gl_FragColor broadcasting behavior when GL_EXT_draw_buffers is

enabled. BUG=angleproject:1467,635433 TEST=WebGL conformance, angle_unittests Change-Id: I9eb4ce715732087a3786da886f42243716f2b9b2 Reviewed-on: https://chromium-review.googlesource.com/367532 Commit-Queue: Zhenyao Mo <zmo@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 856c497e
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
'compiler/translator/Diagnostics.h', 'compiler/translator/Diagnostics.h',
'compiler/translator/DirectiveHandler.cpp', 'compiler/translator/DirectiveHandler.cpp',
'compiler/translator/DirectiveHandler.h', 'compiler/translator/DirectiveHandler.h',
'compiler/translator/EmulateGLFragColorBroadcast.cpp',
'compiler/translator/EmulateGLFragColorBroadcast.h',
'compiler/translator/EmulatePrecision.cpp', 'compiler/translator/EmulatePrecision.cpp',
'compiler/translator/EmulatePrecision.h', 'compiler/translator/EmulatePrecision.h',
'compiler/translator/ExpandIntegerPowExpressions.cpp', 'compiler/translator/ExpandIntegerPowExpressions.cpp',
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "compiler/translator/Compiler.h" #include "compiler/translator/Compiler.h"
#include "compiler/translator/CallDAG.h" #include "compiler/translator/CallDAG.h"
#include "compiler/translator/DeferGlobalInitializers.h" #include "compiler/translator/DeferGlobalInitializers.h"
#include "compiler/translator/EmulateGLFragColorBroadcast.h"
#include "compiler/translator/ForLoopUnroll.h" #include "compiler/translator/ForLoopUnroll.h"
#include "compiler/translator/Initialize.h" #include "compiler/translator/Initialize.h"
#include "compiler/translator/InitializeParseContext.h" #include "compiler/translator/InitializeParseContext.h"
...@@ -386,6 +387,13 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -386,6 +387,13 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
root->traverse(&gen); root->traverse(&gen);
} }
if (success && shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
IsExtensionEnabled(extensionBehavior, "GL_EXT_draw_buffers"))
{
EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables);
}
if (success) if (success)
{ {
DeferGlobalInitializers(root); DeferGlobalInitializers(root);
......
//
// Copyright (c) 2002-2016 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.
//
// gl_FragColor needs to broadcast to all color buffers in ES2 if
// GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
//
// We emulate this by replacing all gl_FragColor with gl_FragData[0], and in the end
// of main() function, assigning gl_FragData[1], ..., gl_FragData[maxDrawBuffers-1]
// with gl_FragData[0].
//
#include "compiler/translator/EmulateGLFragColorBroadcast.h"
#include "compiler/translator/IntermNode.h"
namespace
{
TIntermConstantUnion *constructIndexNode(int index)
{
TConstantUnion *u = new TConstantUnion[1];
u[0].setIConst(index);
TType type(EbtInt, EbpUndefined, EvqConst, 1);
TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
return node;
}
TIntermBinary *constructGLFragDataNode(int index)
{
TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
TIntermSymbol *symbol = new TIntermSymbol(0, "gl_FragData", TType(EbtFloat, 4));
indexDirect->setLeft(symbol);
TIntermConstantUnion *indexNode = constructIndexNode(index);
indexDirect->setRight(indexNode);
return indexDirect;
}
TIntermBinary *constructGLFragDataAssignNode(int index)
{
TIntermBinary *assign = new TIntermBinary(EOpAssign);
assign->setLeft(constructGLFragDataNode(index));
assign->setRight(constructGLFragDataNode(0));
assign->setType(TType(EbtFloat, 4));
return assign;
}
class GLFragColorBroadcastTraverser : public TIntermTraverser
{
public:
GLFragColorBroadcastTraverser()
: TIntermTraverser(true, false, false), mMainSequence(nullptr), mGLFragColorUsed(false)
{
}
void broadcastGLFragColor(int maxDrawBuffers);
bool isGLFragColorUsed() const { return mGLFragColorUsed; }
protected:
void visitSymbol(TIntermSymbol *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
private:
TIntermSequence *mMainSequence;
bool mGLFragColorUsed;
};
void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
{
if (node->getSymbol() == "gl_FragColor")
{
queueReplacement(node, constructGLFragDataNode(0), OriginalNode::IS_DROPPED);
mGLFragColorUsed = true;
}
}
bool GLFragColorBroadcastTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
switch (node->getOp())
{
case EOpFunction:
// Function definition.
ASSERT(visit == PreVisit);
if (node->getName() == "main(")
{
TIntermSequence *sequence = node->getSequence();
ASSERT((sequence->size() == 1) || (sequence->size() == 2));
if (sequence->size() == 2)
{
TIntermAggregate *body = (*sequence)[1]->getAsAggregate();
ASSERT(body);
mMainSequence = body->getSequence();
}
}
break;
default:
break;
}
return true;
}
void GLFragColorBroadcastTraverser::broadcastGLFragColor(int maxDrawBuffers)
{
ASSERT(maxDrawBuffers > 1);
if (!mGLFragColorUsed)
{
return;
}
ASSERT(mMainSequence);
// Now insert statements
// gl_FragData[1] = gl_FragData[0];
// ...
// gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
for (int colorIndex = 1; colorIndex < maxDrawBuffers; ++colorIndex)
{
mMainSequence->insert(mMainSequence->end(), constructGLFragDataAssignNode(colorIndex));
}
}
} // namespace anonymous
void EmulateGLFragColorBroadcast(TIntermNode *root,
int maxDrawBuffers,
std::vector<sh::OutputVariable> *outputVariables)
{
ASSERT(maxDrawBuffers > 1);
GLFragColorBroadcastTraverser traverser;
root->traverse(&traverser);
if (traverser.isGLFragColorUsed())
{
traverser.updateTree();
traverser.broadcastGLFragColor(maxDrawBuffers);
for (auto &var : *outputVariables)
{
if (var.name == "gl_FragColor")
{
// TODO(zmo): Find a way to keep the original variable information.
var.name = "gl_FragData";
var.mappedName = "gl_FragData";
var.arraySize = maxDrawBuffers;
}
}
}
}
//
// Copyright (c) 2002-2016 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.
//
// Emulate gl_FragColor broadcast behaviors in ES2 where
// GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
//
#ifndef COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
#define COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
#include <vector>
namespace sh
{
struct OutputVariable;
}
class TIntermNode;
// Replace all gl_FragColor with gl_FragData[0], and in the end of main() function,
// assign gl_FragData[1] ... gl_FragData[maxDrawBuffers - 1] with gl_FragData[0].
// If gl_FragColor is in outputVariables, it is replaced by gl_FragData.
void EmulateGLFragColorBroadcast(TIntermNode *root,
int maxDrawBuffers,
std::vector<sh::OutputVariable> *outputVariables);
#endif // COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
'<(angle_path)/src/tests/compiler_tests/CollectVariables_test.cpp', '<(angle_path)/src/tests/compiler_tests/CollectVariables_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ConstantFolding_test.cpp', '<(angle_path)/src/tests/compiler_tests/ConstantFolding_test.cpp',
'<(angle_path)/src/tests/compiler_tests/DebugShaderPrecision_test.cpp', '<(angle_path)/src/tests/compiler_tests/DebugShaderPrecision_test.cpp',
'<(angle_path)/src/tests/compiler_tests/EmulateGLFragColorBroadcast_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp', '<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp',
'<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp', '<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp',
'<(angle_path)/src/tests/compiler_tests/FragDepth_test.cpp', '<(angle_path)/src/tests/compiler_tests/FragDepth_test.cpp',
......
//
// Copyright (c) 2016 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.
//
// EmulateGLFragColorBroadcast_test.cpp:
// Tests for gl_FragColor broadcast behavior emulation.
//
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "GLSLANG/ShaderLang.h"
#include "tests/test_utils/compiler_test.h"
namespace
{
const int kMaxDrawBuffers = 2;
class EmulateGLFragColorBroadcastTest : public MatchOutputCodeTest
{
public:
EmulateGLFragColorBroadcastTest()
: MatchOutputCodeTest(GL_FRAGMENT_SHADER,
0, // compile options
SH_GLSL_COMPATIBILITY_OUTPUT)
{
getResources()->MaxDrawBuffers = kMaxDrawBuffers;
getResources()->EXT_draw_buffers = 1;
}
};
// Verifies that without explicitly enabling GL_EXT_draw_buffers extension
// in the shader, no broadcast emulation.
TEST_F(EmulateGLFragColorBroadcastTest, FragColorNoBroadcast)
{
const std::string shaderString =
"void main()\n"
"{\n"
" gl_FragColor = vec4(1, 0, 0, 0);\n"
"}\n";
compile(shaderString);
EXPECT_TRUE(foundInCode("gl_FragColor"));
EXPECT_FALSE(foundInCode("gl_FragData[0]"));
EXPECT_FALSE(foundInCode("gl_FragData[1]"));
}
// Verifies that with explicitly enabling GL_EXT_draw_buffers extension
// in the shader, broadcast is emualted by replacing gl_FragColor with gl_FragData.
TEST_F(EmulateGLFragColorBroadcastTest, FragColorBroadcast)
{
const std::string shaderString =
"#extension GL_EXT_draw_buffers : require\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(1, 0, 0, 0);\n"
"}\n";
compile(shaderString);
EXPECT_FALSE(foundInCode("gl_FragColor"));
EXPECT_TRUE(foundInCode("gl_FragData[0]"));
EXPECT_TRUE(foundInCode("gl_FragData[1]"));
}
// Verifies that with explicitly enabling GL_EXT_draw_buffers extension
// in the shader with an empty main(), anothing happens.
TEST_F(EmulateGLFragColorBroadcastTest, EmptyMain)
{
const std::string shaderString =
"#extension GL_EXT_draw_buffers : require\n"
"void main()\n"
"{\n"
"}\n";
compile(shaderString);
EXPECT_FALSE(foundInCode("gl_FragColor"));
EXPECT_FALSE(foundInCode("gl_FragData[0]"));
EXPECT_FALSE(foundInCode("gl_FragData[1]"));
}
} // namespace anonymous
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