/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2021 Google Inc. * Copyright (c) 2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file glcFramebuffercompleteness.cpp * \brief Tests for OpenGL ES frame buffer completeness *//*--------------------------------------------------------------------*/ #include "glcFramebufferCompleteness.hpp" #include "deInt32.h" #include "gluDefs.hpp" #include "gluContextInfo.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuStringTemplate.hpp" #include #include #include #include #include #include #include namespace glcts { namespace { using namespace glw; using namespace std; struct TestContext { const glu::RenderContext& renderContext; const glw::Functions& gl; vector& fboIds; vector& texIds; vector& rboIds; void texParameteri (GLuint texId, GLenum target, GLenum pname, GLint parameter); void bindTexture (GLenum target, GLuint texId); void texImage2D (GLenum target, GLuint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data); void texImage2D (GLuint texId, GLenum target, GLuint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data); void texImage3D (GLuint texId, GLenum target, GLuint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* data); void renderbufferStorage (GLuint rboId, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height); void renderbufferStorageMultisample (GLuint rboId, GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height); void bindFramebuffer (GLenum target, GLuint fboId); void framebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texId, GLint level); void framebufferTextureLayer (GLenum target, GLenum attachment, GLuint texId, GLint level, GLint layer); void framebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint rboId); }; void TestContext::texParameteri(GLuint texId, GLenum target, GLenum pname, GLint parameter) { bindTexture(target, texId); gl.texParameteri(target, pname, parameter); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri() failed"); bindTexture(target, 0); } void TestContext::bindTexture(GLenum target, GLuint texId) { gl.bindTexture(target, texId); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() failed"); } void TestContext::texImage2D(GLenum target, GLuint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data) { gl.texImage2D(target, level, internalFormat, width, height, border, format, type, data); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexImage2D() failed"); } void TestContext::texImage2D(GLuint texId, GLenum target, GLuint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data) { bindTexture(target, texId); texImage2D(target, level, internalFormat, width, height, border, format, type, data); bindTexture(target, 0); } void TestContext::texImage3D(GLuint texId, GLenum target, GLuint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* data) { bindTexture(target, texId); gl.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, data); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexImage3D() failed"); bindTexture(target, 0); } void TestContext::renderbufferStorage(GLuint rboId, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height) { gl.bindRenderbuffer(GL_RENDERBUFFER, rboId); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed"); gl.renderbufferStorage(target, internalFormat, width, height); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage() failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed"); } void TestContext::renderbufferStorageMultisample(GLuint rboId, GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height) { gl.bindRenderbuffer(GL_RENDERBUFFER, rboId); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed"); gl.renderbufferStorageMultisample(target, samples, internalFormat, width, height); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorageMultisample() failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed"); } void TestContext::bindFramebuffer(GLenum target, GLuint fboId) { gl.bindFramebuffer(target, fboId); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() failed"); } void TestContext::framebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texId, GLint level) { gl.framebufferTexture2D(target, attachment, textarget, texId, level); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() failed"); } void TestContext::framebufferTextureLayer(GLenum target, GLenum attachment, GLuint texId, GLint level, GLint layer) { gl.framebufferTextureLayer(target, attachment, texId, level, layer); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTextureLayer() failed"); } void TestContext::framebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint rboId) { gl.framebufferRenderbuffer(target, attachment, renderbuffertarget, rboId); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer() failed"); } typedef function ExpectedStatusFn; typedef function TestFn; ExpectedStatusFn expectedStatusConstant(GLenum expectedStatus) { return [expectedStatus] (const glu::ContextInfo&) { return expectedStatus; }; } ExpectedStatusFn expectedStatusWithExtension(const string& extension, GLenum statusIfSupported, GLenum statusIfNotSupported) { return [extension, statusIfSupported, statusIfNotSupported](const glu::ContextInfo &contextInfo) { if (contextInfo.isExtensionSupported(extension.c_str())) return statusIfSupported; else return statusIfNotSupported; }; } struct TestStep { TestFn testFn; ExpectedStatusFn expectedFbStatus; }; typedef function&, TestContext& context)> StepsGeneratorFn; struct ExtensionEntry { string name; bool supportedStatus; }; struct TestParams { string name; string description; glu::ApiType apiType; size_t numFboIds; size_t numTexIds; size_t numRboIds; vector initialSteps; StepsGeneratorFn stepsGenerator; }; const GLuint TEXTURE_WIDTH = 16; const GLuint TEXTURE_HEIGHT = 16; const GLuint TEXTURE_DEPTH = 16; static const GLenum cubemapTextureTargets[] = { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_TEXTURE_CUBE_MAP_POSITIVE_Z }; const glu::ApiType apiES30 = glu::ApiType::es(3, 0); const glu::ApiType apiES31 = glu::ApiType::es(3, 1); const glu::ApiType apiES32 = glu::ApiType::es(3, 2); bool isDifferentRboSampleCountsSupported(TestContext& testContext, GLint& maxSamples) { const auto& gl = testContext.gl; gl.getIntegerv(GL_MAX_SAMPLES, &maxSamples); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed"); if (maxSamples < 4) TCU_FAIL("GL_MAX_SAMPLES needs to be >= 4"); testContext.renderbufferStorageMultisample(testContext.rboIds[0], GL_RENDERBUFFER, 1, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); gl.bindRenderbuffer(GL_RENDERBUFFER, testContext.rboIds[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed"); GLint minSamplesRbo; gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &minSamplesRbo); GLU_EXPECT_NO_ERROR(gl.getError(), "getRenderbufferParameteriv() failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer() failed"); return minSamplesRbo < maxSamples; } bool isDifferentTextureSampleCountsSupported(TestContext& testContext, GLint& maxSamples) { if (glu::contextSupports(testContext.renderContext.getType(), apiES31)) { const auto& gl = testContext.gl; GLint maxColorSamples; gl.getIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &maxColorSamples); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed"); GLint maxDepthSamples; gl.getIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &maxDepthSamples); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed"); maxSamples = min(maxColorSamples, maxDepthSamples); GLuint tempTexId; gl.genTextures(1, &tempTexId); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed"); testContext.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, tempTexId); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, GL_TRUE); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() failed"); GLint minSamplesTex; gl.getTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_SAMPLES, &minSamplesTex); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetTexLevelParameteriv() failed"); testContext.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); gl.deleteTextures(1, &tempTexId); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteTextures() failed"); return minSamplesTex < maxSamples; } return false; } /* Tests are defined as ordered series of steps that each expect a specific current framebuffer status after being executed. A new TestContext instance (parameter) is created for each test but all steps within a test use the same context. No code in addition to the framebuffer status check is executed between steps. */ const TestParams tests[] = { { "incomplete_missing_attachment", // string name "No attachments", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 0, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "incomplete_image_zero_width", // string name "Zero width attachment image", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 0, TEXTURE_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, DE_NULL); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "incomplete_image_zero_height", // string name "Zero height attachment image", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 0, TEXTURE_WIDTH, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, DE_NULL); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "incomplete_texture_3d_layer_oob", // string name "3D texture layer out of bounds", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.texImage3D(context.texIds[0], GL_TEXTURE_3D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, TEXTURE_DEPTH, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, context.texIds[0], 0, TEXTURE_DEPTH + 1); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "incomplete_texture_2d_layer_oob", // string name "2D texture layer out of bounds", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 1); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "incomplete_texture_2d_mm_layer_oob", // string name "2D mipmapped texture layer out of bounds", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 1, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 1); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { [](TestContext& context) { const deUint32 maxMipmapLevel = deLog2Floor32(de::max(TEXTURE_WIDTH, TEXTURE_HEIGHT)); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], maxMipmapLevel + 2); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "mutable_nbl_texture_expect_mipmap_complete", // string name "Mutable non base level texture as framebuffer attachment must be mipmap complete", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { DE_ASSERT(TEXTURE_WIDTH >= 8 && TEXTURE_HEIGHT >= 8); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 1, GL_RGBA8, TEXTURE_WIDTH >> 1, TEXTURE_HEIGHT >> 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 3, GL_RGBA8, TEXTURE_WIDTH >> 3, TEXTURE_HEIGHT >> 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3); context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 1); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 2, GL_RGBA8, TEXTURE_WIDTH >> 2, TEXTURE_HEIGHT >> 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "mutable_nbl_texture_expect_cube_complete", // string name "Mutable non base level texture as framebuffer attachment must be cube complete", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.texParameteri(context.texIds[0], GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 1); context.texParameteri(context.texIds[0], GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); context.bindTexture(GL_TEXTURE_CUBE_MAP, context.texIds[0]); for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(cubemapTextureTargets); ++i) { if (i % 2) continue; context.texImage2D(cubemapTextureTargets[i], 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(cubemapTextureTargets[i], 1, GL_RGBA8, TEXTURE_WIDTH >> 1, TEXTURE_HEIGHT >> 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); } context.bindTexture(GL_TEXTURE_CUBE_MAP, 0); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cubemapTextureTargets[0], context.texIds[0], 1); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { [](TestContext& context) { context.bindTexture(GL_TEXTURE_CUBE_MAP, context.texIds[0]); for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(cubemapTextureTargets); ++i) { if (i % 2 == 0) continue; context.texImage2D(cubemapTextureTargets[i], 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(cubemapTextureTargets[i], 1, GL_RGBA8, TEXTURE_WIDTH >> 1, TEXTURE_HEIGHT >> 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); } context.bindTexture(GL_TEXTURE_CUBE_MAP, 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "expect_renderable_internal_format", // string name "Color/Depth/Stencil attachment texture must have a color/depth/stencil" // string description " renderable internal format", apiES30, // glu::ApiType apiType 1, // size_t numFboIds 3, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(context.texIds[1], GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, DE_NULL); context.texImage2D(context.texIds[2], GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, DE_NULL); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, context.texIds[1], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, context.texIds[2], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) } }, [](vector& steps, TestContext& testContext) // StepsGeneratorFn stepsGenerator { GLint maxColorAttachmentsCount; testContext.gl.getIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachmentsCount); GLU_EXPECT_NO_ERROR(testContext.gl.getError(), "glGetInteger() failed"); steps.reserve(steps.size() + 2 * maxColorAttachmentsCount); for (GLint i = 0; i < maxColorAttachmentsCount; ++i) { steps.push_back( { [i](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, context.texIds[1], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }); steps.push_back( { [i](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }); } }, }, { "all_rbos_expect_same_numsamples", // string name "Same value of FRAMEBUFFER_SAMPLES for all attached render buffers", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 0, // size_t numTexIds 2, // size_t numRboIds {}, // vector initialSteps [](vector& steps, TestContext& testContext) // StepsGeneratorFn stepsGenerator { GLint maxSamples; if (!isDifferentRboSampleCountsSupported(testContext, maxSamples)) return; steps.push_back( { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }); steps.push_back( { [](TestContext& context) { context.renderbufferStorage(context.rboIds[0], GL_RENDERBUFFER, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.renderbufferStorage(context.rboIds[1], GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, context.rboIds[0]); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, context.rboIds[1]); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }); steps.push_back( { [maxSamples](TestContext& context) { context.renderbufferStorageMultisample(context.rboIds[0], GL_RENDERBUFFER, maxSamples, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.renderbufferStorageMultisample(context.rboIds[1], GL_RENDERBUFFER, 1, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) }); }, }, { "rbo_and_texture_expect_zero_numsamples", // string name "When using mixed renderbuffer and texture attachments, the value of" // string description " FRAMEBUFFER_SAMPLES needs to be zero for all attached renderbuffers", apiES30, // glu::ApiType apiType 1, // size_t numFboIds 2, // size_t numTexIds 2, // size_t u numRboIds { // vector initialSteps { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { [](TestContext& context) { context.renderbufferStorage(context.rboIds[0], GL_RENDERBUFFER, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.texImage2D(context.texIds[1], GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, DE_NULL); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, context.rboIds[0]); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, context.texIds[1], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { [](TestContext& context) { context.renderbufferStorage(context.rboIds[1], GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 0); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, context.rboIds[1]); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { [](TestContext& context) { context.renderbufferStorageMultisample(context.rboIds[1], GL_RENDERBUFFER, 2, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT); }, expectedStatusWithExtension("GL_NV_framebuffer_mixed_samples", GL_FRAMEBUFFER_COMPLETE, GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) }, { [](TestContext& context) { context.renderbufferStorageMultisample(context.rboIds[0], GL_RENDERBUFFER, 3, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, context.rboIds[0]); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, context.texIds[1], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) }, { [](TestContext& context) { context.renderbufferStorageMultisample(context.rboIds[0], GL_RENDERBUFFER, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "expect_equal_numsamples", // string name "The value of samples for each attached target must be equal", // string description apiES31, // glu::ApiType apiType 1, // size_t numFboIds 4, // size_t numTexIds 2, // size_t numRboIds {}, // vector initialSteps [](vector& steps, TestContext& testContext) // StepsGeneratorFn stepsGenerator { GLint maxRboSamples, maxTextureSamples; if (!isDifferentRboSampleCountsSupported(testContext, maxRboSamples) || !isDifferentTextureSampleCountsSupported(testContext, maxTextureSamples)) return; steps.push_back( { [maxRboSamples, maxTextureSamples](TestContext& context) { // Set up textures and renderbuffers for all following steps, complete = (tex0, rbo1) or (tex1, rbo0) */ context.renderbufferStorageMultisample(context.rboIds[0], GL_RENDERBUFFER, maxRboSamples, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.renderbufferStorageMultisample(context.rboIds[1], GL_RENDERBUFFER, 1, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT); const auto& gl = context.gl; context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, context.texIds[0]); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, GL_TRUE); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() failed"); context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, context.texIds[1]); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT, GL_TRUE); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() failed"); context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, context.texIds[2]); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, maxTextureSamples, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, GL_TRUE); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() failed"); context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, context.texIds[3]); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, maxTextureSamples, GL_DEPTH24_STENCIL8, TEXTURE_WIDTH, TEXTURE_HEIGHT, GL_TRUE); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() failed"); context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); // Framebuffer binding for rest of this test context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }); steps.push_back( { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[0], 0); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[1], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }); steps.push_back( { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[3], 0); }, expectedStatusWithExtension("GL_NV_framebuffer_mixed_samples", GL_FRAMEBUFFER_COMPLETE, GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) }); steps.push_back( { [](TestContext& context) { context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, context.rboIds[1]); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }); steps.push_back( { [](TestContext& context) { context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, context.rboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) }); steps.push_back( { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[2], 0); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[3], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }); steps.push_back( { [](TestContext& context) { const auto& gl = context.gl; gl.deleteTextures(1, &context.texIds[0]); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glDeleteTextures() failed"); gl.genTextures(1, &context.texIds[0]); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glGenTextures() failed"); context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, context.texIds[0]); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, GL_FALSE); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glTexStorage2DMultisample() failed"); context.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[0], 0); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, context.texIds[1], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) }); }, // StepsGeneratorFn stepsGenerator }, { "status_tracking", // string name "Modifying framebuffer attached objects correctly updates the fbo status", // string description apiES30, // glu::ApiType apiType 3, // size_t numFboIds 2, // size_t numTexIds 1, // size_t numRboIds { // vector initialSteps { // Initial status -> missing_attachment [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { // Allocate and attach texture -> complete [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { // Detach texture from fbo -> missing_attachment [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { // Allocate and attach renderbuffer -> complete [](TestContext& context) { context.renderbufferStorage(context.rboIds[0], GL_RENDERBUFFER, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, context.rboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { // Detach renderbuffer -> incomplete [](TestContext& context) { context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { // Switch to incomplete fb -> missing_attachment [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[1]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { // Attach texture to fbo -> complete [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { // Change image format of attached texture -> incomplete_attachment [](TestContext& context) { context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, DE_NULL); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { // Change image format (tex storage) -> complete [](TestContext& context) { context.bindTexture(GL_TEXTURE_2D, context.texIds[0]); context.gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glTexStorage2D() failed"); context.bindTexture(GL_TEXTURE_2D, 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { // Delete image -> missing_attachment [](TestContext& context) { context.gl.deleteTextures(1, &context.texIds[0]); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glDeleteTextures() failed"); context.texIds.erase(context.texIds.begin()); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }, { // Recreate image in wrong format, attach to color attachment -> incomplete_attachment [](TestContext& context) { const auto& gl = context.gl; GLuint texId; gl.genTextures(1, &texId); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed"); context.texIds.push_back(texId); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, DE_NULL); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { // Format to rgba8 using copyTexImage2D from compatible fbo -> framebuffer_complete [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[2]); context.texImage2D(context.texIds[1], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[1], 0); context.bindTexture(GL_TEXTURE_2D, context.texIds[0]); const auto& gl = context.gl; gl.copyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glCopyTexImage2D() failed"); context.bindTexture(GL_TEXTURE_2D, 0); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { // Change currently attached texture's format to compressed tex image -> incomplete_attachment (non color renderable) [](TestContext& context) { DE_ASSERT(TEXTURE_WIDTH == 16 && TEXTURE_HEIGHT == 16); static const glw::GLubyte textureDataETC2[] = // 16x16 all black RGBA8 texture in ETC2 format { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00 }; const auto& gl = context.gl; context.bindTexture(GL_TEXTURE_2D, context.texIds[0]); gl.compressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA8_ETC2_EAC, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, DE_LENGTH_OF_ARRAY(textureDataETC2), textureDataETC2); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompressedTexImage2D() failed"); context.bindTexture(GL_TEXTURE_2D, 0); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { // Re-attach rbo0 -> complete [](TestContext& context) { context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, context.rboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE) }, { // Rbo storage to non renderable format -> incomplete_attachment [](TestContext& context) { context.renderbufferStorage(context.rboIds[0], GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, TEXTURE_WIDTH, TEXTURE_HEIGHT); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) }, { // Delete rbo -> missing_attachment [](TestContext& context) { context.gl.deleteRenderbuffers(1, &context.rboIds[0]); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glDeleteRenderbuffers() failed"); context.rboIds.erase(context.rboIds.begin()); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "mutable_texture_missing_attachment_level", // string name "Attaching a mutable texture with undefined image for attachment level" " should be invalid", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds {}, // vector initialSteps [](vector& steps, TestContext&) // StepsGeneratorFn stepsGenerator { DE_ASSERT(TEXTURE_WIDTH >= 16 && TEXTURE_HEIGHT >= 16); steps.push_back( { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }); steps.push_back( { [](TestContext& context) { context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 1, GL_RGBA8, TEXTURE_WIDTH >> 1, TEXTURE_HEIGHT >> 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texImage2D(context.texIds[0], GL_TEXTURE_2D, 3, GL_RGBA8, TEXTURE_WIDTH >> 3, TEXTURE_HEIGHT >> 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL); context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 2); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), }); } }, { "immutable_texture_any_level_as_attachment", // string name "Any level of immutable texture as attachment should be valid", // string description apiES30, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds {}, // vector initialSteps [](vector& steps, TestContext&) // StepsGeneratorFn stepsGenerator { DE_ASSERT(TEXTURE_WIDTH >= 8 && TEXTURE_HEIGHT >= 8); steps.push_back( { [](TestContext& context) { context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) }); steps.push_back( { [](TestContext& context) { context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); context.bindTexture(GL_TEXTURE_2D, context.texIds[0]); const auto& gl = context.gl; gl.texStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT); GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() failed"); context.bindTexture(GL_TEXTURE_2D, 0); context.texParameteri(context.texIds[0], GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 2); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE), }); steps.push_back( { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 1); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE), }); steps.push_back( { [](TestContext& context) { context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context.texIds[0], 0); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE), }); } }, { "cube_map_layered_attachment_valid_size_and_format", // string name "Cube map attachment with images of same size, same format and square should be valid", // string description apiES32, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindTexture(GL_TEXTURE_CUBE_MAP, context.texIds[0]); for(const auto target : cubemapTextureTargets) context.texImage2D(target, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); context.bindTexture(GL_TEXTURE_CUBE_MAP, 0); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); context.gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, context.texIds[0], 0); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glFramebufferTexture() failed"); }, expectedStatusConstant(GL_FRAMEBUFFER_COMPLETE), } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "cube_map_layered_attachment_different_formats", // string name "Cube map attachment with images of the same size, square but different formats" " should be incomplete", // string description apiES32, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindTexture(GL_TEXTURE_CUBE_MAP, context.texIds[0]); for(size_t i = 0; i < DE_LENGTH_OF_ARRAY(cubemapTextureTargets); ++i) context.texImage2D(cubemapTextureTargets[i], 0, (i % 2) ? GL_RGBA8 : GL_RGB8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, (i % 2) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, 0); context.bindTexture(GL_TEXTURE_CUBE_MAP, 0); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); context.gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, context.texIds[0], 0); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glFramebufferTexture() failed"); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), }, { [](TestContext& context) { context.bindTexture(GL_TEXTURE_CUBE_MAP, context.texIds[0]); for(size_t i = 0; i < DE_LENGTH_OF_ARRAY(cubemapTextureTargets); ++i) context.texImage2D(cubemapTextureTargets[i], 0, (i % 2) ? GL_RGBA8 : GL_SRGB8_ALPHA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); context.bindTexture(GL_TEXTURE_CUBE_MAP, 0); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); context.gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, context.texIds[0], 0); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glFramebufferTexture() failed"); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), } }, DE_NULL, // StepsGeneratorFn stepsGenerator }, { "cube_map_layered_attachment_different_sizes", // string name "Cube map with images of different sizes, same format and all square should" " be incomplete", // string description apiES32, // glu::ApiType apiType 1, // size_t numFboIds 1, // size_t numTexIds 0, // size_t numRboIds { // vector initialSteps { [](TestContext& context) { context.bindTexture(GL_TEXTURE_CUBE_MAP, context.texIds[0]); for(size_t i = 0; i < DE_LENGTH_OF_ARRAY(cubemapTextureTargets); ++i) context.texImage2D(cubemapTextureTargets[i], 0, GL_RGBA8, (i % 2) ? TEXTURE_WIDTH : TEXTURE_WIDTH / 2, (i % 2) ? TEXTURE_HEIGHT : TEXTURE_HEIGHT / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); context.bindTexture(GL_TEXTURE_CUBE_MAP, 0); context.bindFramebuffer(GL_FRAMEBUFFER, context.fboIds[0]); context.gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, context.texIds[0], 0); GLU_EXPECT_NO_ERROR(context.gl.getError(), "glFramebufferTexture() failed"); }, expectedStatusConstant(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), }, }, DE_NULL, // StepsGeneratorFn stepsGenerator } }; class FramebufferCompletenessTestCase : public deqp::TestCase { public: FramebufferCompletenessTestCase (deqp::Context& context, const TestParams& params); virtual ~FramebufferCompletenessTestCase (); virtual void init (void); virtual void deinit (void); TestNode::IterateResult iterate (void); private: bool verifyFramebufferStatus (const glw::Functions& gl, const ExpectedStatusFn expectedStatusFn, const size_t stepIndex); const TestParams m_params; vector m_fboIds; vector m_texIds; vector m_rboIds; }; FramebufferCompletenessTestCase::FramebufferCompletenessTestCase(deqp::Context& context, const TestParams& params) : deqp::TestCase(context, params.name.c_str(), params.description.c_str()), m_params(params) { } FramebufferCompletenessTestCase::~FramebufferCompletenessTestCase() { } void FramebufferCompletenessTestCase::init(void) { const auto& renderContext = m_context.getRenderContext(); const auto& gl = renderContext.getFunctions(); if (m_params.numFboIds > 0) { m_fboIds.resize(m_params.numFboIds); gl.genFramebuffers(m_params.numFboIds, m_fboIds.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() failed"); } if (m_params.numTexIds > 0) { m_texIds.resize(m_params.numTexIds); gl.genTextures(m_params.numTexIds, m_texIds.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed"); } if (m_params.numRboIds > 0) { m_rboIds.resize(m_params.numRboIds); gl.genRenderbuffers(m_params.numRboIds, m_rboIds.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers() failed"); } } void FramebufferCompletenessTestCase::deinit(void) { const auto& renderContext = m_context.getRenderContext(); const auto& gl = renderContext.getFunctions(); if (!m_rboIds.empty()) { gl.deleteRenderbuffers(m_params.numRboIds, m_rboIds.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteRenderbuffers() failed"); m_rboIds.clear(); } if (!m_texIds.empty()) { gl.deleteTextures(m_params.numTexIds, m_texIds.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteTextures() failed"); m_texIds.clear(); } if (!m_fboIds.empty()) { gl.deleteFramebuffers(m_params.numFboIds, m_fboIds.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteFramebufers() failed"); m_fboIds.clear(); } } tcu::TestNode::IterateResult FramebufferCompletenessTestCase::iterate(void) { const auto& renderContext = m_context.getRenderContext(); const auto& gl = renderContext.getFunctions(); TestContext context = { renderContext, // const glu::RenderContext& renderContext gl, // const glw::Functions& gl m_fboIds, // vector& fboIds m_texIds, // vector& texIds m_rboIds // vector& rboIds }; auto steps = vector(m_params.initialSteps); if (m_params.stepsGenerator != DE_NULL) m_params.stepsGenerator(steps, context); if (steps.empty()) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } size_t stepIndex = 0; for (const auto& step : steps) { step.testFn(context); if (!verifyFramebufferStatus(gl, step.expectedFbStatus, stepIndex++)) return STOP; } return STOP; } bool FramebufferCompletenessTestCase::verifyFramebufferStatus(const glw::Functions& gl, const ExpectedStatusFn expectedStatusFn, const size_t stepIndex) { static const map statusNames = { { GL_FRAMEBUFFER_COMPLETE , "GL_FRAMEBUFFER_COMPLETE" }, { GL_FRAMEBUFFER_UNDEFINED , "GL_FRAMEBUFFER_UNDEFINED" }, { GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT , "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" }, { GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT , "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" }, { GL_FRAMEBUFFER_UNSUPPORTED , "GL_FRAMEBUFFER_UNSUPPORTED" }, { GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE , "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" }, { GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS , "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS" } }; const auto expectedStatus = expectedStatusFn(m_context.getContextInfo()); const auto fboStatus = gl.checkFramebufferStatus(GL_FRAMEBUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glCheckFramebufferStatus() failed"); if (fboStatus != expectedStatus) { ostringstream msg; const auto& fboStatusName = statusNames.find(fboStatus); const auto& expectedStatusName = statusNames.find(expectedStatus); msg << "Frame buffer status (" << ((fboStatusName != statusNames.end()) ? fboStatusName->second : std::to_string(fboStatus)) << ") does not match the expected status (" << ((expectedStatusName != statusNames.end()) ? expectedStatusName->second : std::to_string(expectedStatus)) << ") after step " << stepIndex; TCU_FAIL(msg.str().c_str()); return false; } m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); return true; } } // namespace FramebufferCompletenessTests::FramebufferCompletenessTests(deqp::Context& context) : deqp::TestCaseGroup(context, "framebuffer_completeness", "Tests for frame buffer completeness") { } void FramebufferCompletenessTests::init(void) { const auto& renderContext = m_context.getRenderContext(); for (const auto& test : tests) { if (!glu::contextSupports(renderContext.getType(), test.apiType)) continue; addChild(new FramebufferCompletenessTestCase(m_context, test)); } } } // namespace es3cts