WebGL: Add test for ES3 MRT feedback loops.

BUG=angleproject:1685

Change-Id: I0f1e24d7c27f0b3ef56f35debcd20f4c7cff8951
Reviewed-on: https://chromium-review.googlesource.com/425492
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index 0964855..555f0a5 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -41,6 +41,11 @@
                                     const std::array<GLenum, 2> &drawBuffers,
                                     GLenum expectedError);
 
+    // Called from RenderingFeedbackLoopWithDrawBuffers.
+    void drawBuffersFeedbackLoop(GLuint program,
+                                 const std::array<GLenum, 2> &drawBuffers,
+                                 GLenum expectedError);
+
     PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
 };
 
@@ -596,67 +601,6 @@
     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
 }
 
-// Test tests that texture copying feedback loops are properly rejected in WebGL.
-// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
-TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
-{
-    GLTexture texture;
-    glBindTexture(GL_TEXTURE_2D, texture.get());
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-    GLTexture texture2;
-    glBindTexture(GL_TEXTURE_2D, texture2.get());
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-    GLFramebuffer framebuffer;
-    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
-
-    // framebuffer should be FRAMEBUFFER_COMPLETE.
-    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
-    ASSERT_GL_NO_ERROR();
-
-    // testing copyTexImage2D
-
-    // copyTexImage2D to same texture but different level
-    glBindTexture(GL_TEXTURE_2D, texture.get());
-    glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
-    EXPECT_GL_NO_ERROR();
-
-    // copyTexImage2D to same texture same level, invalid feedback loop
-    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
-    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
-
-    // copyTexImage2D to different texture
-    glBindTexture(GL_TEXTURE_2D, texture2.get());
-    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
-    EXPECT_GL_NO_ERROR();
-
-    // testing copyTexSubImage2D
-
-    // copyTexSubImage2D to same texture but different level
-    glBindTexture(GL_TEXTURE_2D, texture.get());
-    glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
-    EXPECT_GL_NO_ERROR();
-
-    // copyTexSubImage2D to same texture same level, invalid feedback loop
-    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
-    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
-
-    // copyTexSubImage2D to different texture
-    glBindTexture(GL_TEXTURE_2D, texture2.get());
-    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
-    EXPECT_GL_NO_ERROR();
-}
-
 // Test for the max draw buffers and color attachments.
 TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
 {
@@ -862,6 +806,142 @@
     drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
 }
 
+// Test tests that texture copying feedback loops are properly rejected in WebGL.
+// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
+TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
+{
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture.get());
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    GLTexture texture2;
+    glBindTexture(GL_TEXTURE_2D, texture2.get());
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    GLFramebuffer framebuffer;
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
+
+    // framebuffer should be FRAMEBUFFER_COMPLETE.
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    ASSERT_GL_NO_ERROR();
+
+    // testing copyTexImage2D
+
+    // copyTexImage2D to same texture but different level
+    glBindTexture(GL_TEXTURE_2D, texture.get());
+    glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
+    EXPECT_GL_NO_ERROR();
+
+    // copyTexImage2D to same texture same level, invalid feedback loop
+    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // copyTexImage2D to different texture
+    glBindTexture(GL_TEXTURE_2D, texture2.get());
+    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
+    EXPECT_GL_NO_ERROR();
+
+    // testing copyTexSubImage2D
+
+    // copyTexSubImage2D to same texture but different level
+    glBindTexture(GL_TEXTURE_2D, texture.get());
+    glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
+    EXPECT_GL_NO_ERROR();
+
+    // copyTexSubImage2D to same texture same level, invalid feedback loop
+    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // copyTexSubImage2D to different texture
+    glBindTexture(GL_TEXTURE_2D, texture2.get());
+    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+    EXPECT_GL_NO_ERROR();
+}
+
+void WebGLCompatibilityTest::drawBuffersFeedbackLoop(GLuint program,
+                                                     const std::array<GLenum, 2> &drawBuffers,
+                                                     GLenum expectedError)
+{
+    glDrawBuffers(2, drawBuffers.data());
+
+    // Make sure framebuffer is complete before feedback loop detection
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+    drawQuad(program, "aPosition", 0.5f, 1.0f, true);
+
+    // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
+    // it should be NO_ERROR"
+    EXPECT_GL_ERROR(expectedError);
+}
+
+// This tests that rendering feedback loops works as expected with WebGL 2.
+// Based on WebGL test conformance2/rendering/rendering-sampling-feedback-loop.html
+TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDrawBuffers)
+{
+    const std::string vertexShader =
+        "#version 300 es\n"
+        "in vec4 aPosition;\n"
+        "out vec2 texCoord;\n"
+        "void main() {\n"
+        "    gl_Position = aPosition;\n"
+        "    texCoord = (aPosition.xy * 0.5) + 0.5;\n"
+        "}\n";
+
+    const std::string fragmentShader =
+        "#version 300 es\n"
+        "precision mediump float;\n"
+        "uniform sampler2D tex;\n"
+        "in vec2 texCoord;\n"
+        "out vec4 oColor;\n"
+        "void main() {\n"
+        "    oColor = texture(tex, texCoord);\n"
+        "}\n";
+
+    GLsizei width  = 8;
+    GLsizei height = 8;
+
+    GLint maxDrawBuffers = 0;
+    glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+    // ES3 requires a minimum value of 4 for MAX_DRAW_BUFFERS.
+    ASSERT_GE(maxDrawBuffers, 2);
+
+    ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
+    glUseProgram(program.get());
+    glViewport(0, 0, width, height);
+
+    GLTexture tex0;
+    GLTexture tex1;
+    GLFramebuffer fbo;
+    FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
+    FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
+    ASSERT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_2D, tex1.get());
+    GLint texLoc = glGetUniformLocation(program.get(), "tex");
+    ASSERT_NE(-1, texLoc);
+    glUniform1i(texLoc, 0);
+
+    // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
+    ASSERT_GL_NO_ERROR();
+
+    drawBuffersFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}}, GL_INVALID_OPERATION);
+    drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
+                            GL_INVALID_OPERATION);
+    drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
 // tests should be run against.
 ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
@@ -874,9 +954,5 @@
                        ES2_OPENGLES(),
                        ES3_OPENGLES());
 
-ANGLE_INSTANTIATE_TEST(WebGL2CompatibilityTest,
-                       ES3_D3D11(),
-                       ES3_OPENGL(),
-                       ES3_OPENGLES());
-
+ANGLE_INSTANTIATE_TEST(WebGL2CompatibilityTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
 }  // namespace