Commit ffb35f64 by Olli Etuaho Committed by Commit Bot

Fix broadcasting gl_FragColor at the end of main

Previously, the broadcast step would not get run in case the main() function returned before reaching its end. Now the broadcast step is put in a separate function that wraps main() if needed, so that it gets run even if the main() function in the original shader source returns in the middle. DrawBuffersTest is refactored to use ANGLETest::drawQuad() instead of calling glDrawArrays directly. BUG=angleproject:2109 TEST=WebGL conformance tests, angle_end2end_tests Change-Id: Id5f05094e816df03bc9c8ca62b60de914072682c Reviewed-on: https://chromium-review.googlesource.com/574597Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 3e5695d1
...@@ -114,6 +114,8 @@ ...@@ -114,6 +114,8 @@
'compiler/translator/RewriteUnaryMinusOperatorFloat.h', 'compiler/translator/RewriteUnaryMinusOperatorFloat.h',
'compiler/translator/RewriteUnaryMinusOperatorInt.cpp', 'compiler/translator/RewriteUnaryMinusOperatorInt.cpp',
'compiler/translator/RewriteUnaryMinusOperatorInt.h', 'compiler/translator/RewriteUnaryMinusOperatorInt.h',
'compiler/translator/RunAtTheEndOfShader.cpp',
'compiler/translator/RunAtTheEndOfShader.h',
'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp',
'compiler/translator/ScalarizeVecAndMatConstructorArgs.h', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.h',
'compiler/translator/SearchSymbol.cpp', 'compiler/translator/SearchSymbol.cpp',
......
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
#include "compiler/translator/EmulateGLFragColorBroadcast.h" #include "compiler/translator/EmulateGLFragColorBroadcast.h"
#include "compiler/translator/FindMain.h"
#include "compiler/translator/IntermNode_util.h" #include "compiler/translator/IntermNode_util.h"
#include "compiler/translator/IntermTraverse.h" #include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/RunAtTheEndOfShader.h"
namespace sh namespace sh
{ {
...@@ -88,15 +88,17 @@ void GLFragColorBroadcastTraverser::broadcastGLFragColor(TIntermBlock *root) ...@@ -88,15 +88,17 @@ void GLFragColorBroadcastTraverser::broadcastGLFragColor(TIntermBlock *root)
{ {
return; return;
} }
TIntermSequence *mainSequence = FindMainBody(root)->getSequence();
TIntermBlock *broadcastBlock = new TIntermBlock();
// Now insert statements // Now insert statements
// gl_FragData[1] = gl_FragData[0]; // gl_FragData[1] = gl_FragData[0];
// ... // ...
// gl_FragData[maxDrawBuffers - 1] = gl_FragData[0]; // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex) for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
{ {
mainSequence->insert(mainSequence->end(), constructGLFragDataAssignNode(colorIndex)); broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex));
} }
RunAtTheEndOfShader(root, broadcastBlock);
} }
} // namespace anonymous } // namespace anonymous
......
//
// Copyright (c) 2017 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.
//
// RunAtTheEndOfShader.cpp: Add code to be run at the end of the shader. In case main() contains a
// return statement, this is done by replacing the main() function with another function that calls
// the old main, like this:
//
// void main() { body }
// =>
// void main0() { body }
// void main()
// {
// main0();
// codeToRun
// }
//
// This way the code will get run even if the return statement inside main is executed.
//
#include "compiler/translator/RunAtTheEndOfShader.h"
#include "compiler/translator/FindMain.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/IntermNode_util.h"
#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/SymbolTable.h"
namespace sh
{
namespace
{
class ContainsReturnTraverser : public TIntermTraverser
{
public:
ContainsReturnTraverser() : TIntermTraverser(true, false, false), mContainsReturn(false) {}
bool visitBranch(Visit visit, TIntermBranch *node) override
{
if (node->getFlowOp() == EOpReturn)
{
mContainsReturn = true;
}
return false;
}
bool containsReturn() { return mContainsReturn; }
private:
bool mContainsReturn;
};
bool ContainsReturn(TIntermNode *node)
{
ContainsReturnTraverser traverser;
node->traverse(&traverser);
return traverser.containsReturn();
}
void WrapMainAndAppend(TIntermBlock *root, TIntermFunctionDefinition *main, TIntermNode *codeToRun)
{
// Replace main() with main0() with the same body.
TSymbolUniqueId oldMainId;
std::stringstream oldMainName;
oldMainName << "main" << oldMainId.get();
TIntermFunctionDefinition *oldMain = CreateInternalFunctionDefinitionNode(
TType(EbtVoid), oldMainName.str().c_str(), main->getBody(), oldMainId);
bool replaced = root->replaceChildNode(main, oldMain);
ASSERT(replaced);
// void main()
TIntermFunctionPrototype *newMainProto = new TIntermFunctionPrototype(
TType(EbtVoid), main->getFunctionPrototype()->getFunctionSymbolInfo()->getId());
newMainProto->getFunctionSymbolInfo()->setName("main");
// {
// main0();
// codeToRun
// }
TIntermBlock *newMainBody = new TIntermBlock();
TIntermAggregate *oldMainCall = CreateInternalFunctionCallNode(
TType(EbtVoid), oldMainName.str().c_str(), oldMainId, new TIntermSequence());
newMainBody->appendStatement(oldMainCall);
newMainBody->appendStatement(codeToRun);
// Add the new main() to the root node.
TIntermFunctionDefinition *newMain = new TIntermFunctionDefinition(newMainProto, newMainBody);
root->appendStatement(newMain);
}
} // anonymous namespace
void RunAtTheEndOfShader(TIntermBlock *root, TIntermNode *codeToRun)
{
TIntermFunctionDefinition *main = FindMain(root);
if (!ContainsReturn(main))
{
main->getBody()->appendStatement(codeToRun);
return;
}
WrapMainAndAppend(root, main, codeToRun);
}
} // namespace sh
//
// Copyright (c) 2017 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.
//
// RunAtTheEndOfShader.h: Add code to be run at the end of the shader.
//
#ifndef COMPILER_TRANSLATOR_RUNATTHEENDOFSHADER_H_
#define COMPILER_TRANSLATOR_RUNATTHEENDOFSHADER_H_
namespace sh
{
class TIntermBlock;
class TIntermNode;
void RunAtTheEndOfShader(TIntermBlock *root, TIntermNode *codeToRun);
} // namespace sh
#endif // COMPILER_TRANSLATOR_RUNATTHEENDOFSHADER_H_
\ No newline at end of file
...@@ -43,17 +43,6 @@ class DrawBuffersTest : public ANGLETest ...@@ -43,17 +43,6 @@ class DrawBuffersTest : public ANGLETest
GL_UNSIGNED_BYTE, nullptr); GL_UNSIGNED_BYTE, nullptr);
} }
GLfloat data[] =
{
-1.0f, 1.0f,
-1.0f, -2.0f,
2.0f, 1.0f
};
glGenBuffers(1, &mBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6, data, GL_STATIC_DRAW);
if (checkSupport()) if (checkSupport())
{ {
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &mMaxDrawBuffers); glGetIntegerv(GL_MAX_DRAW_BUFFERS, &mMaxDrawBuffers);
...@@ -66,7 +55,6 @@ class DrawBuffersTest : public ANGLETest ...@@ -66,7 +55,6 @@ class DrawBuffersTest : public ANGLETest
{ {
glDeleteFramebuffers(1, &mFBO); glDeleteFramebuffers(1, &mFBO);
glDeleteTextures(4, mTextures); glDeleteTextures(4, mTextures);
glDeleteBuffers(1, &mBuffer);
ANGLETest::TearDown(); ANGLETest::TearDown();
} }
...@@ -139,14 +127,6 @@ class DrawBuffersTest : public ANGLETest ...@@ -139,14 +127,6 @@ class DrawBuffersTest : public ANGLETest
{ {
FAIL() << "shader compilation failed."; FAIL() << "shader compilation failed.";
} }
glUseProgram(*programOut);
GLint location = glGetAttribLocation(*programOut, "position");
ASSERT_NE(location, -1);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 8, nullptr);
glEnableVertexAttribArray(location);
} }
void setupMRTProgramESSL1(bool bufferEnabled[8], GLuint *programOut) void setupMRTProgramESSL1(bool bufferEnabled[8], GLuint *programOut)
...@@ -185,14 +165,6 @@ class DrawBuffersTest : public ANGLETest ...@@ -185,14 +165,6 @@ class DrawBuffersTest : public ANGLETest
{ {
FAIL() << "shader compilation failed."; FAIL() << "shader compilation failed.";
} }
glUseProgram(*programOut);
GLint location = glGetAttribLocation(*programOut, "position");
ASSERT_NE(location, -1);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 8, nullptr);
glEnableVertexAttribArray(location);
} }
void setupMRTProgram(bool bufferEnabled[8], GLuint *programOut) void setupMRTProgram(bool bufferEnabled[8], GLuint *programOut)
...@@ -243,7 +215,6 @@ class DrawBuffersTest : public ANGLETest ...@@ -243,7 +215,6 @@ class DrawBuffersTest : public ANGLETest
GLuint mFBO; GLuint mFBO;
GLuint mTextures[4]; GLuint mTextures[4];
GLuint mBuffer;
GLint mMaxDrawBuffers; GLint mMaxDrawBuffers;
}; };
...@@ -300,9 +271,8 @@ TEST_P(DrawBuffersTest, Gaps) ...@@ -300,9 +271,8 @@ TEST_P(DrawBuffersTest, Gaps)
GL_NONE, GL_NONE,
GL_COLOR_ATTACHMENT1 GL_COLOR_ATTACHMENT1
}; };
glUseProgram(program);
setDrawBuffers(2, bufs); setDrawBuffers(2, bufs);
glDrawArrays(GL_TRIANGLES, 0, 3); drawQuad(program, "position", 0.5);
verifyAttachment2D(1, mTextures[0], GL_TEXTURE_2D, 0); verifyAttachment2D(1, mTextures[0], GL_TEXTURE_2D, 0);
...@@ -344,9 +314,8 @@ TEST_P(DrawBuffersTest, FirstAndLast) ...@@ -344,9 +314,8 @@ TEST_P(DrawBuffersTest, FirstAndLast)
GL_COLOR_ATTACHMENT3 GL_COLOR_ATTACHMENT3
}; };
glUseProgram(program);
setDrawBuffers(4, bufs); setDrawBuffers(4, bufs);
glDrawArrays(GL_TRIANGLES, 0, 3); drawQuad(program, "position", 0.5);
verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0); verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0);
verifyAttachment2D(3, mTextures[1], GL_TEXTURE_2D, 0); verifyAttachment2D(3, mTextures[1], GL_TEXTURE_2D, 0);
...@@ -388,9 +357,8 @@ TEST_P(DrawBuffersTest, FirstHalfNULL) ...@@ -388,9 +357,8 @@ TEST_P(DrawBuffersTest, FirstHalfNULL)
GLuint program; GLuint program;
setupMRTProgram(flags, &program); setupMRTProgram(flags, &program);
glUseProgram(program);
setDrawBuffers(mMaxDrawBuffers, bufs); setDrawBuffers(mMaxDrawBuffers, bufs);
glDrawArrays(GL_TRIANGLES, 0, 3); drawQuad(program, "position", 0.5);
for (GLuint texIndex = 0; texIndex < halfMaxDrawBuffers; texIndex++) for (GLuint texIndex = 0; texIndex < halfMaxDrawBuffers; texIndex++)
{ {
...@@ -431,13 +399,65 @@ TEST_P(DrawBuffersTest, UnwrittenOutputVariablesShouldNotCrash) ...@@ -431,13 +399,65 @@ TEST_P(DrawBuffersTest, UnwrittenOutputVariablesShouldNotCrash)
GL_NONE, GL_NONE,
}; };
glUseProgram(program);
setDrawBuffers(4, bufs); setDrawBuffers(4, bufs);
// This call should not crash when we dynamically generate the HLSL code. // This call should not crash when we dynamically generate the HLSL code.
glDrawArrays(GL_TRIANGLES, 0, 3); drawQuad(program, "position", 0.5);
verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0);
EXPECT_GL_NO_ERROR();
glDeleteProgram(program);
}
TEST_P(DrawBuffersTest, BroadcastGLFragColor)
{
if (!extensionEnabled("GL_EXT_draw_buffers"))
{
std::cout << "Test skipped because EGL_EXT_draw_buffers is not enabled." << std::endl;
return;
}
// Bind two render targets. gl_FragColor should be broadcast to both.
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glBindTexture(GL_TEXTURE_2D, mTextures[1]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], 0);
const GLenum bufs[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
const std::string vertexShaderSource =
"attribute vec4 position;\n"
"void main() {\n"
" gl_Position = position;\n"
"}\n";
const std::string fragmentShaderSource =
"#extension GL_EXT_draw_buffers : enable\n"
"precision highp float;\n"
"uniform float u_zero;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(1, 0, 0, 1);\n"
" if (u_zero < 1.0)\n"
" {\n"
" return;\n"
" }\n"
"}\n";
GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
if (program == 0)
{
FAIL() << "shader compilation failed.";
}
setDrawBuffers(2, bufs);
drawQuad(program, "position", 0.5);
verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0); verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0);
verifyAttachment2D(0, mTextures[1], GL_TEXTURE_2D, 0);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
...@@ -470,10 +490,8 @@ TEST_P(DrawBuffersTestES3, 3DTextures) ...@@ -470,10 +490,8 @@ TEST_P(DrawBuffersTestES3, 3DTextures)
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
}; };
glUseProgram(program);
glDrawBuffers(4, bufs); glDrawBuffers(4, bufs);
drawQuad(program, "position", 0.5);
glDrawArrays(GL_TRIANGLES, 0, 3);
verifyAttachmentLayer(0, texture.get(), 0, 0); verifyAttachmentLayer(0, texture.get(), 0, 0);
verifyAttachmentLayer(1, texture.get(), 0, 1); verifyAttachmentLayer(1, texture.get(), 0, 1);
...@@ -507,10 +525,8 @@ TEST_P(DrawBuffersTestES3, 2DArrayTextures) ...@@ -507,10 +525,8 @@ TEST_P(DrawBuffersTestES3, 2DArrayTextures)
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
}; };
glUseProgram(program);
glDrawBuffers(4, bufs); glDrawBuffers(4, bufs);
drawQuad(program, "position", 0.5);
glDrawArrays(GL_TRIANGLES, 0, 3);
verifyAttachmentLayer(0, texture.get(), 0, 0); verifyAttachmentLayer(0, texture.get(), 0, 0);
verifyAttachmentLayer(1, texture.get(), 0, 1); verifyAttachmentLayer(1, texture.get(), 0, 1);
......
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