| // |
| // Copyright 2015 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. |
| // |
| // StateChangeTest: |
| // Specifically designed for an ANGLE implementation of GL, these tests validate that |
| // ANGLE's dirty bits systems don't get confused by certain sequences of state changes. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| #include "util/random_utils.h" |
| |
| #include <thread> |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class StateChangeTest : public ANGLETest<> |
| { |
| protected: |
| StateChangeTest() |
| { |
| setWindowWidth(64); |
| setWindowHeight(64); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| setConfigDepthBits(24); |
| setConfigStencilBits(8); |
| |
| // Enable the no error extension to avoid syncing the FBO state on validation. |
| setNoErrorEnabled(true); |
| } |
| |
| void testSetUp() override |
| { |
| glGenFramebuffers(1, &mFramebuffer); |
| glGenTextures(2, mTextures.data()); |
| glGenRenderbuffers(1, &mRenderbuffer); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| if (mFramebuffer != 0) |
| { |
| glDeleteFramebuffers(1, &mFramebuffer); |
| mFramebuffer = 0; |
| } |
| |
| if (!mTextures.empty()) |
| { |
| glDeleteTextures(static_cast<GLsizei>(mTextures.size()), mTextures.data()); |
| mTextures.clear(); |
| } |
| |
| glDeleteRenderbuffers(1, &mRenderbuffer); |
| } |
| |
| GLuint mFramebuffer = 0; |
| GLuint mRenderbuffer = 0; |
| std::vector<GLuint> mTextures = {0, 0}; |
| }; |
| |
| class StateChangeTestES3 : public StateChangeTest |
| { |
| protected: |
| StateChangeTestES3() {} |
| }; |
| |
| // Ensure that CopyTexImage2D syncs framebuffer changes. |
| TEST_P(StateChangeTest, CopyTexImage2DSync) |
| { |
| // TODO(geofflang): Fix on Linux AMD drivers (http://anglebug.com/1291) |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL()); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Copy in the red texture to the green one. |
| // CopyTexImage should sync the framebuffer attachment change. |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 16, 16, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that CopyTexSubImage2D syncs framebuffer changes. |
| TEST_P(StateChangeTest, CopyTexSubImage2DSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Copy in the red texture to the green one. |
| // CopyTexImage should sync the framebuffer attachment change. |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 16, 16); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that Framebuffer completeness caching works when color attachments change. |
| TEST_P(StateChangeTest, FramebufferIncompleteColorAttachment) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Change the texture at color attachment 0 to be non-color-renderable. |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 16, 16, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that caching works when color attachments change with TexStorage. |
| TEST_P(StateChangeTest, FramebufferIncompleteWithTexStorage) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_storage")); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Change the texture at color attachment 0 to be non-color-renderable. |
| glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_ALPHA8_EXT, 16, 16); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that caching works when color attachments change with CompressedTexImage2D. |
| TEST_P(StateChangeTestES3, FramebufferIncompleteWithCompressedTex) |
| { |
| // ETC texture formats are not supported on Mac OpenGL. http://anglebug.com/3853 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsDesktopOpenGL()); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Change the texture at color attachment 0 to be non-color-renderable. |
| glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB8_ETC2, 16, 16, 0, 128, nullptr); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that caching works when color attachments are deleted. |
| TEST_P(StateChangeTestES3, FramebufferIncompleteWhenAttachmentDeleted) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Delete the texture at color attachment 0. |
| glDeleteTextures(1, &mTextures[0]); |
| mTextures[0] = 0; |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that Framebuffer completeness caching works when depth attachments change. |
| TEST_P(StateChangeTest, FramebufferIncompleteDepthAttachment) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 16, 16); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mRenderbuffer); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Change the texture at color attachment 0 to be non-depth-renderable. |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that Framebuffer completeness caching works when stencil attachments change. |
| TEST_P(StateChangeTest, FramebufferIncompleteStencilAttachment) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, 16, 16); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, |
| mRenderbuffer); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Change the texture at the stencil attachment to be non-stencil-renderable. |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that Framebuffer completeness caching works when depth-stencil attachments change. |
| TEST_P(StateChangeTestES3, FramebufferIncompleteDepthStencilAttachment) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 16, 16); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, |
| mRenderbuffer); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Change the texture the depth-stencil attachment to be non-depth-stencil-renderable. |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that enabling GL_SAMPLE_ALPHA_TO_COVERAGE doesn't generate errors. |
| TEST_P(StateChangeTest, AlphaToCoverageEnable) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 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); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| |
| // We don't actually care that this does anything, just that it can be enabled without causing |
| // an error. |
| glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); |
| |
| glUseProgram(greenProgram); |
| drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| const char kSimpleAttributeVS[] = R"(attribute vec2 position; |
| attribute vec4 testAttrib; |
| varying vec4 testVarying; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| testVarying = testAttrib; |
| })"; |
| |
| const char kSimpleAttributeFS[] = R"(precision mediump float; |
| varying vec4 testVarying; |
| void main() |
| { |
| gl_FragColor = testVarying; |
| })"; |
| |
| // Tests that using a buffered attribute, then disabling it and using current value, works. |
| TEST_P(StateChangeTest, DisablingBufferedVertexAttribute) |
| { |
| ANGLE_GL_PROGRAM(program, kSimpleAttributeVS, kSimpleAttributeFS); |
| glUseProgram(program); |
| GLint attribLoc = glGetAttribLocation(program, "testAttrib"); |
| GLint positionLoc = glGetAttribLocation(program, "position"); |
| ASSERT_NE(-1, attribLoc); |
| ASSERT_NE(-1, positionLoc); |
| |
| // Set up the buffered attribute. |
| std::vector<GLColor> red(6, GLColor::red); |
| GLBuffer attribBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, attribBuffer); |
| glBufferData(GL_ARRAY_BUFFER, red.size() * sizeof(GLColor), red.data(), GL_STATIC_DRAW); |
| glEnableVertexAttribArray(attribLoc); |
| glVertexAttribPointer(attribLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr); |
| |
| // Also set the current value to green now. |
| glVertexAttrib4f(attribLoc, 0.0f, 1.0f, 0.0f, 1.0f); |
| |
| // Set up the position attribute as well. |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| |
| // Draw with the buffered attribute. Verify red. |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw with the disabled "current value attribute". Verify green. |
| glDisableVertexAttribArray(attribLoc); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Verify setting buffer data on the disabled buffer doesn't change anything. |
| std::vector<GLColor> blue(128, GLColor::blue); |
| glBindBuffer(GL_ARRAY_BUFFER, attribBuffer); |
| glBufferData(GL_ARRAY_BUFFER, blue.size() * sizeof(GLColor), blue.data(), GL_STATIC_DRAW); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests that setting value for a subset of default attributes doesn't affect others. |
| TEST_P(StateChangeTest, SetCurrentAttribute) |
| { |
| constexpr char kVS[] = R"(attribute vec4 position; |
| attribute mat4 testAttrib; // Note that this generates 4 attributes |
| varying vec4 testVarying; |
| void main (void) |
| { |
| gl_Position = position; |
| |
| testVarying = position.y < 0.0 ? |
| position.x < 0.0 ? testAttrib[0] : testAttrib[1] : |
| position.x < 0.0 ? testAttrib[2] : testAttrib[3]; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kSimpleAttributeFS); |
| glUseProgram(program); |
| GLint attribLoc = glGetAttribLocation(program, "testAttrib"); |
| GLint positionLoc = glGetAttribLocation(program, "position"); |
| ASSERT_NE(-1, attribLoc); |
| ASSERT_NE(-1, positionLoc); |
| |
| // Set the current value of two of the test attributes, while leaving the other two as default. |
| glVertexAttrib4f(attribLoc + 1, 0.0f, 1.0f, 0.0f, 1.0f); |
| glVertexAttrib4f(attribLoc + 2, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Set up the position attribute. |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| |
| // Draw and verify the four section in the output: |
| // |
| // +---------------+ |
| // | Black | Green | |
| // +-------+-------+ |
| // | Blue | Black | |
| // +---------------+ |
| // |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| const int w = getWindowWidth(); |
| const int h = getWindowHeight(); |
| constexpr unsigned int kPixelTolerance = 5u; |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor::black, kPixelTolerance); |
| EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, GLColor::green, kPixelTolerance); |
| EXPECT_PIXEL_COLOR_NEAR(0, h - 1, GLColor::blue, kPixelTolerance); |
| EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, GLColor::black, kPixelTolerance); |
| } |
| |
| // Tests that drawing with transform feedback paused, then lines without transform feedback works |
| // without Vulkan validation errors. |
| TEST_P(StateChangeTestES3, DrawPausedXfbThenNonXfbLines) |
| { |
| // glTransformFeedbackVaryings for program2 returns GL_INVALID_OPERATION on both Linux and |
| // windows. http://anglebug.com/4265 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsOpenGL()); |
| // http://anglebug.com/5388 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL()); |
| |
| std::vector<std::string> tfVaryings = {"gl_Position"}; |
| ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program1, essl1_shaders::vs::Simple(), |
| essl1_shaders::fs::Blue(), tfVaryings, GL_SEPARATE_ATTRIBS); |
| |
| GLBuffer xfbBuffer; |
| glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer); |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 6 * sizeof(float[4]), nullptr, GL_STATIC_DRAW); |
| |
| GLTransformFeedback xfb; |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer); |
| |
| glUseProgram(program1); |
| glBeginTransformFeedback(GL_TRIANGLES); |
| glPauseTransformFeedback(); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| glUseProgram(program2); |
| glDrawArrays(GL_LINES, 0, 6); |
| glEndTransformFeedback(); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Tests that vertex attribute value is preserved across context switches. |
| TEST_P(StateChangeTest, MultiContextVertexAttribute) |
| { |
| EGLWindow *window = getEGLWindow(); |
| EGLDisplay display = window->getDisplay(); |
| EGLConfig config = window->getConfig(); |
| EGLSurface surface = window->getSurface(); |
| EGLContext context1 = window->getContext(); |
| |
| // Set up program in primary context |
| ANGLE_GL_PROGRAM(program1, kSimpleAttributeVS, kSimpleAttributeFS); |
| glUseProgram(program1); |
| GLint attribLoc = glGetAttribLocation(program1, "testAttrib"); |
| GLint positionLoc = glGetAttribLocation(program1, "position"); |
| ASSERT_NE(-1, attribLoc); |
| ASSERT_NE(-1, positionLoc); |
| |
| // Set up the position attribute in primary context |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| |
| // Set primary context attribute to green and draw quad |
| glVertexAttrib4f(attribLoc, 0.0f, 1.0f, 0.0f, 1.0f); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Set up and switch to secondary context |
| EGLint contextAttributes[] = { |
| EGL_CONTEXT_MAJOR_VERSION_KHR, |
| GetParam().majorVersion, |
| EGL_CONTEXT_MINOR_VERSION_KHR, |
| GetParam().minorVersion, |
| EGL_NONE, |
| }; |
| EGLContext context2 = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); |
| ASSERT_NE(context2, EGL_NO_CONTEXT); |
| eglMakeCurrent(display, surface, surface, context2); |
| |
| // Set up program in secondary context |
| ANGLE_GL_PROGRAM(program2, kSimpleAttributeVS, kSimpleAttributeFS); |
| glUseProgram(program2); |
| ASSERT_EQ(attribLoc, glGetAttribLocation(program2, "testAttrib")); |
| ASSERT_EQ(positionLoc, glGetAttribLocation(program2, "position")); |
| |
| // Set up the position attribute in secondary context |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| |
| // attribLoc current value should be default - (0,0,0,1) |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| |
| // Restore primary context |
| eglMakeCurrent(display, surface, surface, context1); |
| // ReadPixels to ensure context is switched |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| |
| // Switch to secondary context second time |
| eglMakeCurrent(display, surface, surface, context2); |
| // Check that it still draws black |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| |
| // Restore primary context second time |
| eglMakeCurrent(display, surface, surface, context1); |
| // Check if it still draws green |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Clean up |
| eglDestroyContext(display, context2); |
| } |
| |
| // Ensure that CopyTexSubImage3D syncs framebuffer changes. |
| TEST_P(StateChangeTestES3, CopyTexSubImage3DSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| glBindTexture(GL_TEXTURE_3D, mTextures[0]); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_3D, mTextures[1]); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Copy in the red texture to the green one. |
| // CopyTexImage should sync the framebuffer attachment change. |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0); |
| glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 16, 16); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that BlitFramebuffer syncs framebuffer changes. |
| TEST_P(StateChangeTestES3, BlitFramebufferSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Change to the red textures and blit. |
| // BlitFramebuffer should sync the framebuffer attachment change. |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], |
| 0); |
| glBlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that ReadBuffer and DrawBuffers sync framebuffer changes. |
| TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Initialize two FBO attachments |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], 0); |
| |
| // Clear first attachment to red |
| GLenum bufs1[] = {GL_COLOR_ATTACHMENT0, GL_NONE}; |
| glDrawBuffers(2, bufs1); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Clear second texture to green |
| GLenum bufs2[] = {GL_NONE, GL_COLOR_ATTACHMENT1}; |
| glDrawBuffers(2, bufs2); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Verify first attachment is red and second is green |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Tests calling invalidate on incomplete framebuffers after switching attachments. |
| // Adapted partially from WebGL 2 test "renderbuffers/invalidate-framebuffer" |
| TEST_P(StateChangeTestES3, IncompleteRenderbufferAttachmentInvalidateSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); |
| GLint samples = 0; |
| glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, 1, &samples); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer); |
| ASSERT_GL_NO_ERROR(); |
| |
| // invalidate the framebuffer when the attachment is incomplete: no storage allocated to the |
| // attached renderbuffer |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, |
| glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| GLenum attachments1[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1); |
| ASSERT_GL_NO_ERROR(); |
| |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, static_cast<GLsizei>(samples), GL_RGBA8, |
| getWindowWidth(), getWindowHeight()); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLRenderbuffer renderbuf; |
| |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuf.get()); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, |
| renderbuf.get()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // invalidate the framebuffer when the attachment is incomplete: no storage allocated to the |
| // attached renderbuffer |
| // Note: the bug will only repro *without* a call to checkStatus before the invalidate. |
| GLenum attachments2[] = {GL_DEPTH_ATTACHMENT}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments2); |
| |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, static_cast<GLsizei>(samples), |
| GL_DEPTH_COMPONENT16, getWindowWidth(), getWindowHeight()); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| class StateChangeRenderTest : public StateChangeTest |
| { |
| protected: |
| StateChangeRenderTest() : mProgram(0), mRenderbuffer(0) {} |
| |
| void testSetUp() override |
| { |
| StateChangeTest::testSetUp(); |
| |
| constexpr char kVS[] = |
| "attribute vec2 position;\n" |
| "void main() {\n" |
| " gl_Position = vec4(position, 0, 1);\n" |
| "}"; |
| constexpr char kFS[] = |
| "uniform highp vec4 uniformColor;\n" |
| "void main() {\n" |
| " gl_FragColor = uniformColor;\n" |
| "}"; |
| |
| mProgram = CompileProgram(kVS, kFS); |
| ASSERT_NE(0u, mProgram); |
| |
| glGenRenderbuffers(1, &mRenderbuffer); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(mProgram); |
| glDeleteRenderbuffers(1, &mRenderbuffer); |
| |
| StateChangeTest::testTearDown(); |
| } |
| |
| void setUniformColor(const GLColor &color) |
| { |
| glUseProgram(mProgram); |
| const Vector4 &normalizedColor = color.toNormalizedVector(); |
| GLint uniformLocation = glGetUniformLocation(mProgram, "uniformColor"); |
| ASSERT_NE(-1, uniformLocation); |
| glUniform4fv(uniformLocation, 1, normalizedColor.data()); |
| } |
| |
| GLuint mProgram; |
| GLuint mRenderbuffer; |
| }; |
| |
| // Test that re-creating a currently attached texture works as expected. |
| TEST_P(StateChangeRenderTest, RecreateTexture) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| |
| // Explictly check FBO status sync in some versions of ANGLE no_error skips FBO checks. |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Draw with red to the FBO. |
| GLColor red(255, 0, 0, 255); |
| setUniformColor(red); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, red); |
| |
| // Recreate the texture with green. |
| GLColor green(0, 255, 0, 255); |
| std::vector<GLColor> greenPixels(32 * 32, green); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| greenPixels.data()); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, green); |
| |
| // Explictly check FBO status sync in some versions of ANGLE no_error skips FBO checks. |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Verify drawing blue gives blue. This covers the FBO sync with D3D dirty bits. |
| GLColor blue(0, 0, 255, 255); |
| setUniformColor(blue); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, blue); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that re-creating a currently attached renderbuffer works as expected. |
| TEST_P(StateChangeRenderTest, RecreateRenderbuffer) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer); |
| |
| // Explictly check FBO status sync in some versions of ANGLE no_error skips FBO checks. |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw with red to the FBO. |
| setUniformColor(GLColor::red); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Recreate the renderbuffer and clear to green. |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 32, 32); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Explictly check FBO status sync in some versions of ANGLE no_error skips FBO checks. |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Verify drawing blue gives blue. This covers the FBO sync with D3D dirty bits. |
| setUniformColor(GLColor::blue); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test that recreating a texture with GenerateMipmaps signals the FBO is dirty. |
| TEST_P(StateChangeRenderTest, GenerateMipmap) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| |
| // Explictly check FBO status sync in some versions of ANGLE no_error skips FBO checks. |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Draw once to set the RenderTarget in D3D11 |
| GLColor red(255, 0, 0, 255); |
| setUniformColor(red); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, red); |
| |
| // This may trigger the texture to be re-created internally. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| |
| // Explictly check FBO status sync in some versions of ANGLE no_error skips FBO checks. |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // Now ensure we don't have a stale render target. |
| GLColor blue(0, 0, 255, 255); |
| setUniformColor(blue); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, blue); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Tests that gl_DepthRange syncs correctly after a change. |
| TEST_P(StateChangeRenderTest, DepthRangeUpdates) |
| { |
| constexpr char kFragCoordShader[] = R"(void main() |
| { |
| if (gl_DepthRange.near == 0.2) |
| { |
| gl_FragColor = vec4(1, 0, 0, 1); |
| } |
| else if (gl_DepthRange.near == 0.5) |
| { |
| gl_FragColor = vec4(0, 1, 0, 1); |
| } |
| else |
| { |
| gl_FragColor = vec4(0, 0, 1, 1); |
| } |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragCoordShader); |
| glUseProgram(program); |
| |
| const auto &quadVertices = GetQuadVertices(); |
| |
| ASSERT_EQ(0, glGetAttribLocation(program, essl1_shaders::PositionAttrib())); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, quadVertices.size() * sizeof(quadVertices[0]), |
| quadVertices.data(), GL_STATIC_DRAW); |
| glVertexAttribPointer(0u, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(0u); |
| |
| // First, clear. |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Draw to left half viewport with a first depth range. |
| glDepthRangef(0.2f, 1.0f); |
| glViewport(0, 0, getWindowWidth() / 2, getWindowHeight()); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw to right half viewport with a second depth range. |
| glDepthRangef(0.5f, 1.0f); |
| glViewport(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight()); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify left half of the framebuffer is red and right half is green. |
| EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth() / 2, getWindowHeight(), GLColor::red); |
| EXPECT_PIXEL_RECT_EQ(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight(), |
| GLColor::green); |
| } |
| |
| class StateChangeRenderTestES3 : public StateChangeRenderTest |
| {}; |
| |
| TEST_P(StateChangeRenderTestES3, InvalidateNonCurrentFramebuffer) |
| { |
| glBindTexture(GL_TEXTURE_2D, mTextures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw with red to the FBO. |
| GLColor red(255, 0, 0, 255); |
| setUniformColor(red); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, red); |
| |
| // Go back to default framebuffer, draw green |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| GLColor green(0, 255, 0, 255); |
| setUniformColor(green); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, green); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Invalidate color buffer of FBO |
| GLenum attachments1[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify drawing blue gives blue. |
| GLColor blue(0, 0, 255, 255); |
| setUniformColor(blue); |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, blue); |
| } |
| |
| // Tests that D3D11 dirty bit updates don't forget about BufferSubData attrib updates. |
| TEST_P(StateChangeTest, VertexBufferUpdatedAfterDraw) |
| { |
| constexpr char kVS[] = |
| "attribute vec2 position;\n" |
| "attribute vec4 color;\n" |
| "varying vec4 outcolor;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(position, 0, 1);\n" |
| " outcolor = color;\n" |
| "}"; |
| constexpr char kFS[] = |
| "varying mediump vec4 outcolor;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = outcolor;\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| |
| GLint colorLoc = glGetAttribLocation(program, "color"); |
| ASSERT_NE(-1, colorLoc); |
| GLint positionLoc = glGetAttribLocation(program, "position"); |
| ASSERT_NE(-1, positionLoc); |
| |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| |
| GLBuffer colorBuf; |
| glBindBuffer(GL_ARRAY_BUFFER, colorBuf); |
| glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr); |
| glEnableVertexAttribArray(colorLoc); |
| |
| // Fill with green. |
| std::vector<GLColor> colorData(6, GLColor::green); |
| glBufferData(GL_ARRAY_BUFFER, colorData.size() * sizeof(GLColor), colorData.data(), |
| GL_STATIC_DRAW); |
| |
| // Draw, expect green. |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Update buffer with red. |
| std::fill(colorData.begin(), colorData.end(), GLColor::red); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, colorData.size() * sizeof(GLColor), colorData.data()); |
| |
| // Draw, expect red. |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Tests that drawing after flush without any state change works. |
| TEST_P(StateChangeTestES3, DrawAfterFlushWithNoStateChange) |
| { |
| // Draw (0.125, 0.25, 0.5, 0.5) once, using additive blend |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| GLint positionLocation = glGetAttribLocation(drawColor, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| // Setup VAO |
| const auto &quadVertices = GetQuadVertices(); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 6, quadVertices.data(), GL_STATIC_DRAW); |
| |
| GLVertexArray vertexArray; |
| glBindVertexArray(vertexArray); |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clear and draw |
| glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| glUniform4f(colorUniformLocation, 0.125f, 0.25f, 0.5f, 0.5f); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Make sure the work is submitted. |
| glFinish(); |
| |
| // Draw again with no state change |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Make sure the pixels have the correct colors. |
| const int h = getWindowHeight() - 1; |
| const int w = getWindowWidth() - 1; |
| const GLColor kExpected(63, 127, 255, 255); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1); |
| EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, kExpected, 1); |
| EXPECT_PIXEL_COLOR_NEAR(0, h - 1, kExpected, 1); |
| EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, kExpected, 1); |
| } |
| |
| // Test that switching VAOs keeps the disabled "current value" attributes up-to-date. |
| TEST_P(StateChangeTestES3, VertexArrayObjectAndDisabledAttributes) |
| { |
| constexpr char kSingleVS[] = "attribute vec4 position; void main() { gl_Position = position; }"; |
| constexpr char kSingleFS[] = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }"; |
| ANGLE_GL_PROGRAM(singleProgram, kSingleVS, kSingleFS); |
| |
| constexpr char kDualVS[] = |
| "#version 300 es\n" |
| "in vec4 position;\n" |
| "in vec4 color;\n" |
| "out vec4 varyColor;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| " varyColor = color;\n" |
| "}"; |
| constexpr char kDualFS[] = |
| "#version 300 es\n" |
| "precision mediump float;\n" |
| "in vec4 varyColor;\n" |
| "out vec4 colorOut;\n" |
| "void main()\n" |
| "{\n" |
| " colorOut = varyColor;\n" |
| "}"; |
| |
| ANGLE_GL_PROGRAM(dualProgram, kDualVS, kDualFS); |
| |
| // Force consistent attribute locations |
| constexpr GLint positionLocation = 0; |
| constexpr GLint colorLocation = 1; |
| |
| glBindAttribLocation(singleProgram, positionLocation, "position"); |
| glBindAttribLocation(dualProgram, positionLocation, "position"); |
| glBindAttribLocation(dualProgram, colorLocation, "color"); |
| |
| { |
| glLinkProgram(singleProgram); |
| GLint linkStatus; |
| glGetProgramiv(singleProgram, GL_LINK_STATUS, &linkStatus); |
| ASSERT_NE(linkStatus, 0); |
| } |
| |
| { |
| glLinkProgram(dualProgram); |
| GLint linkStatus; |
| glGetProgramiv(dualProgram, GL_LINK_STATUS, &linkStatus); |
| ASSERT_NE(linkStatus, 0); |
| } |
| |
| glUseProgram(singleProgram); |
| |
| // Initialize position vertex buffer. |
| const auto &quadVertices = GetQuadVertices(); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 6, quadVertices.data(), GL_STATIC_DRAW); |
| |
| // Initialize a VAO. Draw with single program. |
| GLVertexArray vertexArray; |
| glBindVertexArray(vertexArray); |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Should draw red. |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw with a green buffer attribute, without the VAO. |
| glBindVertexArray(0); |
| glUseProgram(dualProgram); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| std::vector<GLColor> greenColors(6, GLColor::green); |
| GLBuffer greenBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, greenBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLColor) * 6, greenColors.data(), GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(colorLocation, 4, GL_UNSIGNED_BYTE, GL_FALSE, 4, nullptr); |
| glEnableVertexAttribArray(colorLocation); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Re-bind VAO and try to draw with different program, without changing state. |
| // Should draw black since current value is not initialized. |
| glBindVertexArray(vertexArray); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| } |
| |
| const char kSamplerMetadataVertexShader0[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| uniform sampler2D texture; |
| void main() |
| { |
| vec2 size = vec2(textureSize(texture, 0)); |
| color = size.x != 0.0 ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 0.0); |
| vec2 pos = vec2(0.0); |
| switch (gl_VertexID) { |
| case 0: pos = vec2(-1.0, -1.0); break; |
| case 1: pos = vec2(3.0, -1.0); break; |
| case 2: pos = vec2(-1.0, 3.0); break; |
| }; |
| gl_Position = vec4(pos, 0.0, 1.0); |
| })"; |
| |
| const char kSamplerMetadataVertexShader1[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| uniform sampler2D texture1; |
| uniform sampler2D texture2; |
| void main() |
| { |
| vec2 size1 = vec2(textureSize(texture1, 0)); |
| vec2 size2 = vec2(textureSize(texture2, 0)); |
| color = size1.x * size2.x != 0.0 ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 0.0); |
| vec2 pos = vec2(0.0); |
| switch (gl_VertexID) { |
| case 0: pos = vec2(-1.0, -1.0); break; |
| case 1: pos = vec2(3.0, -1.0); break; |
| case 2: pos = vec2(-1.0, 3.0); break; |
| }; |
| gl_Position = vec4(pos, 0.0, 1.0); |
| })"; |
| |
| const char kSamplerMetadataFragmentShader[] = R"(#version 300 es |
| precision mediump float; |
| in vec4 color; |
| out vec4 result; |
| void main() |
| { |
| result = color; |
| })"; |
| |
| // Tests that changing an active program invalidates the sampler metadata properly. |
| TEST_P(StateChangeTestES3, SamplerMetadataUpdateOnSetProgram) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| // TODO(anglebug.com/5491) Appears as though there's something wrong with textureSize on iOS |
| // unrelated to switching programs. |
| ANGLE_SKIP_TEST_IF(IsIOS() && IsOpenGLES()); |
| GLVertexArray vertexArray; |
| glBindVertexArray(vertexArray); |
| |
| // Create a simple framebuffer. |
| GLTexture texture1, texture2; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glActiveTexture(GL_TEXTURE1); |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| |
| // Create 2 shader programs differing only in the number of active samplers. |
| ANGLE_GL_PROGRAM(program1, kSamplerMetadataVertexShader0, kSamplerMetadataFragmentShader); |
| glUseProgram(program1); |
| glUniform1i(glGetUniformLocation(program1, "texture"), 0); |
| ANGLE_GL_PROGRAM(program2, kSamplerMetadataVertexShader1, kSamplerMetadataFragmentShader); |
| glUseProgram(program2); |
| glUniform1i(glGetUniformLocation(program2, "texture1"), 0); |
| glUniform1i(glGetUniformLocation(program2, "texture2"), 0); |
| |
| // Draw a solid green color to the framebuffer. |
| glUseProgram(program1); |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| // Test that our first program is good. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Bind a different program that uses more samplers. |
| // Draw another quad that depends on the sampler metadata. |
| glUseProgram(program2); |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| // Flush via ReadPixels and check that it's still green. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Tests that redefining Buffer storage syncs with the Transform Feedback object. |
| TEST_P(StateChangeTestES3, RedefineTransformFeedbackBuffer) |
| { |
| // Create the most simple program possible - simple a passthrough for a float attribute. |
| constexpr char kVertexShader[] = R"(#version 300 es |
| in float valueIn; |
| out float valueOut; |
| void main() |
| { |
| gl_Position = vec4(0, 0, 0, 0); |
| valueOut = valueIn; |
| })"; |
| |
| constexpr char kFragmentShader[] = R"(#version 300 es |
| out mediump float unused; |
| void main() |
| { |
| unused = 1.0; |
| })"; |
| |
| std::vector<std::string> tfVaryings = {"valueOut"}; |
| ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program, kVertexShader, kFragmentShader, tfVaryings, |
| GL_SEPARATE_ATTRIBS); |
| glUseProgram(program); |
| |
| GLint attribLoc = glGetAttribLocation(program, "valueIn"); |
| ASSERT_NE(-1, attribLoc); |
| |
| // Disable rasterization - we're not interested in the framebuffer. |
| glEnable(GL_RASTERIZER_DISCARD); |
| |
| // Initialize a float vertex buffer with 1.0. |
| std::vector<GLfloat> data1(16, 1.0); |
| GLsizei size1 = static_cast<GLsizei>(sizeof(GLfloat) * data1.size()); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, size1, data1.data(), GL_STATIC_DRAW); |
| glVertexAttribPointer(attribLoc, 1, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(attribLoc); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Initialize a same-sized XFB buffer. |
| GLBuffer xfbBuffer; |
| glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer); |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, size1, nullptr, GL_STATIC_DRAW); |
| |
| // Draw with XFB enabled. |
| GLTransformFeedback xfb; |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer); |
| |
| glBeginTransformFeedback(GL_POINTS); |
| glDrawArrays(GL_POINTS, 0, 16); |
| glEndTransformFeedback(); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify the XFB stage caught the 1.0 attribute values. |
| void *mapped1 = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, size1, GL_MAP_READ_BIT); |
| GLfloat *asFloat1 = reinterpret_cast<GLfloat *>(mapped1); |
| std::vector<GLfloat> actualData1(asFloat1, asFloat1 + data1.size()); |
| EXPECT_EQ(data1, actualData1); |
| glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); |
| |
| // Now, reinitialize the XFB buffer to a larger size, and draw with 2.0. |
| std::vector<GLfloat> data2(128, 2.0); |
| const GLsizei size2 = static_cast<GLsizei>(sizeof(GLfloat) * data2.size()); |
| glBufferData(GL_ARRAY_BUFFER, size2, data2.data(), GL_STATIC_DRAW); |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, size2, nullptr, GL_STATIC_DRAW); |
| |
| glBeginTransformFeedback(GL_POINTS); |
| glDrawArrays(GL_POINTS, 0, 128); |
| glEndTransformFeedback(); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify the XFB stage caught the 2.0 attribute values. |
| void *mapped2 = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, size2, GL_MAP_READ_BIT); |
| GLfloat *asFloat2 = reinterpret_cast<GLfloat *>(mapped2); |
| std::vector<GLfloat> actualData2(asFloat2, asFloat2 + data2.size()); |
| EXPECT_EQ(data2, actualData2); |
| glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); |
| } |
| |
| // Variations: |
| // |
| // - bool: whether to use WebGL compatibility mode. |
| using StateChangeTestWebGL2Params = std::tuple<angle::PlatformParameters, bool>; |
| |
| std::string StateChangeTestWebGL2Print( |
| const ::testing::TestParamInfo<StateChangeTestWebGL2Params> ¶msInfo) |
| { |
| const StateChangeTestWebGL2Params ¶ms = paramsInfo.param; |
| std::ostringstream out; |
| |
| out << std::get<0>(params); |
| |
| if (std::get<1>(params)) |
| { |
| out << "__WebGLCompatibility"; |
| } |
| |
| return out.str(); |
| } |
| |
| // State change test verifying both ES3 and WebGL2 specific behaviors. |
| // Test is parameterized to allow execution with and without WebGL validation. |
| // Note that this can not inherit from StateChangeTest due to the need to use ANGLETestWithParam. |
| class StateChangeTestWebGL2 : public ANGLETest<StateChangeTestWebGL2Params> |
| { |
| protected: |
| StateChangeTestWebGL2() |
| { |
| setWindowWidth(kWidth); |
| setWindowHeight(kHeight); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| if (testing::get<1>(GetParam())) |
| setWebGLCompatibilityEnabled(true); |
| } |
| |
| struct TestResources |
| { |
| GLTexture colorTexture; |
| GLFramebuffer framebuffer; |
| }; |
| |
| void setupResources(TestResources &resources) |
| { |
| glBindTexture(GL_TEXTURE_2D, resources.colorTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, resources.framebuffer); |
| EXPECT_GL_NO_ERROR(); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| resources.colorTexture, 0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Use a larger window/framebuffer size than 1x1 (though not much larger) to |
| // increase the chances that random garbage will appear. |
| static constexpr GLsizei kWidth = 4; |
| static constexpr GLsizei kHeight = 4; |
| }; |
| |
| // Note: tested multiple other combinations: |
| // |
| // - Clearing/drawing to the framebuffer after invalidating, without using a |
| // secondary FBO |
| // - Clearing the framebuffer after invalidating, using a secondary FBO |
| // - Invalidating after clearing/drawing to the FBO, to verify WebGL's behavior |
| // that after invalidation, the framebuffer is either unmodified, or cleared |
| // to transparent black |
| // |
| // This combination, drawing after invalidating plus copying from the drawn-to |
| // texture, was the only one which provoked the original bug in the Metal |
| // backend with the following command line arguments: |
| // |
| // MTL_DEBUG_LAYER=1 MTL_DEBUG_LAYER_VALIDATE_LOAD_ACTIONS=1 \ |
| // MTL_DEBUG_LAYER_VALIDATE_STORE_ACTIONS=1 \ |
| // MTL_DEBUG_LAYER_VALIDATE_UNRETAINED_RESOURCES=4 \ |
| // angle_end2end_tests ... |
| // |
| // See anglebug.com/6923. |
| |
| TEST_P(StateChangeTestWebGL2, InvalidateThenDrawFBO) |
| { |
| GLint origFramebuffer = 0; |
| glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &origFramebuffer); |
| |
| TestResources resources; |
| setupResources(resources); |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green()); |
| |
| const GLenum attachment = GL_COLOR_ATTACHMENT0; |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, resources.framebuffer); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to red to start. |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Invalidate framebuffer. |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &attachment); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw green. |
| // Important to use a vertex buffer because WebGL doesn't support client-side arrays. |
| constexpr bool useVertexBuffer = true; |
| drawQuad(drawGreen.get(), essl3_shaders::PositionAttrib(), 0.5f, 1.0f, useVertexBuffer); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Bind original framebuffer. |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, origFramebuffer); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, resources.framebuffer); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Blit from user's framebuffer to the window. |
| // |
| // This step is crucial to catch bugs in the Metal backend's use of no-op load/store actions. |
| glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT, |
| GL_NEAREST); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Verify results. |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, origFramebuffer); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::green); |
| } |
| |
| // Simple state change tests for line loop drawing. There is some very specific handling of line |
| // line loops in Vulkan and we need to test switching between drawElements and drawArrays calls to |
| // validate every edge cases. |
| class LineLoopStateChangeTest : public StateChangeTest |
| { |
| protected: |
| LineLoopStateChangeTest() |
| { |
| setWindowWidth(32); |
| setWindowHeight(32); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void validateSquareAndHourglass() const |
| { |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Bottom left |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight, GLColor::blue); |
| |
| // Top left |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, (quarterHeight * 3), GLColor::blue); |
| |
| // Top right |
| // The last pixel isn't filled on a line loop so we check the pixel right before. |
| EXPECT_PIXEL_COLOR_EQ((quarterWidth * 3), (quarterHeight * 3) - 1, GLColor::blue); |
| |
| // dead center to validate the hourglass. |
| EXPECT_PIXEL_COLOR_EQ((quarterWidth * 2), quarterHeight * 2, GLColor::blue); |
| |
| // Verify line is closed between the 2 last vertices |
| EXPECT_PIXEL_COLOR_EQ((quarterWidth * 2), quarterHeight, GLColor::blue); |
| } |
| }; |
| |
| // Draw an hourglass with a drawElements call followed by a square with drawArrays. |
| TEST_P(LineLoopStateChangeTest, DrawElementsThenDrawArrays) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| glUseProgram(program); |
| |
| // We expect to draw a square with these 4 vertices with a drawArray call. |
| std::vector<Vector3> vertices; |
| CreatePixelCenterWindowCoords({{8, 8}, {8, 24}, {24, 24}, {24, 8}}, getWindowWidth(), |
| getWindowHeight(), &vertices); |
| |
| // If we use these indices to draw however, we should be drawing an hourglass. |
| auto indices = std::vector<GLushort>{0, 2, 1, 3}; |
| |
| GLint mPositionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, mPositionLocation); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| GLBuffer indexBuffer; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(mPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(mPositionLocation); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, nullptr); // hourglass |
| glDrawArrays(GL_LINE_LOOP, 0, 4); // square |
| glDisableVertexAttribArray(mPositionLocation); |
| |
| validateSquareAndHourglass(); |
| } |
| |
| // Draw line loop using a drawArrays followed by an hourglass with drawElements. |
| TEST_P(LineLoopStateChangeTest, DrawArraysThenDrawElements) |
| { |
| // http://anglebug.com/2856: Seems to fail on older drivers and pass on newer. |
| // Tested failing on 18.3.3 and passing on 18.9.2. |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsVulkan() && IsWindows()); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| glUseProgram(program); |
| |
| // We expect to draw a square with these 4 vertices with a drawArray call. |
| std::vector<Vector3> vertices; |
| CreatePixelCenterWindowCoords({{8, 8}, {8, 24}, {24, 24}, {24, 8}}, getWindowWidth(), |
| getWindowHeight(), &vertices); |
| |
| // If we use these indices to draw however, we should be drawing an hourglass. |
| auto indices = std::vector<GLushort>{0, 2, 1, 3}; |
| |
| GLint mPositionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, mPositionLocation); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| GLBuffer indexBuffer; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(mPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(mPositionLocation); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDrawArrays(GL_LINE_LOOP, 0, 4); // square |
| glDrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, nullptr); // hourglass |
| glDisableVertexAttribArray(mPositionLocation); |
| |
| validateSquareAndHourglass(); |
| } |
| |
| // Draw a triangle with a drawElements call and a non-zero offset and draw the same |
| // triangle with the same offset again followed by a line loop with drawElements. |
| TEST_P(LineLoopStateChangeTest, DrawElementsThenDrawElements) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| |
| glUseProgram(program); |
| |
| // Background Red color |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // We expect to draw a triangle with the last three points on the bottom right, |
| // draw with LineLoop, and then draw a triangle with the same non-zero offset. |
| auto vertices = std::vector<Vector3>{ |
| {-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}}; |
| |
| auto indices = std::vector<GLushort>{0, 1, 2, 1, 2, 3}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLBuffer indexBuffer; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], |
| GL_STATIC_DRAW); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Draw a triangle with a non-zero offset on the bottom right. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort))); |
| |
| // Draw with LineLoop. |
| glDrawElements(GL_LINE_LOOP, 3, GL_UNSIGNED_SHORT, nullptr); |
| |
| // Draw the triangle again with the same offset. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort))); |
| |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Validate the top left point's color. |
| EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() - 1, GLColor::blue); |
| |
| // Validate the triangle is drawn on the bottom right. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue); |
| |
| // Validate the triangle is NOT on the top left part. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::red); |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::red); |
| } |
| |
| // Simple state change tests, primarily focused on basic object lifetime and dependency management |
| // with back-ends that don't support that automatically (i.e. Vulkan). |
| class SimpleStateChangeTest : public ANGLETest<> |
| { |
| protected: |
| static constexpr int kWindowSize = 64; |
| |
| SimpleStateChangeTest() |
| { |
| setWindowWidth(kWindowSize); |
| setWindowHeight(kWindowSize); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void simpleDrawWithBuffer(GLBuffer *buffer); |
| void simpleDrawWithColor(const GLColor &color); |
| |
| using UpdateFunc = std::function<void(GLenum, GLTexture *, GLint, GLint, const GLColor &)>; |
| void updateTextureBoundToFramebufferHelper(UpdateFunc updateFunc); |
| void bindTextureToFbo(GLFramebuffer &fbo, GLTexture &texture); |
| void drawToFboWithCulling(const GLenum frontFace, bool earlyFrontFaceDirty); |
| }; |
| |
| class SimpleStateChangeTestES3 : public SimpleStateChangeTest |
| { |
| protected: |
| void blendAndVerifyColor(const GLColor32F blendColor, const GLColor expectedColor) |
| { |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| EXPECT_GL_NO_ERROR(); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(program); |
| |
| GLint colorUniformLocation = |
| glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, blendColor.R, blendColor.G, blendColor.B, blendColor.A); |
| EXPECT_GL_NO_ERROR(); |
| |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 1); |
| } |
| }; |
| |
| class SimpleStateChangeTestES31 : public SimpleStateChangeTestES3 |
| {}; |
| |
| class SimpleStateChangeTestComputeES31 : public SimpleStateChangeTest |
| { |
| protected: |
| void testSetUp() override |
| { |
| glGenFramebuffers(1, &mFramebuffer); |
| glGenTextures(1, &mTexture); |
| |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 2, 2); |
| EXPECT_GL_NO_ERROR(); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| layout(local_size_x=2, local_size_y=2) in; |
| layout (rgba8, binding = 0) readonly uniform highp image2D srcImage; |
| layout (rgba8, binding = 1) writeonly uniform highp image2D dstImage; |
| void main() |
| { |
| imageStore(dstImage, ivec2(gl_LocalInvocationID.xy), |
| imageLoad(srcImage, ivec2(gl_LocalInvocationID.xy))); |
| })"; |
| |
| mProgram = CompileComputeProgram(kCS); |
| ASSERT_NE(mProgram, 0u); |
| |
| glBindImageTexture(1, mTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); |
| |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebuffer); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, |
| 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| if (mFramebuffer != 0) |
| { |
| glDeleteFramebuffers(1, &mFramebuffer); |
| mFramebuffer = 0; |
| } |
| |
| if (mTexture != 0) |
| { |
| glDeleteTextures(1, &mTexture); |
| mTexture = 0; |
| } |
| glDeleteProgram(mProgram); |
| } |
| |
| GLuint mProgram; |
| GLuint mFramebuffer = 0; |
| GLuint mTexture = 0; |
| }; |
| |
| class ImageES31PPO |
| { |
| protected: |
| ImageES31PPO() : mComputeProg(0), mPipeline(0) {} |
| |
| void bindProgramPipeline(const GLchar *computeString) |
| { |
| mComputeProg = glCreateShaderProgramv(GL_COMPUTE_SHADER, 1, &computeString); |
| ASSERT_NE(mComputeProg, 0u); |
| |
| // Generate a program pipeline and attach the programs to their respective stages |
| glGenProgramPipelines(1, &mPipeline); |
| EXPECT_GL_NO_ERROR(); |
| glUseProgramStages(mPipeline, GL_COMPUTE_SHADER_BIT, mComputeProg); |
| EXPECT_GL_NO_ERROR(); |
| glBindProgramPipeline(mPipeline); |
| EXPECT_GL_NO_ERROR(); |
| glActiveShaderProgram(mPipeline, mComputeProg); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| GLuint mComputeProg; |
| GLuint mPipeline; |
| }; |
| |
| class SimpleStateChangeTestComputeES31PPO : public ImageES31PPO, public SimpleStateChangeTest |
| { |
| protected: |
| SimpleStateChangeTestComputeES31PPO() : ImageES31PPO(), SimpleStateChangeTest() {} |
| |
| void testTearDown() override |
| { |
| if (mFramebuffer != 0) |
| { |
| glDeleteFramebuffers(1, &mFramebuffer); |
| mFramebuffer = 0; |
| } |
| |
| if (mTexture != 0) |
| { |
| glDeleteTextures(1, &mTexture); |
| mTexture = 0; |
| } |
| glDeleteProgramPipelines(1, &mPipeline); |
| } |
| |
| GLuint mFramebuffer = 0; |
| GLuint mTexture = 0; |
| }; |
| |
| constexpr char kSimpleVertexShader[] = R"(attribute vec2 position; |
| attribute vec4 color; |
| varying vec4 vColor; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| vColor = color; |
| } |
| )"; |
| |
| constexpr char kSimpleFragmentShader[] = R"(precision mediump float; |
| varying vec4 vColor; |
| void main() |
| { |
| gl_FragColor = vColor; |
| } |
| )"; |
| |
| void SimpleStateChangeTest::simpleDrawWithBuffer(GLBuffer *buffer) |
| { |
| ANGLE_GL_PROGRAM(program, kSimpleVertexShader, kSimpleFragmentShader); |
| glUseProgram(program); |
| |
| GLint colorLoc = glGetAttribLocation(program, "color"); |
| ASSERT_NE(-1, colorLoc); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, *buffer); |
| glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr); |
| glEnableVertexAttribArray(colorLoc); |
| |
| drawQuad(program, "position", 0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void SimpleStateChangeTest::simpleDrawWithColor(const GLColor &color) |
| { |
| std::vector<GLColor> colors(6, color); |
| GLBuffer colorBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); |
| glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(GLColor), colors.data(), GL_STATIC_DRAW); |
| simpleDrawWithBuffer(&colorBuffer); |
| } |
| |
| // Test that we can do a drawElements call successfully after making a drawArrays call in the same |
| // frame. |
| TEST_P(SimpleStateChangeTest, DrawArraysThenDrawElements) |
| { |
| // http://anglebug.com/4121 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGLES()); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| glUseProgram(program); |
| |
| // We expect to draw a triangle with the first 3 points to the left, then another triangle with |
| // the last 3 vertices using a drawElements call. |
| auto vertices = std::vector<Vector3>{{-1.0f, -1.0f, 0.0f}, |
| {-1.0f, 1.0f, 0.0f}, |
| {0.0f, 0.0f, 0.0f}, |
| {1.0f, 1.0f, 0.0f}, |
| {1.0f, -1.0f, 0.0f}}; |
| |
| // If we use these indices to draw we'll be using the last 2 vertex only to draw. |
| auto indices = std::vector<GLushort>{2, 3, 4}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| GLBuffer indexBuffer; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| for (int i = 0; i < 10; i++) |
| { |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDrawArrays(GL_TRIANGLES, 0, 3); // triangle to the left |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, nullptr); // triangle to the right |
| glFinish(); |
| } |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int halfHeight = getWindowHeight() / 2; |
| |
| // Validate triangle to the left |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, halfHeight, GLColor::blue); |
| |
| // Validate triangle to the right |
| EXPECT_PIXEL_COLOR_EQ((quarterWidth * 3), halfHeight, GLColor::blue); |
| } |
| |
| // Draw a triangle with drawElements and a non-zero offset and draw the same |
| // triangle with the same offset followed by binding the same element buffer. |
| TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElements) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| |
| glUseProgram(program); |
| |
| // Background Red color |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // We expect to draw the triangle with the last three points on the bottom right, and |
| // rebind the same element buffer and draw with the same indices. |
| auto vertices = std::vector<Vector3>{ |
| {-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}}; |
| |
| auto indices = std::vector<GLushort>{0, 1, 2, 1, 2, 3}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLBuffer indexBuffer; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], |
| GL_STATIC_DRAW); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort))); |
| |
| // Rebind the same element buffer. |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| |
| // Draw the triangle again with the same offset. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort))); |
| |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Validate the triangle is drawn on the bottom right. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue); |
| |
| // Validate the triangle is NOT on the top left part. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::red); |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::red); |
| } |
| |
| // Draw a triangle with drawElements then change the index buffer and draw again. |
| TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElementsNewIndexBuffer) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| |
| glUseProgram(program); |
| |
| // Background Red color |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // We expect to draw the triangle with the last three points on the bottom right, and |
| // rebind the same element buffer and draw with the same indices. |
| auto vertices = std::vector<Vector3>{ |
| {-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}}; |
| |
| auto indices8 = std::vector<GLubyte>{0, 1, 2, 1, 2, 3}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLint colorUniformLocation = |
| glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f); |
| |
| GLBuffer indexBuffer8; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0], |
| GL_STATIC_DRAW); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| auto indices2nd8 = std::vector<GLubyte>{2, 3, 0, 0, 1, 2}; |
| |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices2nd8.size() * sizeof(GLubyte), &indices2nd8[0], |
| GL_STATIC_DRAW); |
| |
| glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Draw the triangle again with the same offset. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Validate the triangle is drawn on the bottom left. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue); |
| |
| // Validate the triangle is NOT on the top right part. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::white); |
| } |
| |
| // Draw a triangle with drawElements then change the indices and draw again. |
| TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElementsNewIndices) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| |
| glUseProgram(program); |
| |
| // Background Red color |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // We expect to draw the triangle with the last three points on the bottom right, and |
| // rebind the same element buffer and draw with the same indices. |
| std::vector<Vector3> vertices = { |
| {-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}}; |
| |
| std::vector<GLubyte> indices8 = {0, 1, 2, 2, 3, 0}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLint colorUniformLocation = |
| glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f); |
| |
| GLBuffer indexBuffer8; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0], |
| GL_DYNAMIC_DRAW); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| std::vector<GLubyte> newIndices8 = {2, 3, 0}; |
| |
| glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, newIndices8.size() * sizeof(GLubyte), |
| &newIndices8[0]); |
| |
| glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Draw the triangle again with the same offset. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Validate the triangle is drawn on the bottom left. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue); |
| |
| // Validate the triangle is NOT on the top right part. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::white); |
| } |
| |
| // Draw a triangle with drawElements then change the indices and draw again. Similar to |
| // DrawElementsThenDrawElementsNewIndices, but changes the whole index buffer (not just half). This |
| // triggers a different path in the Vulkan backend based on the fact that the majority of the buffer |
| // is being updated. |
| TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElementsWholeNewIndices) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| |
| glUseProgram(program); |
| |
| // Background Red color |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // We expect to draw the triangle with the last three points on the bottom right, and |
| // rebind the same element buffer and draw with the same indices. |
| std::vector<Vector3> vertices = { |
| {-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}}; |
| |
| std::vector<GLubyte> indices8 = {0, 1, 2, 2, 3, 0}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLint colorUniformLocation = |
| glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f); |
| |
| GLBuffer indexBuffer8; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0], |
| GL_DYNAMIC_DRAW); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| std::vector<GLubyte> newIndices8 = {2, 3, 0, 0, 0, 0}; |
| |
| glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, newIndices8.size() * sizeof(GLubyte), |
| &newIndices8[0]); |
| |
| glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Draw the triangle again with the same offset. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Validate the triangle is drawn on the bottom left. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue); |
| |
| // Validate the triangle is NOT on the top right part. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::white); |
| } |
| |
| // Draw a triangle with drawElements and a non-zero offset and draw the same |
| // triangle with the same offset followed by binding a USHORT element buffer. |
| TEST_P(SimpleStateChangeTest, DrawElementsUBYTEX2ThenDrawElementsUSHORT) |
| { |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| |
| glUseProgram(program); |
| |
| // Background Red color |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // We expect to draw the triangle with the last three points on the bottom right, and |
| // rebind the same element buffer and draw with the same indices. |
| auto vertices = std::vector<Vector3>{ |
| {-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}}; |
| |
| auto indices8 = std::vector<GLubyte>{0, 1, 2, 1, 2, 3}; |
| |
| GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLint colorUniformLocation = |
| glGetUniformLocation(program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f); |
| |
| GLBuffer indexBuffer8; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0], |
| GL_STATIC_DRAW); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| auto indices2nd8 = std::vector<GLubyte>{2, 3, 0, 0, 1, 2}; |
| GLBuffer indexBuffer2nd8; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer2nd8); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices2nd8.size() * sizeof(GLubyte), &indices2nd8[0], |
| GL_STATIC_DRAW); |
| glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte))); |
| |
| // Bind the 16bit element buffer. |
| auto indices16 = std::vector<GLushort>{0, 1, 3, 1, 2, 3}; |
| GLBuffer indexBuffer16; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer16); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices16.size() * sizeof(GLushort), &indices16[0], |
| GL_STATIC_DRAW); |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer16); |
| |
| glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Draw the triangle again with the same offset. |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(0 * sizeof(GLushort))); |
| |
| glDisableVertexAttribArray(positionLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| int quarterWidth = getWindowWidth() / 4; |
| int quarterHeight = getWindowHeight() / 4; |
| |
| // Validate green triangle is drawn on the bottom. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::green); |
| |
| // Validate white triangle is drawn on the right. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 3, quarterHeight * 2, GLColor::white); |
| |
| // Validate blue triangle is on the top left part. |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue); |
| } |
| |
| // Draw a points use multiple unaligned vertex buffer with same data, |
| // verify all the rendering results are the same. |
| TEST_P(SimpleStateChangeTest, DrawRepeatUnalignedVboChange) |
| { |
| // http://anglebug.com/4470 |
| ANGLE_SKIP_TEST_IF(isSwiftshader() && (IsWindows() || IsLinux())); |
| |
| const int kRepeat = 2; |
| |
| // set up VBO, colorVBO is unaligned |
| GLBuffer positionBuffer; |
| constexpr size_t posOffset = 0; |
| const GLfloat posData[] = {0.5f, 0.5f}; |
| glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(posData), posData, GL_STATIC_DRAW); |
| |
| GLBuffer colorBuffers[kRepeat]; |
| constexpr size_t colorOffset = 1; |
| const GLfloat colorData[] = {0.515f, 0.515f, 0.515f, 1.0f}; |
| constexpr size_t colorBufferSize = colorOffset + sizeof(colorData); |
| uint8_t colorDataUnaligned[colorBufferSize] = {0}; |
| memcpy(reinterpret_cast<void *>(colorDataUnaligned + colorOffset), colorData, |
| sizeof(colorData)); |
| for (uint32_t i = 0; i < kRepeat; i++) |
| { |
| glBindBuffer(GL_ARRAY_BUFFER, colorBuffers[i]); |
| glBufferData(GL_ARRAY_BUFFER, colorBufferSize, colorDataUnaligned, GL_STATIC_DRAW); |
| } |
| |
| // set up frame buffer |
| GLFramebuffer framebuffer; |
| GLTexture framebufferTexture; |
| bindTextureToFbo(framebuffer, framebufferTexture); |
| |
| // set up program |
| ANGLE_GL_PROGRAM(program, kSimpleVertexShader, kSimpleFragmentShader); |
| glUseProgram(program); |
| GLuint colorAttrLocation = glGetAttribLocation(program, "color"); |
| glEnableVertexAttribArray(colorAttrLocation); |
| GLuint posAttrLocation = glGetAttribLocation(program, "position"); |
| glEnableVertexAttribArray(posAttrLocation); |
| EXPECT_GL_NO_ERROR(); |
| |
| // draw and get drawing results |
| constexpr size_t kRenderSize = kWindowSize * kWindowSize; |
| std::array<GLColor, kRenderSize> pixelBufs[kRepeat]; |
| |
| for (uint32_t i = 0; i < kRepeat; i++) |
| { |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); |
| glVertexAttribPointer(posAttrLocation, 2, GL_FLOAT, GL_FALSE, 0, |
| reinterpret_cast<const void *>(posOffset)); |
| glBindBuffer(GL_ARRAY_BUFFER, colorBuffers[i]); |
| glVertexAttribPointer(colorAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, |
| reinterpret_cast<const void *>(colorOffset)); |
| |
| glDrawArrays(GL_POINTS, 0, 1); |
| |
| // read drawing results |
| glReadPixels(0, 0, kWindowSize, kWindowSize, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelBufs[i].data()); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // verify something is drawn |
| static_assert(kRepeat >= 2, "More than one repetition required"); |
| std::array<GLColor, kRenderSize> pixelAllBlack{0}; |
| EXPECT_NE(pixelBufs[0], pixelAllBlack); |
| // verify drawing results are all identical |
| for (uint32_t i = 1; i < kRepeat; i++) |
| { |
| EXPECT_EQ(pixelBufs[i - 1], pixelBufs[i]); |
| } |
| } |
| |
| // Handles deleting a Buffer when it's being used. |
| TEST_P(SimpleStateChangeTest, DeleteBufferInUse) |
| { |
| std::vector<GLColor> colorData(6, GLColor::red); |
| |
| GLBuffer buffer; |
| glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLColor) * colorData.size(), colorData.data(), |
| GL_STATIC_DRAW); |
| |
| simpleDrawWithBuffer(&buffer); |
| |
| buffer.reset(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Tests that resizing a Buffer during a draw works as expected. |
| TEST_P(SimpleStateChangeTest, RedefineBufferInUse) |
| { |
| std::vector<GLColor> redColorData(6, GLColor::red); |
| |
| GLBuffer buffer; |
| glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLColor) * redColorData.size(), redColorData.data(), |
| GL_STATIC_DRAW); |
| |
| // Trigger a pull from the buffer. |
| simpleDrawWithBuffer(&buffer); |
| |
| // Redefine the buffer that's in-flight. |
| std::vector<GLColor> greenColorData(1024, GLColor::green); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLColor) * greenColorData.size(), greenColorData.data(), |
| GL_STATIC_DRAW); |
| |
| // Trigger the flush and verify the first draw worked. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw again and verify the new data is correct. |
| simpleDrawWithBuffer(&buffer); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests updating a buffer's contents while in use, without redefining it. |
| TEST_P(SimpleStateChangeTest, UpdateBufferInUse) |
| { |
| std::vector<GLColor> redColorData(6, GLColor::red); |
| |
| GLBuffer buffer; |
| glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLColor) * redColorData.size(), redColorData.data(), |
| GL_STATIC_DRAW); |
| |
| // Trigger a pull from the buffer. |
| simpleDrawWithBuffer(&buffer); |
| |
| // Update the buffer that's in-flight. |
| std::vector<GLColor> greenColorData(6, GLColor::green); |
| glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLColor) * greenColorData.size(), |
| greenColorData.data()); |
| |
| // Trigger the flush and verify the first draw worked. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw again and verify the new data is correct. |
| simpleDrawWithBuffer(&buffer); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Tests that deleting an in-flight Texture does not immediately delete the resource. |
| TEST_P(SimpleStateChangeTest, DeleteTextureInUse) |
| { |
| std::array<GLColor, 4> colors = { |
| {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}}; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| tex.reset(); |
| EXPECT_GL_NO_ERROR(); |
| |
| int w = getWindowWidth() - 2; |
| int h = getWindowHeight() - 2; |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow); |
| } |
| |
| // Tests that modifying a texture parameter in-flight does not cause problems. |
| TEST_P(SimpleStateChangeTest, ChangeTextureFilterModeBetweenTwoDraws) |
| { |
| std::array<GLColor, 4> colors = { |
| {GLColor::black, GLColor::white, GLColor::black, GLColor::white}}; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Draw to the left side of the window only with NEAREST. |
| glViewport(0, 0, getWindowWidth() / 2, getWindowHeight()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| |
| // Draw to the right side of the window only with LINEAR. |
| glViewport(getWindowWidth() / 2, 0, getWindowWidth() / 2, getWindowHeight()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| EXPECT_GL_NO_ERROR(); |
| |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| |
| // The first half (left) should be only black followed by plain white. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::black); |
| EXPECT_PIXEL_COLOR_EQ((getWindowWidth() / 2) - 3, 0, GLColor::white); |
| EXPECT_PIXEL_COLOR_EQ((getWindowWidth() / 2) - 4, 0, GLColor::white); |
| |
| // The second half (right) should be a gradient so we shouldn't find plain black/white in the |
| // middle. |
| EXPECT_NE(angle::ReadColor((getWindowWidth() / 4) * 3, 0), GLColor::black); |
| EXPECT_NE(angle::ReadColor((getWindowWidth() / 4) * 3, 0), GLColor::white); |
| } |
| |
| // Tests that bind the same texture all the time between different draw calls. |
| TEST_P(SimpleStateChangeTest, RebindTextureDrawAgain) |
| { |
| GLuint program = get2DTexturedQuadProgram(); |
| glUseProgram(program); |
| |
| std::array<GLColor, 4> colors = {{GLColor::cyan, GLColor::cyan, GLColor::cyan, GLColor::cyan}}; |
| |
| // Setup the texture |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| // Setup the vertex array to draw a quad. |
| GLint positionLocation = glGetAttribLocation(program, "position"); |
| setupQuadVertexBuffer(1.0f, 1.0f); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Draw quad |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Bind again |
| glBindTexture(GL_TEXTURE_2D, tex); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw again, should still work. |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Validate whole surface is filled with cyan. |
| int h = getWindowHeight() - 1; |
| int w = getWindowWidth() - 1; |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::cyan); |
| } |
| |
| // Tests that we can draw with a texture, modify the texture with a texSubImage, and then draw again |
| // correctly. |
| TEST_P(SimpleStateChangeTest, DrawWithTextureTexSubImageThenDrawAgain) |
| { |
| GLuint program = get2DTexturedQuadProgram(); |
| ASSERT_NE(0u, program); |
| glUseProgram(program); |
| |
| std::array<GLColor, 4> colors = {{GLColor::red, GLColor::red, GLColor::red, GLColor::red}}; |
| std::array<GLColor, 4> subColors = { |
| {GLColor::green, GLColor::green, GLColor::green, GLColor::green}}; |
| |
| // Setup the texture |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| // Setup the vertex array to draw a quad. |
| GLint positionLocation = glGetAttribLocation(program, "position"); |
| setupQuadVertexBuffer(1.0f, 1.0f); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Draw quad |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Update bottom-half of texture with green. |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 1, GL_RGBA, GL_UNSIGNED_BYTE, subColors.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw again, should still work. |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Validate first half of the screen is red and the bottom is green. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4 * 3, GLColor::red); |
| } |
| |
| // Test that we can alternate between textures between different draws. |
| TEST_P(SimpleStateChangeTest, DrawTextureAThenTextureBThenTextureA) |
| { |
| GLuint program = get2DTexturedQuadProgram(); |
| glUseProgram(program); |
| |
| std::array<GLColor, 4> colorsTex1 = { |
| {GLColor::cyan, GLColor::cyan, GLColor::cyan, GLColor::cyan}}; |
| |
| std::array<GLColor, 4> colorsTex2 = { |
| {GLColor::magenta, GLColor::magenta, GLColor::magenta, GLColor::magenta}}; |
| |
| // Setup the texture |
| GLTexture tex1; |
| glBindTexture(GL_TEXTURE_2D, tex1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colorsTex1.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| GLTexture tex2; |
| glBindTexture(GL_TEXTURE_2D, tex2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colorsTex2.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| // Setup the vertex array to draw a quad. |
| GLint positionLocation = glGetAttribLocation(program, "position"); |
| setupQuadVertexBuffer(1.0f, 1.0f); |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Draw quad |
| glBindTexture(GL_TEXTURE_2D, tex1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Bind again, draw again |
| glBindTexture(GL_TEXTURE_2D, tex2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Bind again, draw again |
| glBindTexture(GL_TEXTURE_2D, tex1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| // Validate whole surface is filled with cyan. |
| int h = getWindowHeight() - 1; |
| int w = getWindowWidth() - 1; |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::cyan); |
| } |
| |
| // Tests that redefining an in-flight Texture does not affect the in-flight resource. |
| TEST_P(SimpleStateChangeTest, RedefineTextureInUse) |
| { |
| std::array<GLColor, 4> colors = { |
| {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}}; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| // Draw with the first texture. |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| |
| // Redefine the in-flight texture. |
| constexpr int kBigSize = 32; |
| std::vector<GLColor> bigColors; |
| for (int y = 0; y < kBigSize; ++y) |
| { |
| for (int x = 0; x < kBigSize; ++x) |
| { |
| bool xComp = x < kBigSize / 2; |
| bool yComp = y < kBigSize / 2; |
| if (yComp) |
| { |
| bigColors.push_back(xComp ? GLColor::cyan : GLColor::magenta); |
| } |
| else |
| { |
| bigColors.push_back(xComp ? GLColor::yellow : GLColor::white); |
| } |
| } |
| } |
| |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, bigColors.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Verify the first draw had the correct data via ReadPixels. |
| int w = getWindowWidth() - 2; |
| int h = getWindowHeight() - 2; |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow); |
| |
| // Draw and verify with the redefined data. |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan); |
| EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::magenta); |
| EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::yellow); |
| EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::white); |
| } |
| |
| // Test updating a Texture's contents while in use by GL works as expected. |
| TEST_P(SimpleStateChangeTest, UpdateTextureInUse) |
| { |
| std::array<GLColor, 4> rgby = {{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}}; |
| |
| // Set up 2D quad resources. |
| GLuint program = get2DTexturedQuadProgram(); |
| glUseProgram(program); |
| ASSERT_EQ(0, glGetAttribLocation(program, "position")); |
| |
| const auto &quadVerts = GetQuadVertices(); |
| |
| GLBuffer vbo; |
| glBindBuffer(GL_ARRAY_BUFFER, vbo); |
| glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(), |
| GL_STATIC_DRAW); |
| glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(0); |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgby.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| // Draw RGBY to the Framebuffer. The texture is now in-use by GL. |
| const int w = getWindowWidth() - 2; |
| const int h = getWindowHeight() - 2; |
| const int w2 = w >> 1; |
| |
| glViewport(0, 0, w2, h); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| // Update the texture to be YBGR, while the Texture is in-use. Should not affect the draw. |
| std::array<GLColor, 4> ybgr = {{GLColor::yellow, GLColor::blue, GLColor::green, GLColor::red}}; |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, ybgr.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw again to the Framebuffer. The second draw call should use the updated YBGR data. |
| glViewport(w2, 0, w2, h); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| // Check the Framebuffer. Both draws should have completed. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_PIXEL_COLOR_EQ(w2 - 1, 0, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(0, h - 1, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(w2 - 1, h - 1, GLColor::yellow); |
| |
| EXPECT_PIXEL_COLOR_EQ(w2 + 1, 0, GLColor::yellow); |
| EXPECT_PIXEL_COLOR_EQ(w - 1, 0, GLColor::blue); |
| EXPECT_PIXEL_COLOR_EQ(w2 + 1, h - 1, GLColor::green); |
| EXPECT_PIXEL_COLOR_EQ(w - 1, h - 1, GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void SimpleStateChangeTest::updateTextureBoundToFramebufferHelper(UpdateFunc updateFunc) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit")); |
| |
| std::vector<GLColor> red(4, GLColor::red); |
| std::vector<GLColor> green(4, GLColor::green); |
| |
| GLTexture renderTarget; |
| glBindTexture(GL_TEXTURE_2D, renderTarget); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, red.data()); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTarget, |
| 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); |
| glViewport(0, 0, 2, 2); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, red.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| // Draw once to flush dirty state bits. |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Update the (0, 1) pixel to be blue |
| updateFunc(GL_TEXTURE_2D, &renderTarget, 0, 1, GLColor::blue); |
| |
| // Draw green to the right half of the Framebuffer. |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, green.data()); |
| glViewport(1, 0, 1, 2); |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| |
| // Update the (1, 1) pixel to be yellow |
| updateFunc(GL_TEXTURE_2D, &renderTarget, 1, 1, GLColor::yellow); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify we have a quad with the right colors in the FBO. |
| std::vector<GLColor> expected = { |
| {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}}; |
| std::vector<GLColor> actual(4); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); |
| glReadPixels(0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, actual.data()); |
| EXPECT_EQ(expected, actual); |
| } |
| |
| // Tests that TexSubImage updates are flushed before rendering. |
| TEST_P(SimpleStateChangeTest, TexSubImageOnTextureBoundToFrambuffer) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| auto updateFunc = [](GLenum textureBinding, GLTexture *tex, GLint x, GLint y, |
| const GLColor &color) { |
| glBindTexture(textureBinding, *tex); |
| glTexSubImage2D(textureBinding, 0, x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color.data()); |
| }; |
| |
| updateTextureBoundToFramebufferHelper(updateFunc); |
| } |
| |
| // Tests that CopyTexSubImage updates are flushed before rendering. |
| TEST_P(SimpleStateChangeTest, CopyTexSubImageOnTextureBoundToFrambuffer) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit")); |
| |
| GLTexture copySource; |
| glBindTexture(GL_TEXTURE_2D, copySource); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| |
| GLFramebuffer copyFBO; |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, copyFBO); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copySource, 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER); |
| |
| auto updateFunc = [©Source](GLenum textureBinding, GLTexture *tex, GLint x, GLint y, |
| const GLColor &color) { |
| glBindTexture(GL_TEXTURE_2D, copySource); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color.data()); |
| |
| glBindTexture(textureBinding, *tex); |
| glCopyTexSubImage2D(textureBinding, 0, x, y, 0, 0, 1, 1); |
| }; |
| |
| updateTextureBoundToFramebufferHelper(updateFunc); |
| } |
| |
| // Tests that the read framebuffer doesn't affect what the draw call thinks the attachments are |
| // (which is what the draw framebuffer dictates) when a command is issued with the GL_FRAMEBUFFER |
| // target. |
| TEST_P(SimpleStateChangeTestES3, ReadFramebufferDrawFramebufferDifferentAttachments) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES()); |
| |
| GLRenderbuffer drawColorBuffer; |
| glBindRenderbuffer(GL_RENDERBUFFER, drawColorBuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1); |
| |
| GLRenderbuffer drawDepthBuffer; |
| glBindRenderbuffer(GL_RENDERBUFFER, drawDepthBuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1); |
| |
| GLRenderbuffer readColorBuffer; |
|