/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 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 stencilbuffer tests. *//*--------------------------------------------------------------------*/ #include "es3fFramebufferBlitTests.hpp" #include "es3fFboTestCase.hpp" #include "es3fFboTestUtil.hpp" #include "gluTextureUtil.hpp" #include "gluContextInfo.hpp" #include "tcuTextureUtil.hpp" #include "tcuVectorUtil.hpp" #include "tcuTestLog.hpp" #include "tcuImageCompare.hpp" #include "tcuRenderTarget.hpp" #include "sglrContextUtil.hpp" #include "glwEnums.hpp" #include "deStringUtil.hpp" namespace deqp { namespace gles3 { 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; class BlitRectCase : public FboTestCase { public: BlitRectCase(Context &context, const char *name, const char *desc, uint32_t filter, const IVec2 &srcSize, const IVec4 &srcRect, const IVec2 &dstSize, const IVec4 &dstRect, int cellSize = 8) : FboTestCase(context, name, desc) , m_filter(filter) , m_srcSize(srcSize) , m_srcRect(srcRect) , m_dstSize(dstSize) , m_dstRect(dstRect) , m_cellSize(cellSize) , m_gridCellColorA(0.2f, 0.7f, 0.1f, 1.0f) , m_gridCellColorB(0.7f, 0.1f, 0.5f, 0.8f) { } void render(tcu::Surface &dst) { const uint32_t colorFormat = GL_RGBA8; GradientShader gradShader(glu::TYPE_FLOAT_VEC4); Texture2DShader texShader(DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4); uint32_t gradShaderID = getCurrentContext()->createProgram(&gradShader); uint32_t texShaderID = getCurrentContext()->createProgram(&texShader); uint32_t srcFbo = 0; uint32_t dstFbo = 0; uint32_t srcRbo = 0; uint32_t dstRbo = 0; // Setup shaders gradShader.setGradient(*getCurrentContext(), gradShaderID, Vec4(0.0f), Vec4(1.0f)); texShader.setUniforms(*getCurrentContext(), texShaderID); // Create framebuffers. for (int ndx = 0; ndx < 2; ndx++) { uint32_t &fbo = ndx ? dstFbo : srcFbo; uint32_t &rbo = ndx ? dstRbo : srcRbo; const IVec2 &size = ndx ? m_dstSize : m_srcSize; glGenFramebuffers(1, &fbo); glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, colorFormat, size.x(), size.y()); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); checkError(); checkFramebufferStatus(GL_FRAMEBUFFER); } // Fill destination with gradient. glBindFramebuffer(GL_FRAMEBUFFER, dstFbo); glViewport(0, 0, m_dstSize.x(), m_dstSize.y()); sglr::drawQuad(*getCurrentContext(), gradShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); // Fill source with grid pattern. { const uint32_t format = GL_RGBA; const uint32_t dataType = GL_UNSIGNED_BYTE; const int texW = m_srcSize.x(); const int texH = m_srcSize.y(); uint32_t gridTex = 0; tcu::TextureLevel data(glu::mapGLTransferFormat(format, dataType), texW, texH, 1); tcu::fillWithGrid(data.getAccess(), m_cellSize, m_gridCellColorA, m_gridCellColorB); glGenTextures(1, &gridTex); glBindTexture(GL_TEXTURE_2D, gridTex); 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_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, format, texW, texH, 0, format, dataType, data.getAccess().getDataPtr()); glBindFramebuffer(GL_FRAMEBUFFER, srcFbo); glViewport(0, 0, m_srcSize.x(), m_srcSize.y()); sglr::drawQuad(*getCurrentContext(), texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); } // Perform copy. glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo); glBlitFramebuffer(m_srcRect.x(), m_srcRect.y(), m_srcRect.z(), m_srcRect.w(), m_dstRect.x(), m_dstRect.y(), m_dstRect.z(), m_dstRect.w(), GL_COLOR_BUFFER_BIT, m_filter); // Read back results. glBindFramebuffer(GL_READ_FRAMEBUFFER, dstFbo); readPixels(dst, 0, 0, m_dstSize.x(), m_dstSize.y(), glu::mapGLInternalFormat(colorFormat), Vec4(1.0f), Vec4(0.0f)); } virtual bool compare(const tcu::Surface &reference, const tcu::Surface &result) { // Use pixel-threshold compare for rect cases since 1px off will mean failure. tcu::RGBA threshold = TestCase::m_context.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(7, 7, 7, 7); return tcu::pixelThresholdCompare(m_testCtx.getLog(), "Result", "Image comparison result", reference, result, threshold, tcu::COMPARE_LOG_RESULT); } protected: const uint32_t m_filter; const IVec2 m_srcSize; const IVec4 m_srcRect; const IVec2 m_dstSize; const IVec4 m_dstRect; const int m_cellSize; const Vec4 m_gridCellColorA; const Vec4 m_gridCellColorB; }; class BlitNearestFilterConsistencyCase : public BlitRectCase { public: BlitNearestFilterConsistencyCase(Context &context, const char *name, const char *desc, const IVec2 &srcSize, const IVec4 &srcRect, const IVec2 &dstSize, const IVec4 &dstRect); bool compare(const tcu::Surface &reference, const tcu::Surface &result); }; BlitNearestFilterConsistencyCase::BlitNearestFilterConsistencyCase(Context &context, const char *name, const char *desc, const IVec2 &srcSize, const IVec4 &srcRect, const IVec2 &dstSize, const IVec4 &dstRect) : BlitRectCase(context, name, desc, GL_NEAREST, srcSize, srcRect, dstSize, dstRect, 1) { } bool BlitNearestFilterConsistencyCase::compare(const tcu::Surface &reference, const tcu::Surface &result) { DE_ASSERT(reference.getWidth() == result.getWidth()); DE_ASSERT(reference.getHeight() == result.getHeight()); DE_UNREF(reference); // Image origin must be visible (for baseColor) DE_ASSERT(de::min(m_dstRect.x(), m_dstRect.z()) >= 0); DE_ASSERT(de::min(m_dstRect.y(), m_dstRect.w()) >= 0); const tcu::RGBA cellColorA(m_gridCellColorA); const tcu::RGBA cellColorB(m_gridCellColorB); const tcu::RGBA threshold = TestCase::m_context.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(7, 7, 7, 7); const tcu::IVec4 destinationArea = tcu::IVec4(de::clamp(de::min(m_dstRect.x(), m_dstRect.z()), 0, result.getWidth()), de::clamp(de::min(m_dstRect.y(), m_dstRect.w()), 0, result.getHeight()), de::clamp(de::max(m_dstRect.x(), m_dstRect.z()), 0, result.getWidth()), de::clamp(de::max(m_dstRect.y(), m_dstRect.w()), 0, result.getHeight())); const tcu::RGBA baseColor = result.getPixel(destinationArea.x(), destinationArea.y()); const bool signConfig = tcu::compareThreshold(baseColor, cellColorA, threshold); bool error = false; tcu::Surface errorMask(result.getWidth(), result.getHeight()); std::vector horisontalSign(destinationArea.z() - destinationArea.x()); std::vector verticalSign(destinationArea.w() - destinationArea.y()); tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); // Checking only area in our destination rect m_testCtx.getLog() << tcu::TestLog::Message << "Verifying consistency of NEAREST filtering. Verifying rect " << m_dstRect << ".\n" << "Rounding direction of the NEAREST filter at the horisontal texel edge (x = n + 0.5) should not depend on " "the y-coordinate.\n" << "Rounding direction of the NEAREST filter at the vertical texel edge (y = n + 0.5) should not depend on the " "x-coordinate.\n" << "Blitting a grid (with uniform sized cells) should result in a grid (with non-uniform sized cells)." << tcu::TestLog::EndMessage; // Verify that destination only contains valid colors for (int dy = 0; dy < destinationArea.w() - destinationArea.y(); ++dy) for (int dx = 0; dx < destinationArea.z() - destinationArea.x(); ++dx) { const tcu::RGBA color = result.getPixel(destinationArea.x() + dx, destinationArea.y() + dy); const bool isValidColor = tcu::compareThreshold(color, cellColorA, threshold) || tcu::compareThreshold(color, cellColorB, threshold); if (!isValidColor) { errorMask.setPixel(destinationArea.x() + dx, destinationArea.y() + dy, tcu::RGBA::red()); error = true; } } if (error) { m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed, destination rect contains unexpected values. " << "Expected either " << cellColorA << " or " << cellColorB << "." << tcu::TestLog::EndMessage << tcu::TestLog::ImageSet("Result", "Image verification result") << tcu::TestLog::Image("Result", "Result", result) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) << tcu::TestLog::EndImageSet; return false; } // Detect result edges by reading the first row and first column of the blitted area. // Blitting a grid should result in a grid-like image. ("sign changes" should be consistent) for (int dx = 0; dx < destinationArea.z() - destinationArea.x(); ++dx) { const tcu::RGBA color = result.getPixel(destinationArea.x() + dx, destinationArea.y()); if (tcu::compareThreshold(color, cellColorA, threshold)) horisontalSign[dx] = true; else if (tcu::compareThreshold(color, cellColorB, threshold)) horisontalSign[dx] = false; else DE_ASSERT(false); } for (int dy = 0; dy < destinationArea.w() - destinationArea.y(); ++dy) { const tcu::RGBA color = result.getPixel(destinationArea.x(), destinationArea.y() + dy); if (tcu::compareThreshold(color, cellColorA, threshold)) verticalSign[dy] = true; else if (tcu::compareThreshold(color, cellColorB, threshold)) verticalSign[dy] = false; else DE_ASSERT(false); } // Verify grid-like image for (int dy = 0; dy < destinationArea.w() - destinationArea.y(); ++dy) for (int dx = 0; dx < destinationArea.z() - destinationArea.x(); ++dx) { const tcu::RGBA color = result.getPixel(destinationArea.x() + dx, destinationArea.y() + dy); const bool resultSign = tcu::compareThreshold(cellColorA, color, threshold); const bool correctSign = (horisontalSign[dx] == verticalSign[dy]) == signConfig; if (resultSign != correctSign) { errorMask.setPixel(destinationArea.x() + dx, destinationArea.y() + dy, tcu::RGBA::red()); error = true; } } // Report result if (error) { m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed, nearest filter is not consistent." << tcu::TestLog::EndMessage << tcu::TestLog::ImageSet("Result", "Image verification result") << tcu::TestLog::Image("Result", "Result", result) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) << tcu::TestLog::EndImageSet; } else { m_testCtx.getLog() << tcu::TestLog::Message << "Image verification passed." << tcu::TestLog::EndMessage << tcu::TestLog::ImageSet("Result", "Image verification result") << tcu::TestLog::Image("Result", "Result", result) << tcu::TestLog::EndImageSet; } return !error; } static tcu::BVec4 getChannelMask(tcu::TextureFormat::ChannelOrder order) { switch (order) { case tcu::TextureFormat::R: return tcu::BVec4(true, false, false, false); case tcu::TextureFormat::RG: return tcu::BVec4(true, true, false, false); case tcu::TextureFormat::RGB: return tcu::BVec4(true, true, true, false); case tcu::TextureFormat::RGBA: return tcu::BVec4(true, true, true, true); case tcu::TextureFormat::sRGB: return tcu::BVec4(true, true, true, false); case tcu::TextureFormat::sRGBA: return tcu::BVec4(true, true, true, true); default: DE_ASSERT(false); return tcu::BVec4(false); } } class BlitColorConversionCase : public FboTestCase { public: BlitColorConversionCase(Context &context, const char *name, const char *desc, uint32_t srcFormat, uint32_t dstFormat, const IVec2 &size) : FboTestCase(context, name, desc) , m_srcFormat(srcFormat) , m_dstFormat(dstFormat) , m_size(size) { } protected: void preCheck(void) { checkFormatSupport(m_srcFormat); checkFormatSupport(m_dstFormat); } void render(tcu::Surface &dst) { tcu::TextureFormat srcFormat = glu::mapGLInternalFormat(m_srcFormat); tcu::TextureFormat dstFormat = glu::mapGLInternalFormat(m_dstFormat); glu::DataType srcOutputType = getFragmentOutputType(srcFormat); glu::DataType dstOutputType = getFragmentOutputType(dstFormat); // Compute ranges \note Doesn't handle case where src or dest is not subset of the another! tcu::TextureFormatInfo srcFmtRangeInfo = tcu::getTextureFormatInfo(srcFormat); tcu::TextureFormatInfo dstFmtRangeInfo = tcu::getTextureFormatInfo(dstFormat); tcu::BVec4 copyMask = tcu::logicalAnd(getChannelMask(srcFormat.order), getChannelMask(dstFormat.order)); tcu::BVec4 srcIsGreater = tcu::greaterThan(srcFmtRangeInfo.valueMax - srcFmtRangeInfo.valueMin, dstFmtRangeInfo.valueMax - dstFmtRangeInfo.valueMin); tcu::TextureFormatInfo srcRangeInfo( tcu::select(dstFmtRangeInfo.valueMin, srcFmtRangeInfo.valueMin, tcu::logicalAnd(copyMask, srcIsGreater)), tcu::select(dstFmtRangeInfo.valueMax, srcFmtRangeInfo.valueMax, tcu::logicalAnd(copyMask, srcIsGreater)), tcu::select(dstFmtRangeInfo.lookupScale, srcFmtRangeInfo.lookupScale, tcu::logicalAnd(copyMask, srcIsGreater)), tcu::select(dstFmtRangeInfo.lookupBias, srcFmtRangeInfo.lookupBias, tcu::logicalAnd(copyMask, srcIsGreater))); tcu::TextureFormatInfo dstRangeInfo(tcu::select(dstFmtRangeInfo.valueMin, srcFmtRangeInfo.valueMin, tcu::logicalOr(tcu::logicalNot(copyMask), srcIsGreater)), tcu::select(dstFmtRangeInfo.valueMax, srcFmtRangeInfo.valueMax, tcu::logicalOr(tcu::logicalNot(copyMask), srcIsGreater)), tcu::select(dstFmtRangeInfo.lookupScale, srcFmtRangeInfo.lookupScale, tcu::logicalOr(tcu::logicalNot(copyMask), srcIsGreater)), tcu::select(dstFmtRangeInfo.lookupBias, srcFmtRangeInfo.lookupBias, tcu::logicalOr(tcu::logicalNot(copyMask), srcIsGreater))); // Shaders. GradientShader gradientToSrcShader(srcOutputType); GradientShader gradientToDstShader(dstOutputType); uint32_t gradShaderSrcID = getCurrentContext()->createProgram(&gradientToSrcShader); uint32_t gradShaderDstID = getCurrentContext()->createProgram(&gradientToDstShader); uint32_t srcFbo, dstFbo; uint32_t srcRbo, dstRbo; // Create framebuffers. for (int ndx = 0; ndx < 2; ndx++) { uint32_t &fbo = ndx ? dstFbo : srcFbo; uint32_t &rbo = ndx ? dstRbo : srcRbo; uint32_t format = ndx ? m_dstFormat : m_srcFormat; glGenFramebuffers(1, &fbo); glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, format, m_size.x(), m_size.y()); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); checkError(); checkFramebufferStatus(GL_FRAMEBUFFER); } glViewport(0, 0, m_size.x(), m_size.y()); // Render gradients. for (int ndx = 0; ndx < 2; ndx++) { glBindFramebuffer(GL_FRAMEBUFFER, ndx ? dstFbo : srcFbo); if (ndx) { gradientToDstShader.setGradient(*getCurrentContext(), gradShaderDstID, dstRangeInfo.valueMax, dstRangeInfo.valueMin); sglr::drawQuad(*getCurrentContext(), gradShaderDstID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); } else { gradientToSrcShader.setGradient(*getCurrentContext(), gradShaderSrcID, srcRangeInfo.valueMin, dstRangeInfo.valueMax); sglr::drawQuad(*getCurrentContext(), gradShaderSrcID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); } } // Execute copy. glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo); glBlitFramebuffer(0, 0, m_size.x(), m_size.y(), 0, 0, m_size.x(), m_size.y(), GL_COLOR_BUFFER_BIT, GL_NEAREST); checkError(); // Read results. glBindFramebuffer(GL_READ_FRAMEBUFFER, dstFbo); readPixels(dst, 0, 0, m_size.x(), m_size.y(), dstFormat, dstRangeInfo.lookupScale, dstRangeInfo.lookupBias); } bool compare(const tcu::Surface &reference, const tcu::Surface &result) { const tcu::TextureFormat srcFormat = glu::mapGLInternalFormat(m_srcFormat); const tcu::TextureFormat dstFormat = glu::mapGLInternalFormat(m_dstFormat); const bool srcIsSRGB = tcu::isSRGB(srcFormat); const bool dstIsSRGB = tcu::isSRGB(dstFormat); tcu::RGBA threshold; if (dstIsSRGB) { threshold = getToSRGBConversionThreshold(srcFormat, dstFormat); } else { const tcu::RGBA srcMaxDiff = getFormatThreshold(srcFormat) * (srcIsSRGB ? 2 : 1); const tcu::RGBA dstMaxDiff = getFormatThreshold(dstFormat); threshold = tcu::max(srcMaxDiff, dstMaxDiff); } m_testCtx.getLog() << tcu::TestLog::Message << "threshold = " << threshold << tcu::TestLog::EndMessage; return tcu::pixelThresholdCompare(m_testCtx.getLog(), "Result", "Image comparison result", reference, result, threshold, tcu::COMPARE_LOG_RESULT); } private: uint32_t m_srcFormat; uint32_t m_dstFormat; IVec2 m_size; }; class BlitDepthStencilCase : public FboTestCase { public: BlitDepthStencilCase(Context &context, const char *name, const char *desc, uint32_t depthFormat, uint32_t stencilFormat, uint32_t srcBuffers, const IVec2 &srcSize, const IVec4 &srcRect, uint32_t dstBuffers, const IVec2 &dstSize, const IVec4 &dstRect, uint32_t copyBuffers) : FboTestCase(context, name, desc) , m_depthFormat(depthFormat) , m_stencilFormat(stencilFormat) , m_srcBuffers(srcBuffers) , m_srcSize(srcSize) , m_srcRect(srcRect) , m_dstBuffers(dstBuffers) , m_dstSize(dstSize) , m_dstRect(dstRect) , m_copyBuffers(copyBuffers) { } protected: void preCheck(void) { if (m_depthFormat != GL_NONE) checkFormatSupport(m_depthFormat); if (m_stencilFormat != GL_NONE) checkFormatSupport(m_stencilFormat); if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_separate_depth_stencil") && m_depthFormat != GL_NONE && m_stencilFormat != GL_NONE) throw tcu::NotSupportedError("Separate depth and stencil buffers not supported"); } void render(tcu::Surface &dst) { const uint32_t colorFormat = GL_RGBA8; GradientShader gradShader(glu::TYPE_FLOAT_VEC4); Texture2DShader texShader(DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4); FlatColorShader flatShader(glu::TYPE_FLOAT_VEC4); uint32_t flatShaderID = getCurrentContext()->createProgram(&flatShader); uint32_t texShaderID = getCurrentContext()->createProgram(&texShader); uint32_t gradShaderID = getCurrentContext()->createProgram(&gradShader); uint32_t srcFbo = 0; uint32_t dstFbo = 0; uint32_t srcColorRbo = 0; uint32_t dstColorRbo = 0; uint32_t srcDepthRbo = 0; uint32_t srcStencilRbo = 0; uint32_t dstDepthRbo = 0; uint32_t dstStencilRbo = 0; // setup shaders gradShader.setGradient(*getCurrentContext(), gradShaderID, Vec4(0.0f), Vec4(1.0f)); texShader.setUniforms(*getCurrentContext(), texShaderID); // Create framebuffers. for (int ndx = 0; ndx < 2; ndx++) { uint32_t &fbo = ndx ? dstFbo : srcFbo; uint32_t &colorRbo = ndx ? dstColorRbo : srcColorRbo; uint32_t &depthRbo = ndx ? dstDepthRbo : srcDepthRbo; uint32_t &stencilRbo = ndx ? dstStencilRbo : srcStencilRbo; uint32_t bufs = ndx ? m_dstBuffers : m_srcBuffers; const IVec2 &size = ndx ? m_dstSize : m_srcSize; glGenFramebuffers(1, &fbo); glGenRenderbuffers(1, &colorRbo); glBindRenderbuffer(GL_RENDERBUFFER, colorRbo); glRenderbufferStorage(GL_RENDERBUFFER, colorFormat, size.x(), size.y()); if (m_depthFormat != GL_NONE) { glGenRenderbuffers(1, &depthRbo); glBindRenderbuffer(GL_RENDERBUFFER, depthRbo); glRenderbufferStorage(GL_RENDERBUFFER, m_depthFormat, size.x(), size.y()); } if (m_stencilFormat != GL_NONE) { glGenRenderbuffers(1, &stencilRbo); glBindRenderbuffer(GL_RENDERBUFFER, stencilRbo); glRenderbufferStorage(GL_RENDERBUFFER, m_stencilFormat, size.x(), size.y()); } glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRbo); if (bufs & GL_DEPTH_BUFFER_BIT) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRbo); if (bufs & GL_STENCIL_BUFFER_BIT) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilFormat == GL_NONE ? depthRbo : stencilRbo); checkError(); checkFramebufferStatus(GL_FRAMEBUFFER); // Clear depth to 1 and stencil to 0. glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); } // Fill source with gradient, depth = [-1..1], stencil = 7 glBindFramebuffer(GL_FRAMEBUFFER, srcFbo); glViewport(0, 0, m_srcSize.x(), m_srcSize.y()); glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilFunc(GL_ALWAYS, 7, 0xffu); sglr::drawQuad(*getCurrentContext(), gradShaderID, Vec3(-1.0f, -1.0f, -1.0f), Vec3(1.0f, 1.0f, 1.0f)); // Fill destination with grid pattern, depth = 0 and stencil = 1 { const uint32_t format = GL_RGBA; const uint32_t dataType = GL_UNSIGNED_BYTE; const int texW = m_srcSize.x(); const int texH = m_srcSize.y(); uint32_t gridTex = 0; tcu::TextureLevel data(glu::mapGLTransferFormat(format, dataType), texW, texH, 1); tcu::fillWithGrid(data.getAccess(), 8, Vec4(0.2f, 0.7f, 0.1f, 1.0f), Vec4(0.7f, 0.1f, 0.5f, 0.8f)); glGenTextures(1, &gridTex); glBindTexture(GL_TEXTURE_2D, gridTex); 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_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, format, texW, texH, 0, format, dataType, data.getAccess().getDataPtr()); glBindFramebuffer(GL_FRAMEBUFFER, dstFbo); glViewport(0, 0, m_dstSize.x(), m_dstSize.y()); glStencilFunc(GL_ALWAYS, 1, 0xffu); sglr::drawQuad(*getCurrentContext(), texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); } // Perform copy. glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo); glBlitFramebuffer(m_srcRect.x(), m_srcRect.y(), m_srcRect.z(), m_srcRect.w(), m_dstRect.x(), m_dstRect.y(), m_dstRect.z(), m_dstRect.w(), m_copyBuffers, GL_NEAREST); // Render blue color where depth < 0, decrement on depth failure. glBindFramebuffer(GL_FRAMEBUFFER, dstFbo); glViewport(0, 0, m_dstSize.x(), m_dstSize.y()); glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); glStencilFunc(GL_ALWAYS, 0, 0xffu); flatShader.setColor(*getCurrentContext(), flatShaderID, Vec4(0.0f, 0.0f, 1.0f, 1.0f)); sglr::drawQuad(*getCurrentContext(), flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); if (m_dstBuffers & GL_STENCIL_BUFFER_BIT) { // Render green color where stencil == 6. glDisable(GL_DEPTH_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilFunc(GL_EQUAL, 6, 0xffu); flatShader.setColor(*getCurrentContext(), flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f)); sglr::drawQuad(*getCurrentContext(), flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); } readPixels(dst, 0, 0, m_dstSize.x(), m_dstSize.y(), glu::mapGLInternalFormat(colorFormat), Vec4(1.0f), Vec4(0.0f)); } private: uint32_t m_depthFormat; uint32_t m_stencilFormat; uint32_t m_srcBuffers; IVec2 m_srcSize; IVec4 m_srcRect; uint32_t m_dstBuffers; IVec2 m_dstSize; IVec4 m_dstRect; uint32_t m_copyBuffers; }; class BlitDefaultFramebufferCase : public FboTestCase { public: BlitDefaultFramebufferCase(Context &context, const char *name, const char *desc, uint32_t format, uint32_t filter) : FboTestCase(context, name, desc) , m_format(format) , m_filter(filter) { } protected: void preCheck(void) { if (m_context.getRenderTarget().getNumSamples() > 0) throw tcu::NotSupportedError("Not supported in MSAA config"); checkFormatSupport(m_format); } virtual void render(tcu::Surface &dst) { tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_format); glu::TransferFormat transferFmt = glu::getTransferFormat(colorFormat); GradientShader gradShader(glu::TYPE_FLOAT_VEC4); Texture2DShader texShader(DataTypes() << glu::getSampler2DType(colorFormat), glu::TYPE_FLOAT_VEC4); uint32_t gradShaderID = getCurrentContext()->createProgram(&gradShader); uint32_t texShaderID = getCurrentContext()->createProgram(&texShader); uint32_t fbo = 0; uint32_t tex = 0; const int texW = 128; const int texH = 128; // Setup shaders gradShader.setGradient(*getCurrentContext(), gradShaderID, Vec4(0.0f), Vec4(1.0f)); texShader.setUniforms(*getCurrentContext(), texShaderID); // FBO 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, m_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_filter); glTexImage2D(GL_TEXTURE_2D, 0, m_format, texW, texH, 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 gradient to screen. glBindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); sglr::drawQuad(*getCurrentContext(), gradShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); // Blit gradient from screen to fbo. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glBlitFramebuffer(0, 0, getWidth(), getHeight(), 0, 0, texW, texH, GL_COLOR_BUFFER_BIT, m_filter); // Fill left half of viewport with quad that uses texture. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); glClearBufferfv(GL_COLOR, 0, Vec4(1.0f, 0.0f, 0.0f, 1.0f).getPtr()); sglr::drawQuad(*getCurrentContext(), texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f)); // Blit fbo to right half. glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); glBlitFramebuffer(0, 0, texW, texH, getWidth() / 2, 0, getWidth(), getHeight(), GL_COLOR_BUFFER_BIT, m_filter); glBindFramebuffer(GL_READ_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); readPixels(dst, 0, 0, getWidth(), getHeight()); } bool compare(const tcu::Surface &reference, const tcu::Surface &result) { const tcu::RGBA threshold(tcu::max(getFormatThreshold(m_format), tcu::RGBA(12, 12, 12, 12))); 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; const uint32_t m_filter; }; class DefaultFramebufferBlitCase : public BlitDefaultFramebufferCase { public: enum BlitDirection { BLIT_DEFAULT_TO_TARGET, BLIT_TO_DEFAULT_FROM_TARGET, BLIT_LAST }; enum BlitArea { AREA_SCALE, AREA_OUT_OF_BOUNDS, AREA_LAST }; DefaultFramebufferBlitCase(Context &context, const char *name, const char *desc, uint32_t format, uint32_t filter, BlitDirection dir, BlitArea area) : BlitDefaultFramebufferCase(context, name, desc, format, filter) , m_blitDir(dir) , m_blitArea(area) , m_srcRect(-1, -1, -1, -1) , m_dstRect(-1, -1, -1, -1) , m_interestingArea(-1, -1, -1, -1) { DE_ASSERT(dir < BLIT_LAST); DE_ASSERT(area < AREA_LAST); } void init(void) { // requirements const int minViewportSize = 128; if (m_context.getRenderTarget().getWidth() < minViewportSize || m_context.getRenderTarget().getHeight() < minViewportSize) throw tcu::NotSupportedError("Viewport size " + de::toString(minViewportSize) + "x" + de::toString(minViewportSize) + " required"); // prevent viewport randoming m_viewportWidth = m_context.getRenderTarget().getWidth(); m_viewportHeight = m_context.getRenderTarget().getHeight(); // set proper areas if (m_blitArea == AREA_SCALE) { m_srcRect = IVec4(10, 20, 65, 100); m_dstRect = IVec4(25, 30, 125, 94); m_interestingArea = IVec4(0, 0, 128, 128); } else if (m_blitArea == AREA_OUT_OF_BOUNDS) { const tcu::IVec2 ubound = (m_blitDir == BLIT_DEFAULT_TO_TARGET) ? (tcu::IVec2(128, 128)) : (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())); m_srcRect = IVec4(-10, -15, 100, 63); m_dstRect = ubound.swizzle(0, 1, 0, 1) + IVec4(-75, -99, 8, 16); m_interestingArea = IVec4(ubound.x() - 128, ubound.y() - 128, ubound.x(), ubound.y()); } else DE_ASSERT(false); } void render(tcu::Surface &dst) { const tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_format); const glu::TransferFormat transferFmt = glu::getTransferFormat(colorFormat); const tcu::TextureChannelClass targetClass = (m_blitDir == BLIT_DEFAULT_TO_TARGET) ? (tcu::getTextureChannelClass(colorFormat.type)) : (tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT); uint32_t fbo = 0; uint32_t fboTex = 0; const int fboTexW = 128; const int fboTexH = 128; const int sourceWidth = (m_blitDir == BLIT_DEFAULT_TO_TARGET) ? (getWidth()) : (fboTexW); const int sourceHeight = (m_blitDir == BLIT_DEFAULT_TO_TARGET) ? (getHeight()) : (fboTexH); const int gridRenderWidth = de::min(256, sourceWidth); const int gridRenderHeight = de::min(256, sourceHeight); int targetFbo = -1; int sourceFbo = -1; // FBO glGenFramebuffers(1, &fbo); glGenTextures(1, &fboTex); glBindTexture(GL_TEXTURE_2D, fboTex); 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, m_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_filter); glTexImage2D(GL_TEXTURE_2D, 0, m_format, fboTexW, fboTexH, 0, transferFmt.format, transferFmt.dataType, DE_NULL); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0); checkError(); checkFramebufferStatus(GL_FRAMEBUFFER); targetFbo = (m_blitDir == BLIT_DEFAULT_TO_TARGET) ? (fbo) : (m_context.getRenderContext().getDefaultFramebuffer()); sourceFbo = (m_blitDir == BLIT_DEFAULT_TO_TARGET) ? (m_context.getRenderContext().getDefaultFramebuffer()) : (fbo); // Render grid to source framebuffer { Texture2DShader texShader(DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4); const uint32_t texShaderID = getCurrentContext()->createProgram(&texShader); const uint32_t internalFormat = GL_RGBA8; const uint32_t format = GL_RGBA; const uint32_t dataType = GL_UNSIGNED_BYTE; const int gridTexW = 128; const int gridTexH = 128; uint32_t gridTex = 0; tcu::TextureLevel data(glu::mapGLTransferFormat(format, dataType), gridTexW, gridTexH, 1); tcu::fillWithGrid(data.getAccess(), 9, tcu::Vec4(0.9f, 0.5f, 0.1f, 0.9f), tcu::Vec4(0.2f, 0.8f, 0.2f, 0.7f)); glGenTextures(1, &gridTex); glBindTexture(GL_TEXTURE_2D, gridTex); 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_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, gridTexW, gridTexH, 0, format, dataType, data.getAccess().getDataPtr()); glBindFramebuffer(GL_FRAMEBUFFER, sourceFbo); glViewport(0, 0, gridRenderWidth, gridRenderHeight); glClearBufferfv(GL_COLOR, 0, Vec4(1.0f, 0.0f, 0.0f, 1.0f).getPtr()); texShader.setUniforms(*getCurrentContext(), texShaderID); sglr::drawQuad(*getCurrentContext(), texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); glUseProgram(0); } // Blit source framebuffer to destination glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFbo); checkError(); if (targetClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || targetClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || targetClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT) glClearBufferfv(GL_COLOR, 0, Vec4(1.0f, 1.0f, 0.0f, 1.0f).getPtr()); else if (targetClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER) glClearBufferiv(GL_COLOR, 0, IVec4(0, 0, 0, 0).getPtr()); else if (targetClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER) glClearBufferuiv(GL_COLOR, 0, UVec4(0, 0, 0, 0).getPtr()); else DE_ASSERT(false); glBlitFramebuffer(m_srcRect.x(), m_srcRect.y(), m_srcRect.z(), m_srcRect.w(), m_dstRect.x(), m_dstRect.y(), m_dstRect.z(), m_dstRect.w(), GL_COLOR_BUFFER_BIT, m_filter); checkError(); // Read target glBindFramebuffer(GL_FRAMEBUFFER, targetFbo); if (m_blitDir == BLIT_TO_DEFAULT_FROM_TARGET) readPixels(dst, m_interestingArea.x(), m_interestingArea.y(), m_interestingArea.z() - m_interestingArea.x(), m_interestingArea.w() - m_interestingArea.y()); else readPixels(dst, m_interestingArea.x(), m_interestingArea.y(), m_interestingArea.z() - m_interestingArea.x(), m_interestingArea.w() - m_interestingArea.y(), colorFormat, tcu::Vec4(1.0f), tcu::Vec4(0.0f)); checkError(); } private: const BlitDirection m_blitDir; const BlitArea m_blitArea; tcu::IVec4 m_srcRect; tcu::IVec4 m_dstRect; tcu::IVec4 m_interestingArea; }; FramebufferBlitTests::FramebufferBlitTests(Context &context) : TestCaseGroup(context, "blit", "Framebuffer blit tests") { } FramebufferBlitTests::~FramebufferBlitTests(void) { } void FramebufferBlitTests::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}; static const uint32_t depthStencilFormats[] = {GL_NONE, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT16, GL_DEPTH32F_STENCIL8, GL_DEPTH24_STENCIL8}; static const uint32_t stencilFormats[] = {GL_NONE, GL_STENCIL_INDEX8}; // .rect { static const struct { const char *name; IVec4 srcRect; IVec4 dstRect; } copyRects[] = { {"basic", IVec4(10, 20, 65, 100), IVec4(45, 5, 100, 85)}, {"scale", IVec4(10, 20, 65, 100), IVec4(25, 30, 125, 94)}, {"out_of_bounds", IVec4(-10, -15, 100, 63), IVec4(50, 30, 136, 144)}, }; static const struct { const char *name; IVec4 srcRect; IVec4 dstRect; } filterConsistencyRects[] = { {"mag", IVec4(20, 10, 74, 88), IVec4(10, 10, 91, 101)}, {"min", IVec4(10, 20, 78, 100), IVec4(20, 20, 71, 80)}, {"out_of_bounds_mag", IVec4(21, 10, 73, 82), IVec4(11, 43, 141, 151)}, {"out_of_bounds_min", IVec4(11, 21, 77, 97), IVec4(80, 82, 135, 139)}, }; static const struct { const char *name; IVec4 srcSwizzle; IVec4 dstSwizzle; } swizzles[] = {{DE_NULL, IVec4(0, 1, 2, 3), IVec4(0, 1, 2, 3)}, {"reverse_src_x", IVec4(2, 1, 0, 3), IVec4(0, 1, 2, 3)}, {"reverse_src_y", IVec4(0, 3, 2, 1), IVec4(0, 1, 2, 3)}, {"reverse_dst_x", IVec4(0, 1, 2, 3), IVec4(2, 1, 0, 3)}, {"reverse_dst_y", IVec4(0, 1, 2, 3), IVec4(0, 3, 2, 1)}, {"reverse_src_dst_x", IVec4(2, 1, 0, 3), IVec4(2, 1, 0, 3)}, {"reverse_src_dst_y", IVec4(0, 3, 2, 1), IVec4(0, 3, 2, 1)}}; const IVec2 srcSize(127, 119); const IVec2 dstSize(132, 128); // Blit rectangle tests. tcu::TestCaseGroup *rectGroup = new tcu::TestCaseGroup(m_testCtx, "rect", "Blit rectangle tests"); addChild(rectGroup); for (int rectNdx = 0; rectNdx < DE_LENGTH_OF_ARRAY(copyRects); rectNdx++) { for (int swzNdx = 0; swzNdx < DE_LENGTH_OF_ARRAY(swizzles); swzNdx++) { string name = string(copyRects[rectNdx].name) + (swizzles[swzNdx].name ? (string("_") + swizzles[swzNdx].name) : string()); IVec4 srcSwz = swizzles[swzNdx].srcSwizzle; IVec4 dstSwz = swizzles[swzNdx].dstSwizzle; IVec4 srcRect = copyRects[rectNdx].srcRect.swizzle(srcSwz[0], srcSwz[1], srcSwz[2], srcSwz[3]); IVec4 dstRect = copyRects[rectNdx].dstRect.swizzle(dstSwz[0], dstSwz[1], dstSwz[2], dstSwz[3]); rectGroup->addChild(new BlitRectCase(m_context, (name + "_nearest").c_str(), "", GL_NEAREST, srcSize, srcRect, dstSize, dstRect)); rectGroup->addChild(new BlitRectCase(m_context, (name + "_linear").c_str(), "", GL_LINEAR, srcSize, srcRect, dstSize, dstRect)); } } // Nearest filter tests for (int rectNdx = 0; rectNdx < DE_LENGTH_OF_ARRAY(filterConsistencyRects); rectNdx++) { for (int swzNdx = 0; swzNdx < DE_LENGTH_OF_ARRAY(swizzles); swzNdx++) { string name = string("nearest_consistency_") + filterConsistencyRects[rectNdx].name + (swizzles[swzNdx].name ? (string("_") + swizzles[swzNdx].name) : string()); IVec4 srcSwz = swizzles[swzNdx].srcSwizzle; IVec4 dstSwz = swizzles[swzNdx].dstSwizzle; IVec4 srcRect = filterConsistencyRects[rectNdx].srcRect.swizzle(srcSwz[0], srcSwz[1], srcSwz[2], srcSwz[3]); IVec4 dstRect = filterConsistencyRects[rectNdx].dstRect.swizzle(dstSwz[0], dstSwz[1], dstSwz[2], dstSwz[3]); rectGroup->addChild(new BlitNearestFilterConsistencyCase(m_context, name.c_str(), "Test consistency of the nearest filter", srcSize, srcRect, dstSize, dstRect)); } } } // .conversion { tcu::TestCaseGroup *conversionGroup = new tcu::TestCaseGroup(m_testCtx, "conversion", "Color conversion tests"); addChild(conversionGroup); for (int srcFmtNdx = 0; srcFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); srcFmtNdx++) { for (int dstFmtNdx = 0; dstFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); dstFmtNdx++) { uint32_t srcFormat = colorFormats[srcFmtNdx]; tcu::TextureFormat srcTexFmt = glu::mapGLInternalFormat(srcFormat); tcu::TextureChannelClass srcType = tcu::getTextureChannelClass(srcTexFmt.type); uint32_t dstFormat = colorFormats[dstFmtNdx]; tcu::TextureFormat dstTexFmt = glu::mapGLInternalFormat(dstFormat); tcu::TextureChannelClass dstType = tcu::getTextureChannelClass(dstTexFmt.type); if (((srcType == tcu::TEXTURECHANNELCLASS_FLOATING_POINT || srcType == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) != (dstType == tcu::TEXTURECHANNELCLASS_FLOATING_POINT || dstType == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)) || ((srcType == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER) != (dstType == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER)) || ((srcType == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER) != (dstType == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER))) continue; // Conversion not supported. string name = string(getFormatName(srcFormat)) + "_to_" + getFormatName(dstFormat); conversionGroup->addChild( new BlitColorConversionCase(m_context, name.c_str(), "", srcFormat, dstFormat, IVec2(127, 113))); } } } // .depth_stencil { tcu::TestCaseGroup *depthStencilGroup = new tcu::TestCaseGroup(m_testCtx, "depth_stencil", "Depth and stencil blits"); addChild(depthStencilGroup); for (int dFmtNdx = 0; dFmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); dFmtNdx++) { for (int sFmtNdx = 0; sFmtNdx < DE_LENGTH_OF_ARRAY(stencilFormats); sFmtNdx++) { uint32_t depthFormat = depthStencilFormats[dFmtNdx]; uint32_t stencilFormat = stencilFormats[sFmtNdx]; bool depth = false; bool stencil = false; string fmtName; if (depthFormat != GL_NONE) { tcu::TextureFormat info = glu::mapGLInternalFormat(depthFormat); fmtName += getFormatName(depthFormat); if (info.order == tcu::TextureFormat::D || info.order == tcu::TextureFormat::DS) depth = true; if (info.order == tcu::TextureFormat::DS) stencil = true; } if (stencilFormat != GL_NONE) { // Do not try separate stencil along with a combined depth/stencil if (stencil) continue; if (depthFormat != GL_NONE) fmtName += "_"; fmtName += getFormatName(stencilFormat); stencil = true; } if (!stencil && !depth) continue; uint32_t buffers = (depth ? GL_DEPTH_BUFFER_BIT : 0) | (stencil ? GL_STENCIL_BUFFER_BIT : 0); depthStencilGroup->addChild(new BlitDepthStencilCase( m_context, (fmtName + "_basic").c_str(), "", depthFormat, stencilFormat, buffers, IVec2(128, 128), IVec4(0, 0, 128, 128), buffers, IVec2(128, 128), IVec4(0, 0, 128, 128), buffers)); depthStencilGroup->addChild(new BlitDepthStencilCase( m_context, (fmtName + "_scale").c_str(), "", depthFormat, stencilFormat, buffers, IVec2(127, 119), IVec4(10, 30, 100, 70), buffers, IVec2(111, 130), IVec4(20, 5, 80, 130), buffers)); if (depth && stencil) { depthStencilGroup->addChild( new BlitDepthStencilCase(m_context, (fmtName + "_depth_only").c_str(), "", depthFormat, stencilFormat, buffers, IVec2(128, 128), IVec4(0, 0, 128, 128), buffers, IVec2(128, 128), IVec4(0, 0, 128, 128), GL_DEPTH_BUFFER_BIT)); depthStencilGroup->addChild(new BlitDepthStencilCase( m_context, (fmtName + "_stencil_only").c_str(), "", depthFormat, stencilFormat, buffers, IVec2(128, 128), IVec4(0, 0, 128, 128), buffers, IVec2(128, 128), IVec4(0, 0, 128, 128), GL_STENCIL_BUFFER_BIT)); } } } } // .default_framebuffer { static const struct { const char *name; DefaultFramebufferBlitCase::BlitArea area; } areas[] = { {"scale", DefaultFramebufferBlitCase::AREA_SCALE}, {"out_of_bounds", DefaultFramebufferBlitCase::AREA_OUT_OF_BOUNDS}, }; tcu::TestCaseGroup *defaultFbGroup = new tcu::TestCaseGroup(m_testCtx, "default_framebuffer", "Blits with default framebuffer"); addChild(defaultFbGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); fmtNdx++) { const uint32_t format = colorFormats[fmtNdx]; const tcu::TextureFormat texFmt = glu::mapGLInternalFormat(format); const tcu::TextureChannelClass fmtClass = tcu::getTextureChannelClass(texFmt.type); const uint32_t filter = glu::isGLInternalColorFormatFilterable(format) ? GL_LINEAR : GL_NEAREST; const bool filterable = glu::isGLInternalColorFormatFilterable(format); if (fmtClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT && fmtClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT && fmtClass != tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT) continue; // Conversion not supported. defaultFbGroup->addChild( new BlitDefaultFramebufferCase(m_context, getFormatName(format), "", format, filter)); for (int areaNdx = 0; areaNdx < DE_LENGTH_OF_ARRAY(areas); areaNdx++) { const string name = string(areas[areaNdx].name); const bool addLinear = filterable; const bool addNearest = !addLinear || (areas[areaNdx].area != DefaultFramebufferBlitCase:: AREA_OUT_OF_BOUNDS); // No need to check out-of-bounds with different filtering if (addNearest) { defaultFbGroup->addChild(new DefaultFramebufferBlitCase( m_context, (std::string(getFormatName(format)) + "_nearest_" + name + "_blit_from_default").c_str(), "", format, GL_NEAREST, DefaultFramebufferBlitCase::BLIT_DEFAULT_TO_TARGET, areas[areaNdx].area)); defaultFbGroup->addChild(new DefaultFramebufferBlitCase( m_context, (std::string(getFormatName(format)) + "_nearest_" + name + "_blit_to_default").c_str(), "", format, GL_NEAREST, DefaultFramebufferBlitCase::BLIT_TO_DEFAULT_FROM_TARGET, areas[areaNdx].area)); } if (addLinear) { defaultFbGroup->addChild(new DefaultFramebufferBlitCase( m_context, (std::string(getFormatName(format)) + "_linear_" + name + "_blit_from_default").c_str(), "", format, GL_LINEAR, DefaultFramebufferBlitCase::BLIT_DEFAULT_TO_TARGET, areas[areaNdx].area)); defaultFbGroup->addChild(new DefaultFramebufferBlitCase( m_context, (std::string(getFormatName(format)) + "_linear_" + name + "_blit_to_default").c_str(), "", format, GL_LINEAR, DefaultFramebufferBlitCase::BLIT_TO_DEFAULT_FROM_TARGET, areas[areaNdx].area)); } } } } } } // namespace Functional } // namespace gles3 } // namespace deqp