| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.1 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * 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 |
| * \brief FBO colorbuffer tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fFboColorbufferTests.hpp" |
| #include "es31fFboTestCase.hpp" |
| #include "es31fFboTestUtil.hpp" |
| |
| #include "gluTextureUtil.hpp" |
| #include "gluContextInfo.hpp" |
| |
| #include "tcuCommandLine.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuRGBA.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuTextureUtil.hpp" |
| |
| #include "sglrContextUtil.hpp" |
| |
| #include "deRandom.hpp" |
| #include "deString.h" |
| |
| #include "glwEnums.hpp" |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| |
| using std::string; |
| using tcu::IVec2; |
| using tcu::IVec3; |
| using tcu::IVec4; |
| using tcu::TestLog; |
| using tcu::UVec4; |
| using tcu::Vec2; |
| using tcu::Vec3; |
| using tcu::Vec4; |
| using namespace FboTestUtil; |
| |
| const tcu::RGBA MIN_THRESHOLD(12, 12, 12, 12); |
| |
| static tcu::Vec4 generateRandomColor(de::Random &random) |
| { |
| tcu::Vec4 retVal; |
| |
| retVal[0] = random.getFloat(); |
| retVal[1] = random.getFloat(); |
| retVal[2] = random.getFloat(); |
| retVal[3] = 1.0f; |
| |
| return retVal; |
| } |
| |
| static tcu::CubeFace getCubeFaceFromNdx(int ndx) |
| { |
| switch (ndx) |
| { |
| case 0: |
| return tcu::CUBEFACE_POSITIVE_X; |
| case 1: |
| return tcu::CUBEFACE_NEGATIVE_X; |
| case 2: |
| return tcu::CUBEFACE_POSITIVE_Y; |
| case 3: |
| return tcu::CUBEFACE_NEGATIVE_Y; |
| case 4: |
| return tcu::CUBEFACE_POSITIVE_Z; |
| case 5: |
| return tcu::CUBEFACE_NEGATIVE_Z; |
| default: |
| DE_ASSERT(false); |
| return tcu::CUBEFACE_LAST; |
| } |
| } |
| |
| class FboColorbufferCase : public FboTestCase |
| { |
| public: |
| FboColorbufferCase(Context &context, const char *name, const char *desc, const uint32_t format) |
| : FboTestCase(context, name, desc) |
| , m_format(format) |
| { |
| } |
| |
| bool compare(const tcu::Surface &reference, const tcu::Surface &result) |
| { |
| const tcu::RGBA threshold(tcu::max(getFormatThreshold(m_format), MIN_THRESHOLD)); |
| |
| m_testCtx.getLog() << TestLog::Message << "Comparing images, threshold: " << threshold << TestLog::EndMessage; |
| |
| return tcu::bilinearCompare(m_testCtx.getLog(), "Result", "Image comparison result", reference.getAccess(), |
| result.getAccess(), threshold, tcu::COMPARE_LOG_RESULT); |
| } |
| |
| protected: |
| const uint32_t m_format; |
| }; |
| |
| class FboColorTex2DCase : public FboColorbufferCase |
| { |
| public: |
| FboColorTex2DCase(Context &context, const char *name, const char *description, uint32_t texFmt, |
| const IVec2 &texSize) |
| : FboColorbufferCase(context, name, description, texFmt) |
| , m_texFmt(texFmt) |
| , m_texSize(texSize) |
| { |
| } |
| |
| protected: |
| void preCheck(void) |
| { |
| checkFormatSupport(m_texFmt); |
| } |
| |
| void render(tcu::Surface &dst) |
| { |
| tcu::TextureFormat texFmt = glu::mapGLInternalFormat(m_texFmt); |
| tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt); |
| |
| Texture2DShader texToFboShader(DataTypes() << glu::TYPE_SAMPLER_2D, getFragmentOutputType(texFmt), |
| fmtInfo.valueMax - fmtInfo.valueMin, fmtInfo.valueMin); |
| uint32_t texToFboShaderID = getCurrentContext()->createProgram(&texToFboShader); |
| uint32_t fbo; |
| uint32_t tex; |
| |
| // Setup shader |
| texToFboShader.setUniforms(*getCurrentContext(), texToFboShaderID); |
| |
| // Generate fbo |
| { |
| glu::TransferFormat transferFmt = glu::getTransferFormat(texFmt); |
| uint32_t format = m_texFmt; |
| const IVec2 &size = m_texSize; |
| |
| glGenFramebuffers(1, &fbo); |
| glGenTextures(1, &tex); |
| |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexImage2D(GL_TEXTURE_2D, 0, format, size.x(), size.y(), 0, transferFmt.format, transferFmt.dataType, |
| DE_NULL); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); |
| checkError(); |
| checkFramebufferStatus(GL_FRAMEBUFFER); |
| } |
| |
| // Render texture to fbo |
| { |
| const uint32_t format = GL_RGBA; |
| const uint32_t dataType = GL_UNSIGNED_BYTE; |
| const int texW = 128; |
| const int texH = 128; |
| uint32_t tmpTex = 0; |
| const IVec2 &viewport = m_texSize; |
| tcu::TextureLevel data(glu::mapGLTransferFormat(format, dataType), texW, texH, 1); |
| |
| tcu::fillWithComponentGradients(data.getAccess(), Vec4(0.0f), Vec4(1.0f)); |
| |
| glGenTextures(1, &tmpTex); |
| glBindTexture(GL_TEXTURE_2D, tmpTex); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexImage2D(GL_TEXTURE_2D, 0, format, texW, texH, 0, format, dataType, data.getAccess().getDataPtr()); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glViewport(0, 0, viewport.x(), viewport.y()); |
| sglr::drawQuad(*getCurrentContext(), texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); |
| } |
| |
| readPixels(dst, 0, 0, getWidth(), getHeight(), texFmt, fmtInfo.lookupScale, fmtInfo.lookupBias); |
| checkError(); |
| } |
| |
| private: |
| uint32_t m_texFmt; |
| IVec2 m_texSize; |
| }; |
| |
| class FboColorTexCubeArrayCase : public FboColorbufferCase |
| { |
| public: |
| FboColorTexCubeArrayCase(Context &context, const char *name, const char *description, uint32_t texFmt, |
| const IVec3 &texSize) |
| : FboColorbufferCase(context, name, description, texFmt) |
| , m_texSize(texSize) |
| { |
| DE_ASSERT(texSize.z() % 6 == 0); |
| } |
| |
| protected: |
| void preCheck(void) |
| { |
| auto ctxType = m_context.getRenderContext().getType(); |
| if (!glu::contextSupports(ctxType, glu::ApiType::core(4, 5)) && |
| !glu::contextSupports(ctxType, glu::ApiType::es(3, 2)) && |
| !m_context.getContextInfo().isExtensionSupported("GL_EXT_texture_cube_map_array")) |
| TCU_THROW( |
| NotSupportedError, |
| "Test requires extension GL_EXT_texture_cube_map_array or a context version equal or higher than 3.2"); |
| |
| checkFormatSupport(m_format); |
| } |
| |
| void render(tcu::Surface &dst) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| de::Random rnd(deStringHash(getName()) ^ 0xed607a89 ^ m_testCtx.getCommandLine().getBaseSeed()); |
| tcu::TextureFormat texFmt = glu::mapGLInternalFormat(m_format); |
| tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt); |
| |
| Texture2DShader texToFboShader(DataTypes() << glu::TYPE_SAMPLER_2D, getFragmentOutputType(texFmt), |
| fmtInfo.valueMax - fmtInfo.valueMin, fmtInfo.valueMin); |
| TextureCubeArrayShader arrayTexShader(glu::getSamplerCubeArrayType(texFmt), glu::TYPE_FLOAT_VEC4, |
| glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType())); |
| |
| uint32_t texToFboShaderID = getCurrentContext()->createProgram(&texToFboShader); |
| uint32_t arrayTexShaderID = getCurrentContext()->createProgram(&arrayTexShader); |
| |
| // Setup textures |
| texToFboShader.setUniforms(*getCurrentContext(), texToFboShaderID); |
| arrayTexShader.setTexScaleBias(fmtInfo.lookupScale, fmtInfo.lookupBias); |
| |
| // Framebuffers. |
| std::vector<uint32_t> fbos; |
| uint32_t tex; |
| |
| { |
| glu::TransferFormat transferFmt = glu::getTransferFormat(texFmt); |
| bool isFilterable = glu::isGLInternalColorFormatFilterable(m_format); |
| const IVec3 &size = m_texSize; |
| |
| log << TestLog::Message << "Creating a cube map array texture (" << size.x() << "x" << size.y() |
| << ", depth: " << size.z() << ")" << TestLog::EndMessage; |
| |
| glGenTextures(1, &tex); |
| |
| glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, tex); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, isFilterable ? GL_LINEAR : GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, isFilterable ? GL_LINEAR : GL_NEAREST); |
| glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, m_format, size.x(), size.y(), size.z(), 0, transferFmt.format, |
| transferFmt.dataType, DE_NULL); |
| |
| log << TestLog::Message << "Creating a framebuffer object for each layer-face" << TestLog::EndMessage; |
| |
| // Generate an FBO for each layer-face |
| for (int ndx = 0; ndx < m_texSize.z(); ndx++) |
| { |
| uint32_t layerFbo; |
| |
| glGenFramebuffers(1, &layerFbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, layerFbo); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, ndx); |
| checkError(); |
| checkFramebufferStatus(GL_FRAMEBUFFER); |
| |
| fbos.push_back(layerFbo); |
| } |
| } |
| |
| log << TestLog::Message << "Rendering test images to layer-faces in randomized order" << TestLog::EndMessage; |
| |
| { |
| std::vector<int> order(fbos.size()); |
| |
| for (size_t n = 0; n < order.size(); n++) |
| order[n] = (int)n; |
| rnd.shuffle(order.begin(), order.end()); |
| |
| for (size_t ndx = 0; ndx < order.size(); ndx++) |
| { |
| const int layerFace = order[ndx]; |
| const uint32_t format = GL_RGBA; |
| const uint32_t dataType = GL_UNSIGNED_BYTE; |
| const int texW = 128; |
| const int texH = 128; |
| uint32_t tmpTex = 0; |
| const uint32_t fbo = fbos[layerFace]; |
| const IVec3 &viewport = m_texSize; |
| tcu::TextureLevel data(glu::mapGLTransferFormat(format, dataType), texW, texH, 1); |
| |
| tcu::fillWithGrid(data.getAccess(), 8, generateRandomColor(rnd), Vec4(0.0f)); |
| |
| glGenTextures(1, &tmpTex); |
| glBindTexture(GL_TEXTURE_2D, tmpTex); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexImage2D(GL_TEXTURE_2D, 0, format, texW, texH, 0, format, dataType, data.getAccess().getDataPtr()); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glViewport(0, 0, viewport.x(), viewport.y()); |
| sglr::drawQuad(*getCurrentContext(), texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), |
| Vec3(1.0f, 1.0f, 0.0f)); |
| checkError(); |
| |
| // Render to framebuffer |
| { |
| const Vec3 p0 = Vec3(float(ndx % 2) - 1.0f, float(ndx / 2) - 1.0f, 0.0f); |
| const Vec3 p1 = p0 + Vec3(1.0f, 1.0f, 0.0f); |
| const int layer = layerFace / 6; |
| const tcu::CubeFace face = getCubeFaceFromNdx(layerFace % 6); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glViewport(0, 0, getWidth(), getHeight()); |
| |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, tex); |
| |
| arrayTexShader.setLayer(layer); |
| arrayTexShader.setFace(face); |
| arrayTexShader.setUniforms(*getCurrentContext(), arrayTexShaderID); |
| |
| sglr::drawQuad(*getCurrentContext(), arrayTexShaderID, p0, p1); |
| checkError(); |
| } |
| } |
| } |
| |
| readPixels(dst, 0, 0, getWidth(), getHeight()); |
| } |
| |
| private: |
| IVec3 m_texSize; |
| }; |
| |
| FboColorTests::FboColorTests(Context &context) : TestCaseGroup(context, "color", "Colorbuffer tests") |
| { |
| } |
| |
| FboColorTests::~FboColorTests(void) |
| { |
| } |
| |
| void FboColorTests::init(void) |
| { |
| static const uint32_t colorFormats[] = { |
| // RGBA formats |
| GL_RGBA32I, GL_RGBA32UI, GL_RGBA16I, GL_RGBA16UI, GL_RGBA8, GL_RGBA8I, GL_RGBA8UI, GL_SRGB8_ALPHA8, GL_RGB10_A2, |
| GL_RGB10_A2UI, GL_RGBA4, GL_RGB5_A1, |
| |
| // RGB formats |
| GL_RGB8, GL_RGB565, |
| |
| // RG formats |
| GL_RG32I, GL_RG32UI, GL_RG16I, GL_RG16UI, GL_RG8, GL_RG8I, GL_RG8UI, |
| |
| // R formats |
| GL_R32I, GL_R32UI, GL_R16I, GL_R16UI, GL_R8, GL_R8I, GL_R8UI, |
| |
| // GL_EXT_color_buffer_float |
| GL_RGBA32F, GL_RGBA16F, GL_R11F_G11F_B10F, GL_RG32F, GL_RG16F, GL_R32F, GL_R16F, |
| |
| // GL_EXT_color_buffer_half_float |
| GL_RGB16F}; |
| |
| static const uint32_t unorm16ColorFormats[] = {GL_R16, GL_RG16, GL_RGBA16}; |
| |
| // .texcubearray |
| { |
| tcu::TestCaseGroup *texCubeArrayGroup = |
| new tcu::TestCaseGroup(m_testCtx, "texcubearray", "Cube map array texture tests"); |
| addChild(texCubeArrayGroup); |
| |
| for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); fmtNdx++) |
| texCubeArrayGroup->addChild(new FboColorTexCubeArrayCase(m_context, getFormatName(colorFormats[fmtNdx]), "", |
| colorFormats[fmtNdx], IVec3(128, 128, 12))); |
| } |
| |
| // .tex2d |
| { |
| tcu::TestCaseGroup *tex2dGroup = new tcu::TestCaseGroup(m_testCtx, "tex2d", "Render to texture"); |
| addChild(tex2dGroup); |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(unorm16ColorFormats); ndx++) |
| tex2dGroup->addChild(new FboColorTex2DCase(m_context, getFormatName(unorm16ColorFormats[ndx]), "", |
| unorm16ColorFormats[ndx], IVec2(129, 117))); |
| } |
| } |
| |
| } // namespace Functional |
| } // namespace gles31 |
| } // namespace deqp |