| // | |
| // Copyright 2017 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // WebGLFramebufferTest.cpp : Framebuffer tests for GL_ANGLE_webgl_compatibility. | |
| // Based on WebGL 1 test renderbuffers/framebuffer-object-attachment. | |
| #include "test_utils/ANGLETest.h" | |
| #include "test_utils/gl_raii.h" | |
| namespace angle | |
| { | |
| class WebGLFramebufferTest : public ANGLETest | |
| { | |
| protected: | |
| WebGLFramebufferTest() | |
| { | |
| setWindowWidth(128); | |
| setWindowHeight(128); | |
| setConfigRedBits(8); | |
| setConfigGreenBits(8); | |
| setConfigBlueBits(8); | |
| setConfigAlphaBits(8); | |
| setWebGLCompatibilityEnabled(true); | |
| } | |
| void drawUByteColorQuad(GLuint program, GLint uniformLoc, const GLColor &color); | |
| void testDepthStencilDepthStencil(GLint width, GLint height); | |
| void testDepthStencilRenderbuffer(GLint width, | |
| GLint height, | |
| GLRenderbuffer *colorBuffer, | |
| GLbitfield allowedStatuses); | |
| void testRenderingAndReading(GLuint program); | |
| void testUsingIncompleteFramebuffer(GLenum depthFormat, GLenum depthAttachment); | |
| void testDrawingMissingAttachment(); | |
| }; | |
| constexpr GLint ALLOW_COMPLETE = 0x1; | |
| constexpr GLint ALLOW_UNSUPPORTED = 0x2; | |
| constexpr GLint ALLOW_INCOMPLETE_ATTACHMENT = 0x4; | |
| void checkFramebufferForAllowedStatuses(GLbitfield allowedStatuses) | |
| { | |
| // If the framebuffer is in an error state for multiple reasons, | |
| // we can't guarantee which one will be reported. | |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
| bool statusAllowed = | |
| ((allowedStatuses & ALLOW_COMPLETE) && (status == GL_FRAMEBUFFER_COMPLETE)) || | |
| ((allowedStatuses & ALLOW_UNSUPPORTED) && (status == GL_FRAMEBUFFER_UNSUPPORTED)) || | |
| ((allowedStatuses & ALLOW_INCOMPLETE_ATTACHMENT) && | |
| (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)); | |
| EXPECT_TRUE(statusAllowed); | |
| } | |
| void checkBufferBits(GLenum attachment0, GLenum attachment1) | |
| { | |
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | |
| return; | |
| bool haveDepthBuffer = | |
| attachment0 == GL_DEPTH_ATTACHMENT || attachment0 == GL_DEPTH_STENCIL_ATTACHMENT || | |
| attachment1 == GL_DEPTH_ATTACHMENT || attachment1 == GL_DEPTH_STENCIL_ATTACHMENT; | |
| bool haveStencilBuffer = | |
| attachment0 == GL_STENCIL_ATTACHMENT || attachment0 == GL_DEPTH_STENCIL_ATTACHMENT || | |
| attachment1 == GL_STENCIL_ATTACHMENT || attachment1 == GL_DEPTH_STENCIL_ATTACHMENT; | |
| GLint redBits = 0; | |
| GLint greenBits = 0; | |
| GLint blueBits = 0; | |
| GLint alphaBits = 0; | |
| GLint depthBits = 0; | |
| GLint stencilBits = 0; | |
| glGetIntegerv(GL_RED_BITS, &redBits); | |
| glGetIntegerv(GL_GREEN_BITS, &greenBits); | |
| glGetIntegerv(GL_BLUE_BITS, &blueBits); | |
| glGetIntegerv(GL_ALPHA_BITS, &alphaBits); | |
| glGetIntegerv(GL_DEPTH_BITS, &depthBits); | |
| glGetIntegerv(GL_STENCIL_BITS, &stencilBits); | |
| EXPECT_GE(redBits + greenBits + blueBits + alphaBits, 16); | |
| if (haveDepthBuffer) | |
| EXPECT_GE(depthBits, 16); | |
| else | |
| EXPECT_EQ(0, depthBits); | |
| if (haveStencilBuffer) | |
| EXPECT_GE(stencilBits, 8); | |
| else | |
| EXPECT_EQ(0, stencilBits); | |
| } | |
| // Tests that certain required combinations work in WebGL compatiblity. | |
| TEST_P(WebGLFramebufferTest, TestFramebufferRequiredCombinations) | |
| { | |
| // Per discussion with the OpenGL ES working group, the following framebuffer attachment | |
| // combinations are required to work in all WebGL implementations: | |
| // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture | |
| // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 | |
| // renderbuffer | |
| // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL | |
| // renderbuffer | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| constexpr int width = 64; | |
| constexpr int height = 64; | |
| // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture | |
| GLTexture texture; | |
| glBindTexture(GL_TEXTURE_2D, texture); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebufferForAllowedStatuses(ALLOW_COMPLETE); | |
| checkBufferBits(GL_NONE, GL_NONE); | |
| // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 | |
| // renderbuffer | |
| GLRenderbuffer renderbuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebufferForAllowedStatuses(ALLOW_COMPLETE); | |
| checkBufferBits(GL_DEPTH_ATTACHMENT, GL_NONE); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); | |
| if (getClientMajorVersion() == 2) | |
| { | |
| // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = | |
| // DEPTH_STENCIL renderbuffer | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, | |
| renderbuffer); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebufferForAllowedStatuses(ALLOW_COMPLETE); | |
| checkBufferBits(GL_DEPTH_STENCIL_ATTACHMENT, GL_NONE); | |
| } | |
| } | |
| void testAttachment(GLint width, | |
| GLint height, | |
| GLRenderbuffer *colorBuffer, | |
| GLenum attachment, | |
| GLRenderbuffer *buffer, | |
| GLbitfield allowedStatuses) | |
| { | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, *buffer); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebufferForAllowedStatuses(allowedStatuses); | |
| if ((allowedStatuses & ALLOW_COMPLETE) == 0) | |
| { | |
| std::vector<uint8_t> tempBuffer(width * height * 4); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); | |
| glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, tempBuffer.data()); | |
| EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); | |
| } | |
| checkBufferBits(attachment, GL_NONE); | |
| } | |
| void testAttachments(GLRenderbuffer &colorBuffer, | |
| GLenum attachment0, | |
| GLRenderbuffer &buffer0, | |
| GLenum attachment1, | |
| GLRenderbuffer &buffer1, | |
| GLbitfield allowedStatuses) | |
| { | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment0, GL_RENDERBUFFER, buffer0); | |
| EXPECT_GL_NO_ERROR(); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment1, GL_RENDERBUFFER, buffer1); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebufferForAllowedStatuses(allowedStatuses); | |
| checkBufferBits(attachment0, attachment1); | |
| } | |
| void testColorRenderbuffer(GLint width, | |
| GLint height, | |
| GLenum internalformat, | |
| GLbitfield allowedStatuses) | |
| { | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, internalformat, width, height); | |
| EXPECT_GL_NO_ERROR(); | |
| testAttachment(width, height, &colorBuffer, GL_COLOR_ATTACHMENT0, &colorBuffer, | |
| allowedStatuses); | |
| } | |
| GLint getRenderbufferParameter(GLenum paramName) | |
| { | |
| GLint paramValue = 0; | |
| glGetRenderbufferParameteriv(GL_RENDERBUFFER, paramName, ¶mValue); | |
| return paramValue; | |
| } | |
| void WebGLFramebufferTest::drawUByteColorQuad(GLuint program, | |
| GLint uniformLoc, | |
| const GLColor &color) | |
| { | |
| Vector4 vecColor = color.toNormalizedVector(); | |
| glUseProgram(program); | |
| glUniform4fv(uniformLoc, 1, vecColor.data()); | |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); | |
| } | |
| void WebGLFramebufferTest::testDepthStencilDepthStencil(GLint width, GLint height) | |
| { | |
| if (width == 0 || height == 0) | |
| { | |
| return; | |
| } | |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); | |
| GLint uniformLoc = glGetUniformLocation(program.get(), essl1_shaders::ColorUniform()); | |
| ASSERT_NE(-1, uniformLoc); | |
| struct TestInfo | |
| { | |
| GLenum firstFormat; | |
| GLenum firstAttach; | |
| GLenum secondFormat; | |
| GLenum secondAttach; | |
| }; | |
| TestInfo tests[2] = { | |
| {GL_DEPTH_COMPONENT16, GL_DEPTH_ATTACHMENT, GL_DEPTH_STENCIL, GL_DEPTH_STENCIL_ATTACHMENT}, | |
| {GL_DEPTH_STENCIL, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH_COMPONENT16, GL_DEPTH_ATTACHMENT}}; | |
| for (const TestInfo &test : tests) | |
| { | |
| for (GLint opIndex = 0; opIndex < 2; ++opIndex) | |
| { | |
| GLFramebuffer fbo; | |
| GLTexture tex; | |
| GLRenderbuffer firstRb; | |
| // test: firstFormat vs secondFormat with unbind or delete. | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| // attach texture as color | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| nullptr); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); | |
| // attach first | |
| glBindRenderbuffer(GL_RENDERBUFFER, firstRb); | |
| glRenderbufferStorage(GL_RENDERBUFFER, test.firstFormat, width, height); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, test.firstAttach, GL_RENDERBUFFER, firstRb); | |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| // TODO(jmadill): Remove clear - this should be implicit in WebGL_ | |
| glClear(GL_DEPTH_BUFFER_BIT); | |
| glEnable(GL_DEPTH_TEST); | |
| // Test it works | |
| drawUByteColorQuad(program.get(), uniformLoc, GLColor::green); | |
| // should not draw since DEPTH_FUNC == LESS | |
| drawUByteColorQuad(program.get(), uniformLoc, GLColor::red); | |
| // should be green | |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); | |
| GLuint secondRb = 0; | |
| glGenRenderbuffers(1, &secondRb); | |
| // attach second | |
| glBindRenderbuffer(GL_RENDERBUFFER, secondRb); | |
| glRenderbufferStorage(GL_RENDERBUFFER, test.secondFormat, width, height); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, test.secondAttach, GL_RENDERBUFFER, secondRb); | |
| if (opIndex == 0) | |
| { | |
| // now delete it | |
| glDeleteRenderbuffers(1, &secondRb); | |
| secondRb = 0; | |
| } | |
| else | |
| { | |
| // unbind it | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, test.secondAttach, GL_RENDERBUFFER, 0); | |
| } | |
| // If the first attachment is not restored this may fail | |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| EXPECT_GL_NO_ERROR(); | |
| // If the first attachment is not restored this may fail. | |
| glClear(GL_DEPTH_BUFFER_BIT); | |
| drawUByteColorQuad(program.get(), uniformLoc, GLColor::green); | |
| // should not draw since DEPTH_FUNC == LESS | |
| drawUByteColorQuad(program.get(), uniformLoc, GLColor::red); | |
| // should be green | |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); | |
| glDisable(GL_DEPTH_TEST); | |
| if (opIndex == 1) | |
| { | |
| glDeleteRenderbuffers(1, &secondRb); | |
| secondRb = 0; | |
| } | |
| } | |
| } | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| void WebGLFramebufferTest::testDepthStencilRenderbuffer(GLint width, | |
| GLint height, | |
| GLRenderbuffer *colorBuffer, | |
| GLbitfield allowedStatuses) | |
| { | |
| GLRenderbuffer depthStencilBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height); | |
| EXPECT_GL_NO_ERROR(); | |
| // OpenGL itself doesn't seem to guarantee that e.g. a 2 x 0 | |
| // renderbuffer will report 2 for its width when queried. | |
| if (!(height == 0 && width > 0)) | |
| { | |
| EXPECT_EQ(width, getRenderbufferParameter(GL_RENDERBUFFER_WIDTH)); | |
| } | |
| if (!(width == 0 && height > 0)) | |
| { | |
| EXPECT_EQ(height, getRenderbufferParameter(GL_RENDERBUFFER_HEIGHT)); | |
| } | |
| EXPECT_EQ(GL_DEPTH_STENCIL, getRenderbufferParameter(GL_RENDERBUFFER_INTERNAL_FORMAT)); | |
| EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_RED_SIZE)); | |
| EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_GREEN_SIZE)); | |
| EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_BLUE_SIZE)); | |
| EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_ALPHA_SIZE)); | |
| // Avoid verifying these for zero-sized renderbuffers for the time | |
| // being since it appears that even OpenGL doesn't guarantee them. | |
| if (width > 0 && height > 0) | |
| { | |
| EXPECT_GT(getRenderbufferParameter(GL_RENDERBUFFER_DEPTH_SIZE), 0); | |
| EXPECT_GT(getRenderbufferParameter(GL_RENDERBUFFER_STENCIL_SIZE), 0); | |
| } | |
| EXPECT_GL_NO_ERROR(); | |
| testAttachment(width, height, colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, &depthStencilBuffer, | |
| allowedStatuses); | |
| testDepthStencilDepthStencil(width, height); | |
| } | |
| // Test various attachment combinations with WebGL framebuffers. | |
| TEST_P(WebGLFramebufferTest, TestAttachments) | |
| { | |
| // GL_DEPTH_STENCIL renderbuffer format is only valid for WebGL1 | |
| ANGLE_SKIP_TEST_IF(getClientMajorVersion() != 2); | |
| for (GLint width = 2; width <= 2; width += 2) | |
| { | |
| for (GLint height = 2; height <= 2; height += 2) | |
| { | |
| // Dimensions width x height. | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, width, height); | |
| EXPECT_GL_NO_ERROR(); | |
| GLRenderbuffer depthBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); | |
| EXPECT_GL_NO_ERROR(); | |
| GLRenderbuffer stencilBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); | |
| EXPECT_GL_NO_ERROR(); | |
| GLRenderbuffer depthStencilBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height); | |
| EXPECT_GL_NO_ERROR(); | |
| GLbitfield allowedStatusForGoodCase = | |
| (width == 0 || height == 0) ? ALLOW_INCOMPLETE_ATTACHMENT : ALLOW_COMPLETE; | |
| // some cases involving stencil seem to be implementation-dependent | |
| GLbitfield allowedStatusForImplDependentCase = | |
| allowedStatusForGoodCase | ALLOW_UNSUPPORTED; | |
| // Attach depth using DEPTH_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_DEPTH_ATTACHMENT, &depthBuffer, | |
| allowedStatusForGoodCase); | |
| // Attach depth using STENCIL_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_STENCIL_ATTACHMENT, &depthBuffer, | |
| ALLOW_INCOMPLETE_ATTACHMENT); | |
| // Attach depth using DEPTH_STENCIL_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, &depthBuffer, | |
| ALLOW_INCOMPLETE_ATTACHMENT); | |
| // Attach stencil using STENCIL_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_STENCIL_ATTACHMENT, &stencilBuffer, | |
| allowedStatusForImplDependentCase); | |
| // Attach stencil using DEPTH_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_DEPTH_ATTACHMENT, &stencilBuffer, | |
| ALLOW_INCOMPLETE_ATTACHMENT); | |
| // Attach stencil using DEPTH_STENCIL_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, &stencilBuffer, | |
| ALLOW_INCOMPLETE_ATTACHMENT); | |
| // Attach depthStencil using DEPTH_STENCIL_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, | |
| &depthStencilBuffer, allowedStatusForGoodCase); | |
| // Attach depthStencil using DEPTH_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_DEPTH_ATTACHMENT, &depthStencilBuffer, | |
| ALLOW_INCOMPLETE_ATTACHMENT); | |
| // Attach depthStencil using STENCIL_ATTACHMENT. | |
| testAttachment(width, height, &colorBuffer, GL_STENCIL_ATTACHMENT, &depthStencilBuffer, | |
| ALLOW_INCOMPLETE_ATTACHMENT); | |
| GLbitfield allowedStatusForConflictedAttachment = | |
| (width == 0 || height == 0) ? ALLOW_UNSUPPORTED | ALLOW_INCOMPLETE_ATTACHMENT | |
| : ALLOW_UNSUPPORTED; | |
| // Attach depth, then stencil, causing conflict. | |
| testAttachments(colorBuffer, GL_DEPTH_ATTACHMENT, depthBuffer, GL_STENCIL_ATTACHMENT, | |
| stencilBuffer, allowedStatusForConflictedAttachment); | |
| // Attach stencil, then depth, causing conflict. | |
| testAttachments(colorBuffer, GL_STENCIL_ATTACHMENT, stencilBuffer, GL_DEPTH_ATTACHMENT, | |
| depthBuffer, allowedStatusForConflictedAttachment); | |
| // Attach depth, then depthStencil, causing conflict. | |
| testAttachments(colorBuffer, GL_DEPTH_ATTACHMENT, depthBuffer, | |
| GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, | |
| allowedStatusForConflictedAttachment); | |
| // Attach depthStencil, then depth, causing conflict. | |
| testAttachments(colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, | |
| GL_DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment); | |
| // Attach stencil, then depthStencil, causing conflict. | |
| testAttachments(colorBuffer, GL_DEPTH_ATTACHMENT, depthBuffer, | |
| GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, | |
| allowedStatusForConflictedAttachment); | |
| // Attach depthStencil, then stencil, causing conflict. | |
| testAttachments(colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, | |
| GL_STENCIL_ATTACHMENT, stencilBuffer, | |
| allowedStatusForConflictedAttachment); | |
| // Attach color renderbuffer with internalformat == RGBA4. | |
| testColorRenderbuffer(width, height, GL_RGBA4, allowedStatusForGoodCase); | |
| // Attach color renderbuffer with internalformat == RGB5_A1. | |
| // This particular format seems to be bugged on NVIDIA Retina. http://crbug.com/635081 | |
| // TODO(jmadill): Figure out if we can add a format workaround. | |
| if (!(IsNVIDIA() && IsOSX() && IsOpenGL())) | |
| { | |
| testColorRenderbuffer(width, height, GL_RGB5_A1, allowedStatusForGoodCase); | |
| } | |
| // Attach color renderbuffer with internalformat == RGB565. | |
| testColorRenderbuffer(width, height, GL_RGB565, allowedStatusForGoodCase); | |
| // Create and attach depthStencil renderbuffer. | |
| testDepthStencilRenderbuffer(width, height, &colorBuffer, allowedStatusForGoodCase); | |
| } | |
| } | |
| } | |
| bool tryDepth(GLRenderbuffer *depthBuffer, | |
| GLenum *depthFormat, | |
| GLenum *depthAttachment, | |
| GLenum try_format, | |
| GLenum try_attachment) | |
| { | |
| if (*depthAttachment != GL_NONE) | |
| { | |
| // If we've tried once unattach the old one. | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, *depthAttachment, GL_RENDERBUFFER, 0); | |
| } | |
| *depthFormat = try_format; | |
| *depthAttachment = try_attachment; | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, *depthAttachment, GL_RENDERBUFFER, *depthBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, *depthFormat, 16, 16); | |
| EXPECT_GL_NO_ERROR(); | |
| return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; | |
| } | |
| bool checkValidColorDepthCombination(GLenum *depthFormat, GLenum *depthAttachment) | |
| { | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| GLRenderbuffer depthBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); | |
| return tryDepth(&depthBuffer, depthFormat, depthAttachment, GL_DEPTH_COMPONENT16, | |
| GL_DEPTH_ATTACHMENT) || | |
| tryDepth(&depthBuffer, depthFormat, depthAttachment, GL_DEPTH_STENCIL, | |
| GL_DEPTH_STENCIL_ATTACHMENT); | |
| } | |
| // glCheckFramebufferStatus(GL_FRAMEBUFFER) should be either complete or (unsupported/expected). | |
| void checkFramebuffer(GLenum expected) | |
| { | |
| GLenum actual = glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
| EXPECT_TRUE(actual == expected || | |
| (expected != GL_FRAMEBUFFER_COMPLETE && actual == GL_FRAMEBUFFER_UNSUPPORTED)); | |
| } | |
| void WebGLFramebufferTest::testRenderingAndReading(GLuint program) | |
| { | |
| EXPECT_GL_NO_ERROR(); | |
| // drawArrays with incomplete framebuffer | |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); | |
| EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); | |
| // readPixels from incomplete framebuffer | |
| std::vector<uint8_t> dummyBuffer(4); | |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, dummyBuffer.data()); | |
| EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); | |
| // copyTexImage and copyTexSubImage can be either INVALID_FRAMEBUFFER_OPERATION because | |
| // the framebuffer is invalid OR INVALID_OPERATION because in the case of no attachments | |
| // the framebuffer is not of a compatible type. | |
| // copyTexSubImage2D from incomplete framebuffer | |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1); | |
| GLenum error = glGetError(); | |
| EXPECT_TRUE(error == GL_INVALID_FRAMEBUFFER_OPERATION || error == GL_INVALID_OPERATION); | |
| // copyTexImage2D from incomplete framebuffer | |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0); | |
| error = glGetError(); | |
| EXPECT_TRUE(error == GL_INVALID_FRAMEBUFFER_OPERATION || error == GL_INVALID_OPERATION); | |
| // clear with incomplete framebuffer | |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); | |
| } | |
| // Test drawing or reading from an incomplete framebuffer | |
| void WebGLFramebufferTest::testUsingIncompleteFramebuffer(GLenum depthFormat, | |
| GLenum depthAttachment) | |
| { | |
| // Simple draw program. | |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| GLRenderbuffer depthBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, depthAttachment, GL_RENDERBUFFER, depthBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| // We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES | |
| // 2.0 | |
| glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 32, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS); | |
| // Drawing or reading from an incomplete framebuffer should generate | |
| // INVALID_FRAMEBUFFER_OPERATION. | |
| testRenderingAndReading(program.get()); | |
| GLFramebuffer fbo2; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo2); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); | |
| // Drawing or reading from an incomplete framebuffer should generate | |
| // INVALID_FRAMEBUFFER_OPERATION. | |
| testRenderingAndReading(program.get()); | |
| GLRenderbuffer colorBuffer2; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer2); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer2); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 0, 0); | |
| // Drawing or reading from an incomplete framebuffer should generate | |
| // INVALID_FRAMEBUFFER_OPERATION. | |
| testRenderingAndReading(program.get()); | |
| } | |
| void testFramebufferIncompleteAttachment(GLenum depthFormat) | |
| { | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| // Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 | |
| // 4.4.5). | |
| glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| // 0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5). | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 0, 0); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5). | |
| void testFramebufferIncompleteMissingAttachment() | |
| { | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5). | |
| void testFramebufferIncompleteDimensions(GLenum depthFormat, GLenum depthAttachment) | |
| { | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| GLRenderbuffer colorBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| GLRenderbuffer depthBuffer; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, depthAttachment, GL_RENDERBUFFER, depthBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16); | |
| EXPECT_GL_NO_ERROR(); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 32, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS); | |
| glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 32); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| EXPECT_GL_NO_ERROR(); | |
| GLTexture tex; | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| 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, tex, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | |
| { | |
| return; | |
| } | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| checkFramebuffer(GL_FRAMEBUFFER_COMPLETE); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| class NoColorFB final : angle::NonCopyable | |
| { | |
| public: | |
| NoColorFB(int size) | |
| { | |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); | |
| // The only scenario we can verify is an attempt to read or copy | |
| // from a missing color attachment while the framebuffer is still | |
| // complete. | |
| glBindRenderbuffer(GL_RENDERBUFFER, mDepthBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, | |
| mDepthBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); | |
| // After depth renderbuffer setup | |
| EXPECT_GL_NO_ERROR(); | |
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | |
| { | |
| // Unable to allocate a framebuffer with just a depth attachment; this is legal. | |
| // Try just a depth/stencil renderbuffer. | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); | |
| glBindRenderbuffer(GL_RENDERBUFFER, mDepthStencilBuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, | |
| mDepthStencilBuffer); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, size, size); | |
| // After depth+stencil renderbuffer setup | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| } | |
| private: | |
| GLRenderbuffer mDepthBuffer; | |
| GLRenderbuffer mDepthStencilBuffer; | |
| GLFramebuffer mFBO; | |
| }; | |
| // Test reading from a missing framebuffer attachment. | |
| void TestReadingMissingAttachment(int size) | |
| { | |
| // The FBO has no color attachment. ReadPixels, CopyTexImage2D, | |
| // and CopyTexSubImage2D should all generate INVALID_OPERATION. | |
| // Before ReadPixels from missing attachment | |
| std::vector<uint8_t> dummyBuffer(4); | |
| EXPECT_GL_NO_ERROR(); | |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, dummyBuffer.data()); | |
| // After ReadPixels from missing attachment | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| GLTexture tex; | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| // Before CopyTexImage2D from missing attachment | |
| EXPECT_GL_NO_ERROR(); | |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, size, size, 0); | |
| // After CopyTexImage2D from missing attachment | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| // Before CopyTexSubImage2D from missing attachment | |
| EXPECT_GL_NO_ERROR(); | |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size, size); | |
| // After CopyTexSubImage2D from missing attachment | |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); | |
| } | |
| // Test drawing to a missing framebuffer attachment. | |
| void WebGLFramebufferTest::testDrawingMissingAttachment() | |
| { | |
| // Simple draw program. | |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_GL_NO_ERROR(); | |
| // try glDrawArrays | |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); | |
| EXPECT_GL_NO_ERROR(); | |
| // try glDrawElements | |
| drawIndexedQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // Determine if we can attach both color and depth or color and depth_stencil | |
| TEST_P(WebGLFramebufferTest, CheckValidColorDepthCombination) | |
| { | |
| // In FL9_3, we don't have a good way to handle non-color framebuffer rendering. | |
| if (IsD3D11_FL93()) | |
| { | |
| ignoreD3D11SDKLayersWarnings(); | |
| } | |
| GLenum depthFormat = GL_NONE; | |
| GLenum depthAttachment = GL_NONE; | |
| if (checkValidColorDepthCombination(&depthFormat, &depthAttachment)) | |
| { | |
| testFramebufferIncompleteDimensions(depthFormat, depthAttachment); | |
| testFramebufferIncompleteAttachment(depthFormat); | |
| testFramebufferIncompleteMissingAttachment(); | |
| testUsingIncompleteFramebuffer(depthFormat, depthAttachment); | |
| constexpr int size = 16; | |
| NoColorFB fb(size); | |
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) | |
| { | |
| // The FBO has no color attachment. ReadPixels, CopyTexImage2D, | |
| // and CopyTexSubImage2D should all generate INVALID_OPERATION. | |
| TestReadingMissingAttachment(size); | |
| // The FBO has no color attachment. Clear, DrawArrays, | |
| // and DrawElements should not generate an error. | |
| testDrawingMissingAttachment(); | |
| } | |
| } | |
| } | |
| // Test to cover a bug in preserving the texture image index for WebGL framebuffer attachments | |
| TEST_P(WebGLFramebufferTest, TextureAttachmentCommitBug) | |
| { | |
| ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_depth_texture")); | |
| GLTexture depthTexture; | |
| glBindTexture(GL_TEXTURE_2D, depthTexture.get()); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, | |
| nullptr); | |
| GLFramebuffer framebuffer; | |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture.get(), | |
| 0); | |
| glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // Test combinations of ordering in setting the resource format and attaching it to the depth | |
| // stencil attacchment. Covers http://crbug.com/997702 | |
| TEST_P(WebGLFramebufferTest, DepthStencilAttachmentOrdering) | |
| { | |
| constexpr GLsizei kFramebufferSize = 16; | |
| GLTexture color; | |
| glBindTexture(GL_TEXTURE_2D, color); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kFramebufferSize, kFramebufferSize, 0, GL_RGBA, | |
| GL_UNSIGNED_BYTE, nullptr); | |
| GLRenderbuffer depthStencil; | |
| glBindRenderbuffer(GL_RENDERBUFFER, depthStencil); | |
| // Attach the renderbuffer to the framebuffer when it has no format | |
| GLFramebuffer framebuffer; | |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, | |
| depthStencil); | |
| // Framebuffer is incomplete because the depth stencil attachment doesn't a format/size | |
| EXPECT_GL_NO_ERROR(); | |
| EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), | |
| GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); | |
| // Set depth stencil attachment to a color format | |
| if (EnsureGLExtensionEnabled("GL_OES_rgb8_rgba8")) | |
| { | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kFramebufferSize, kFramebufferSize); | |
| // Non-depth stencil format on the depth stencil attachment | |
| EXPECT_GL_NO_ERROR(); | |
| EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), | |
| GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); | |
| } | |
| { | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, kFramebufferSize, | |
| kFramebufferSize); | |
| // Depth-stencil attachment only has a depth format, not complete | |
| EXPECT_GL_NO_ERROR(); | |
| EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), | |
| GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); | |
| } | |
| if (EnsureGLExtensionEnabled("GL_OES_packed_depth_stencil")) | |
| { | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kFramebufferSize, | |
| kFramebufferSize); | |
| // Framebuffer should be complete now with a depth-stencil format | |
| EXPECT_GL_NO_ERROR(); | |
| EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE); | |
| } | |
| } | |
| // Only run against WebGL 1 validation, since much was changed in 2. | |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGLFramebufferTest); | |
| } // namespace angle |