/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 2.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 Screen clearing test. *//*--------------------------------------------------------------------*/ #include "es2fColorClearTest.hpp" #include "tcuRGBA.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" #include "gluRenderContext.hpp" #include "gluPixelTransfer.hpp" #include "tcuVector.hpp" #include "tcuRenderTarget.hpp" #include "deRandom.hpp" #include "deInt32.h" #include "glwFunctions.hpp" #include "glwEnums.hpp" #include using tcu::RGBA; using tcu::Surface; using tcu::TestLog; using namespace std; namespace deqp { namespace gles2 { namespace Functional { class ColorClearCase : public TestCase { public: ColorClearCase(Context& context, const char* name, int numIters, int numClearsMin, int numClearsMax, bool testAlpha, bool testScissoring, bool testColorMasks, bool firstClearFull) : TestCase (context, name, name) , m_numIters (numIters) , m_numClearsMin (numClearsMin) , m_numClearsMax (numClearsMax) , m_testAlpha (testAlpha) , m_testScissoring (testScissoring) , m_testColorMasks (testColorMasks) , m_firstClearFull (firstClearFull) { m_curIter = 0; } virtual ~ColorClearCase (void) { } virtual IterateResult iterate (void); private: const int m_numIters; const int m_numClearsMin; const int m_numClearsMax; const bool m_testAlpha; const bool m_testScissoring; const bool m_testColorMasks; const bool m_firstClearFull; int m_curIter; }; class ClearInfo { public: ClearInfo (const tcu::IVec4& rect, deUint8 colorMask, tcu::RGBA color) : m_rect(rect), m_colorMask(colorMask), m_color(color) { } tcu::IVec4 m_rect; deUint8 m_colorMask; tcu::RGBA m_color; }; TestCase::IterateResult ColorClearCase::iterate (void) { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const tcu::RenderTarget& renderTarget = m_context.getRenderTarget(); const tcu::PixelFormat& pixelFormat = renderTarget.getPixelFormat(); const int targetWidth = renderTarget.getWidth(); const int targetHeight = renderTarget.getHeight(); const int numPixels = targetWidth * targetHeight; de::Random rnd (deInt32Hash(m_curIter)); vector pixelKnownChannelMask (numPixels, 0); Surface refImage (targetWidth, targetHeight); Surface resImage (targetWidth, targetHeight); Surface diffImage (targetWidth, targetHeight); int numClears = rnd.getUint32() % (m_numClearsMax + 1 - m_numClearsMin) + m_numClearsMin; std::vector clearOps; if (m_testScissoring) gl.enable(GL_SCISSOR_TEST); for (int clearNdx = 0; clearNdx < numClears; clearNdx++) { // Rectangle. int clearX; int clearY; int clearWidth; int clearHeight; if (!m_testScissoring || (clearNdx == 0 && m_firstClearFull)) { clearX = 0; clearY = 0; clearWidth = targetWidth; clearHeight = targetHeight; } else { clearX = (rnd.getUint32() % (2*targetWidth)) - targetWidth; clearY = (rnd.getUint32() % (2*targetHeight)) - targetHeight; clearWidth = (rnd.getUint32() % targetWidth); clearHeight = (rnd.getUint32() % targetHeight); } gl.scissor(clearX, clearY, clearWidth, clearHeight); // Color. int r = (int)(rnd.getUint32() & 0xFF); int g = (int)(rnd.getUint32() & 0xFF); int b = (int)(rnd.getUint32() & 0xFF); int a = m_testAlpha ? (int)(rnd.getUint32() & 0xFF) : 0xFF; RGBA clearCol(r, g, b, a); gl.clearColor(float(r)/255.0f, float(g)/255.0f, float(b)/255.0f, float(a)/255.0f); // Mask. deUint8 clearMask; if (!m_testColorMasks || (clearNdx == 0 && m_firstClearFull)) clearMask = 0xF; else clearMask = (rnd.getUint32() & 0xF); gl.colorMask((clearMask&0x1) != 0, (clearMask&0x2) != 0, (clearMask&0x4) != 0, (clearMask&0x8) != 0); // Clear & store op. gl.clear(GL_COLOR_BUFFER_BIT); clearOps.push_back(ClearInfo(tcu::IVec4(clearX, clearY, clearWidth, clearHeight), clearMask, clearCol)); // Let watchdog know we're alive. m_testCtx.touchWatchdog(); } // Compute reference image. { for (int y = 0; y < targetHeight; y++) { std::vector scanlineClearOps; // Find all rectangles affecting this scanline. for (int opNdx = 0; opNdx < (int)clearOps.size(); opNdx++) { ClearInfo& op = clearOps[opNdx]; if (de::inBounds(y, op.m_rect.y(), op.m_rect.y()+op.m_rect.w())) scanlineClearOps.push_back(op); } // Compute reference for scanline. int x = 0; while (x < targetWidth) { tcu::RGBA spanColor; deUint8 spanKnownMask = 0; int spanLength = (targetWidth - x); for (int opNdx = (int)scanlineClearOps.size() - 1; opNdx >= 0; opNdx--) { const ClearInfo& op = scanlineClearOps[opNdx]; if (de::inBounds(x, op.m_rect.x(), op.m_rect.x()+op.m_rect.z()) && de::inBounds(y, op.m_rect.y(), op.m_rect.y()+op.m_rect.w())) { // Compute span length until end of given rectangle. spanLength = deMin32(spanLength, op.m_rect.x() + op.m_rect.z() - x); tcu::RGBA clearCol = op.m_color; deUint8 clearMask = op.m_colorMask; if ((clearMask & 0x1) && !(spanKnownMask & 0x1)) spanColor.setRed(clearCol.getRed()); if ((clearMask & 0x2) && !(spanKnownMask & 0x2)) spanColor.setGreen(clearCol.getGreen()); if ((clearMask & 0x4) && !(spanKnownMask & 0x4)) spanColor.setBlue(clearCol.getBlue()); if ((clearMask & 0x8) && !(spanKnownMask & 0x8)) spanColor.setAlpha(clearCol.getAlpha()); spanKnownMask |= clearMask; // Break if have all colors. if (spanKnownMask == 0xF) break; } else if (op.m_rect.x() > x) spanLength = deMin32(spanLength, op.m_rect.x() - x); } // Set reference alpha channel to 0xFF, if no alpha in target. if (pixelFormat.alphaBits == 0) spanColor.setAlpha(0xFF); // Fill the span. for (int ndx = 0; ndx < spanLength; ndx++) { refImage.setPixel(x + ndx, y, spanColor); pixelKnownChannelMask[y*targetWidth + x + ndx] |= spanKnownMask; } x += spanLength; } } } glu::readPixels(m_context.getRenderContext(), 0, 0, resImage.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels"); // Compute difference image. RGBA colorThreshold = pixelFormat.getColorThreshold(); RGBA matchColor(0, 255, 0, 255); RGBA diffColor(255, 0, 0, 255); RGBA maxDiff(0, 0, 0, 0); bool isImageOk = true; if (gl.isEnabled(GL_DITHER)) { colorThreshold.setRed(colorThreshold.getRed() + 1); colorThreshold.setGreen(colorThreshold.getGreen() + 1); colorThreshold.setBlue(colorThreshold.getBlue() + 1); colorThreshold.setAlpha(colorThreshold.getAlpha() + 1); } for (int y = 0; y < targetHeight; y++) for (int x = 0; x < targetWidth; x++) { int offset = (y*targetWidth + x); RGBA refRGBA = refImage.getPixel(x, y); RGBA resRGBA = resImage.getPixel(x, y); deUint8 colMask = pixelKnownChannelMask[offset]; RGBA diff = computeAbsDiffMasked(refRGBA, resRGBA, colMask); bool isPixelOk = diff.isBelowThreshold(colorThreshold); diffImage.setPixel(x, y, isPixelOk ? matchColor : diffColor); isImageOk = isImageOk && isPixelOk; maxDiff = max(maxDiff, diff); } if (isImageOk) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); m_testCtx.getLog() << tcu::TestLog::Message << "Image comparison failed, max diff = " << maxDiff << ", threshold = " << colorThreshold << tcu::TestLog::EndMessage; log << TestLog::ImageSet("Result", "Resulting framebuffer") << TestLog::Image("Result", "Resulting framebuffer", resImage) << TestLog::Image("Reference", "Reference image", refImage) << TestLog::Image("DiffMask", "Failing pixels", diffImage) << TestLog::EndImageSet; return TestCase::STOP; } bool isFinal = (++m_curIter == m_numIters); // On final frame, dump images. if (isFinal) { log << TestLog::ImageSet("Result", "Resulting framebuffer") << TestLog::Image("Result", "Resulting framebuffer", resImage) << TestLog::EndImageSet; } return isFinal ? TestCase::STOP : TestCase::CONTINUE; } ColorClearTest::ColorClearTest (Context& context) : TestCaseGroup(context, "color_clear", "Color Clear Tests") { } ColorClearTest::~ColorClearTest (void) { } void ColorClearTest::init (void) { // name iters, #..#, alpha?, scissor,masks, 1stfull? addChild(new ColorClearCase(m_context, "single_rgb", 30, 1,3, false, false, false, true )); addChild(new ColorClearCase(m_context, "single_rgba", 30, 1,3, true, false, false, true )); addChild(new ColorClearCase(m_context, "multiple_rgb", 15, 4,20, false, false, false, true )); addChild(new ColorClearCase(m_context, "multiple_rgba", 15, 4,20, true, false, false, true )); addChild(new ColorClearCase(m_context, "long_rgb", 2, 100,500, false, false, false, true )); addChild(new ColorClearCase(m_context, "long_rgba", 2, 100,500, true, false, false, true )); addChild(new ColorClearCase(m_context, "subclears_rgb", 15, 4,30, false, false, false, false )); addChild(new ColorClearCase(m_context, "subclears_rgba", 15, 4,30, true, false, false, false )); addChild(new ColorClearCase(m_context, "short_scissored_rgb", 30, 2,4, false, true, false, true )); addChild(new ColorClearCase(m_context, "scissored_rgb", 15, 4,30, false, true, false, true )); addChild(new ColorClearCase(m_context, "scissored_rgba", 15, 4,30, true, true, false, true )); addChild(new ColorClearCase(m_context, "masked_rgb", 15, 4,30, false, false, true, true )); addChild(new ColorClearCase(m_context, "masked_rgba", 15, 4,30, true, false, true, true )); addChild(new ColorClearCase(m_context, "masked_scissored_rgb", 15, 4,30, false, true, true, true )); addChild(new ColorClearCase(m_context, "masked_scissored_rgba", 15, 4,30, true, true, true, true )); addChild(new ColorClearCase(m_context, "complex_rgb", 15, 5,50, false, true, true, false )); addChild(new ColorClearCase(m_context, "complex_rgba", 15, 5,50, true, true, true, false )); addChild(new ColorClearCase(m_context, "long_masked_rgb", 2, 100,500, false, true, true, true )); addChild(new ColorClearCase(m_context, "long_masked_rgba", 2, 100,500, true, true, true, true )); } } // Functional } // gles2 } // deqp