summaryrefslogtreecommitdiff
path: root/src/graphics/testgpu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/testgpu.cpp')
-rw-r--r--src/graphics/testgpu.cpp530
1 files changed, 530 insertions, 0 deletions
diff --git a/src/graphics/testgpu.cpp b/src/graphics/testgpu.cpp
new file mode 100644
index 0000000..ee322e8
--- /dev/null
+++ b/src/graphics/testgpu.cpp
@@ -0,0 +1,530 @@
+//
+// libavg - Media Playback Engine.
+// Copyright (C) 2003-2014 Ulrich von Zadow
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Current versions can be found at www.libavg.de
+//
+
+#include "GraphicsTest.h"
+#include "GLTexture.h"
+#include "BitmapLoader.h"
+#include "GPUBrightnessFilter.h"
+#include "GPUBlurFilter.h"
+#include "GPUBandpassFilter.h"
+#include "GPUChromaKeyFilter.h"
+#include "GPUHueSatFilter.h"
+#include "GPUInvertFilter.h"
+#include "GPURGB2YUVFilter.h"
+#include "FilterResizeBilinear.h"
+#include "GLContext.h"
+#include "ShaderRegistry.h"
+#include "BmpTextureMover.h"
+#include "PBO.h"
+
+#include "../base/TestSuite.h"
+#include "../base/Exception.h"
+#include "../base/Test.h"
+#include "../base/StringHelper.h"
+
+#include <math.h>
+#include <iostream>
+
+#include <glib-object.h>
+
+using namespace avg;
+using namespace std;
+
+
+class BandpassFilterTest: public GraphicsTest {
+public:
+ BandpassFilterTest()
+ : GraphicsTest("BandpassFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ runImageTests("spike", B8G8R8X8);
+ runImageTests("i8-64x64", I8);
+ }
+
+private:
+ void runImageTests(const string& sFName, PixelFormat pf)
+ {
+ cerr << " Testing " << sFName << endl;
+ BitmapPtr pBmp = loadTestBmp(sFName, pf);
+ GPUBandpassFilter f(pBmp->getSize(), pf, 0.5, 1.5, 1, false);
+ BitmapPtr pDestBmp = f.apply(pBmp);
+ TEST(fabs(pDestBmp->getAvg() -128) < 0.06);
+ testEqual(*pDestBmp, "bandpass_"+sFName, pf, 0.2, 0.5);
+ TEST(pDestBmp->getPixelFormat() == pf);
+ }
+};
+
+
+class BlurFilterTest: public GraphicsTest {
+public:
+ BlurFilterTest()
+ : GraphicsTest("BlurFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+/*
+ // This has the effect of printing out all the brightness differences for
+ //different kernel sizes.
+ BitmapPtr pDestBmp;
+ pBmp = loadTestBmp("spike");
+ for (float stddev = 0.5f; stddev < 5; stddev += 0.25f) {
+ pDestBmp = GPUBlurFilter(pBmp->getSize(), pBmp->getPixelFormat(), stddev)
+ .apply(pBmp);
+ testEqualBrightness(*pDestBmp, *pBmp, 1);
+ }
+*/
+ runFilterTests(false);
+ if (GLTexture::isFloatFormatSupported()) {
+ runFilterTests(true);
+ }
+ }
+
+private:
+ void runFilterTests(bool bUseFloat)
+ {
+ BitmapPtr pBmp;
+ GPUBlurFilterPtr pFilter;
+ pBmp = loadTestBmp("spike");
+ PixelFormat destPF;
+ if (bUseFloat) {
+ destPF = R32G32B32A32F;
+ } else {
+ destPF = B8G8R8A8;
+ }
+ pFilter = GPUBlurFilterPtr(new GPUBlurFilter(pBmp->getSize(),
+ pBmp->getPixelFormat(), destPF, 0.5f, false));
+ runImageTest(pBmp, pFilter, 0.5f, "blur05_spike");
+ runImageTest(pBmp, pFilter, 1, "blur1_spike");
+ runImageTest(pBmp, pFilter, 3, "blur3_spike");
+
+ pBmp = loadTestBmp("flat");
+ pFilter = GPUBlurFilterPtr(new GPUBlurFilter(pBmp->getSize(),
+ pBmp->getPixelFormat(), destPF, 5, false));
+ runImageTest(pBmp, pFilter, 5, "blur05_flat", true);
+
+ runImageTest("rgb24-64x64", destPF);
+ runImageTest("rgb24alpha-64x64", destPF);
+
+ }
+
+ void runImageTest(const string& sFName, PixelFormat destPF)
+ {
+ BitmapPtr pBmp = loadTestBmp(sFName);
+ GPUBlurFilterPtr pFilter(new GPUBlurFilter(pBmp->getSize(),
+ pBmp->getPixelFormat(), destPF, 2, false));
+ runImageTest(pBmp, pFilter, 2, string("blur_")+sFName, true);
+ }
+
+ void runImageTest(BitmapPtr pBmp, GPUBlurFilterPtr pFilter, float stdDev,
+ string sBmpName, bool bIgnoreBrightness = false)
+ {
+ cerr << " Testing " << sBmpName << ", stddev " << stdDev << endl;
+ pFilter->setStdDev(stdDev);
+ BitmapPtr pDestBmp = pFilter->apply(pBmp);
+ if (!bIgnoreBrightness) {
+ testEqualBrightness(*pDestBmp, *pBmp, 0.03);
+ }
+ testEqual(*pDestBmp, sBmpName, pDestBmp->getPixelFormat(), 0.1, 0.3);
+ }
+
+};
+
+
+class BrightnessFilterTest: public GraphicsTest {
+public:
+ BrightnessFilterTest()
+ : GraphicsTest("BrightnessFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ runImageTests("rgb24-64x64");
+ runImageTests("rgb24alpha-64x64");
+ }
+
+private:
+ void runImageTests(const string& sFName)
+ {
+ cerr << " Testing " << sFName << endl;
+ BitmapPtr pBmp = loadTestBmp(sFName);
+ PixelFormat pf = pBmp->getPixelFormat();
+ cerr << " Source Bmp: " << pf << endl;
+ BitmapPtr pDestBmp;
+ pDestBmp = GPUBrightnessFilter(pBmp->getSize(), pixelFormatHasAlpha(pf), 1)
+ .apply(pBmp);
+ float maxAverage, maxStdDev;
+ if (GLContext::getCurrent()->isGLES()) {
+ // less strict (lower floating point precision?)
+ maxAverage = 0.5;
+ maxStdDev = 1.5;
+ }
+ else {
+ maxAverage = 0.2;
+ maxStdDev = 0.5;
+ }
+ testEqual(*pDestBmp, *pBmp, string("brightness_")+sFName, maxAverage, maxStdDev);
+ }
+};
+
+
+class ChromaKeyFilterTest: public GraphicsTest {
+public:
+ ChromaKeyFilterTest()
+ : GraphicsTest("ChromaKeyFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ BitmapPtr pBmp = loadTestBmp("chromakey");
+ BitmapPtr pDestBmp;
+ GPUChromaKeyFilter filter(pBmp->getSize());
+ for (int erosion = 0; erosion < 3; ++erosion) {
+ filter.setParams(Pixel32(0,255,0), 0.1, 0.2, 0.1, 0.1, erosion, 0);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "ChromaKeyResult"+toString(erosion), B8G8R8A8, 0.3,
+ 0.7);
+ }
+ filter.setParams(Pixel32(0,255,0), 0.0, 0.0, 0.0, 0.0, 0, 0.1);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "ChromaKeySpillResult1", B8G8R8A8, 0.3, 0.7);
+ filter.setParams(Pixel32(0,255,0), 0.1, 0.1, 0.1, 0.0, 0, 0.1);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "ChromaKeySpillResult2", B8G8R8A8, 0.3, 0.7);
+ filter.setParams(Pixel32(0,255,0), 0.1, 0.1, 0.1, 0.0, 0, 0.2);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "ChromaKeySpillResult3", B8G8R8A8, 0.3, 0.7);
+
+ pBmp = loadTestBmp("chromakey-median");
+ filter.setParams(Pixel32(0,255,0), 0.1, 0.1, 0.1, 0.0, 0, 0.0);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "ChromaKeyMedianResult", B8G8R8A8, 1, 6);
+ }
+};
+
+
+class HueSatFilterTest: public GraphicsTest {
+public:
+ HueSatFilterTest()
+ : GraphicsTest("HueSatFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ BitmapPtr pBmp = loadTestBmp("hsl");
+ BitmapPtr pDestBmp;
+ GPUHueSatFilter filter(pBmp->getSize(), true);
+ //Test hue functionality
+ for (int run = 0; run < 3; run++) {
+ filter.setParams(run*90);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "HslHueResult"+toString(run), NO_PIXELFORMAT, 0, 0);
+ }
+ //Test colorize functionality
+ for (int run = 0; run < 3; run++) {
+ filter.setParams(run*90, 1, 0, true);
+ pDestBmp = filter.apply(pBmp);
+ testEqual(*pDestBmp, "HslColorizeResult"+toString(run), NO_PIXELFORMAT, 0, 0);
+ }
+ }
+};
+
+
+class InvertFilterTest: public GraphicsTest {
+public:
+ InvertFilterTest()
+ : GraphicsTest("InvertFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ runImageTests("rgb24-64x64");
+ }
+
+private:
+ void runImageTests(const string& sFName)
+ {
+ cerr << " Testing " << sFName << endl;
+ BitmapPtr pBmp = loadTestBmp(sFName);
+ BitmapPtr pDestBmp;
+ pDestBmp = GPUInvertFilter(pBmp->getSize(), false).apply(pBmp);
+ float maxAverage, maxStdDev;
+ if (GLContext::getCurrent()->isGLES()) {
+ // less strict (lower floating point precision?)
+ maxAverage = 0.6;
+ maxStdDev = 2.0;
+ }
+ else {
+ maxAverage = 0.0;
+ maxStdDev = 0.0;
+ }
+ testEqual(*pDestBmp, string("invert_")+sFName, pBmp->getPixelFormat(),
+ maxAverage, maxStdDev);
+ }
+};
+
+
+class RGB2YUVFilterTest: public GraphicsTest {
+public:
+ RGB2YUVFilterTest()
+ : GraphicsTest("RGB2YUVFilterTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ BitmapPtr pOrigBmp = loadTestBmp("rgb24-64x64");
+ GLTexturePtr pTex = GLTexturePtr(new GLTexture(pOrigBmp->getSize(),
+ pOrigBmp->getPixelFormat()));
+ pTex->moveBmpToTexture(pOrigBmp);
+ GPURGB2YUVFilter f(pOrigBmp->getSize());
+ f.apply(pTex);
+ BitmapPtr pResultBmp = f.getResults();
+ pResultBmp = convertYUVX444ToRGB(pResultBmp);
+ testEqual(*pResultBmp, *pOrigBmp, "RGB2YUV", 1, 2);
+ }
+
+ BitmapPtr convertYUVX444ToRGB(const BitmapPtr& pYUVBmp)
+ {
+ // This is a weird pixel format that's not used anywhere else, so support
+ // hasn't been moved to the Bitmap class.
+ BitmapPtr pRGBBmp(new Bitmap(pYUVBmp->getSize(), B8G8R8X8));
+ int height = pRGBBmp->getSize().y;
+ int width = pRGBBmp->getSize().y;
+ int strideInPixels = pRGBBmp->getStride()/4;
+
+ for (int y = 0; y < height; ++y) {
+ const unsigned char * pSrc = pYUVBmp->getPixels() + pYUVBmp->getStride()*y;
+ Pixel32 * pDest = (Pixel32*)pRGBBmp->getPixels() + strideInPixels*y;
+ for (int x = 0; x < width; x++) {
+ YUVJtoBGR32Pixel(pDest, pSrc[0], pSrc[1], pSrc[2]);
+ pSrc += 4;
+ pDest ++;
+ }
+ }
+ return pRGBBmp;
+ }
+};
+
+
+class TextureMoverTest: public GraphicsTest {
+public:
+ TextureMoverTest()
+ : GraphicsTest("TextureMoverTest", 2)
+ {
+ }
+
+ void runTests()
+ {
+ for (int i=1; i<2; ++i) {
+ bool bPOT = (i==1);
+ if (GLContext::getCurrent()->arePBOsSupported()) {
+ runImageTest(bPOT, MM_PBO, "rgb24-65x65");
+ runImageTest(bPOT, MM_PBO, "rgb24alpha-64x64");
+ }
+ runImageTest(bPOT, MM_OGL, "rgb24-65x65");
+ runImageTest(bPOT, MM_OGL, "rgb24alpha-64x64");
+ }
+ runCompressionTest(MM_OGL, "rgb24-65x65");
+ if (GLContext::getCurrent()->arePBOsSupported()) {
+ runCompressionTest(MM_PBO, "rgb24-65x65");
+ runMipmapTest(MM_PBO, "rgb24alpha-64x64");
+ runMipmapTest(MM_PBO, "rgb24-65x65");
+ }
+ runMipmapTest(MM_OGL, "rgb24-64x64");
+ runMipmapTest(MM_OGL, "rgb24alpha-64x64");
+ runMipmapTest(MM_OGL, "rgb24-65x65");
+ }
+
+private:
+ void runImageTest(bool bPOT, OGLMemoryMode memoryMode, const string& sFName)
+ {
+ string sResultFName = sFName + "-" + oglMemoryMode2String(memoryMode) + "-";
+ if (bPOT) {
+ sResultFName += "pot";
+ } else {
+ sResultFName += "npot";
+ }
+ cerr << " Testing " << sResultFName << endl;
+ BitmapPtr pOrigBmp = loadTestBmp(sFName);
+ {
+ cerr << " move functions." << endl;
+ GLTexturePtr pTex = GLTexturePtr(new GLTexture(pOrigBmp->getSize(),
+ pOrigBmp->getPixelFormat(), false, 0, GL_CLAMP_TO_EDGE,
+ GL_CLAMP_TO_EDGE, bPOT));
+ TextureMoverPtr pWriteMover = TextureMover::create(memoryMode,
+ pOrigBmp->getSize(), pOrigBmp->getPixelFormat(), GL_DYNAMIC_DRAW);
+ pWriteMover->moveBmpToTexture(pOrigBmp, *pTex);
+ BitmapPtr pDestBmp = pTex->moveTextureToBmp();
+ testEqual(*pDestBmp, *pOrigBmp, sResultFName+"-move", 0.01, 0.1);
+ }
+ {
+ cerr << " lock functions." << endl;
+ GLTexturePtr pTex = GLTexturePtr(new GLTexture(pOrigBmp->getSize(),
+ pOrigBmp->getPixelFormat(), false, 0, GL_CLAMP_TO_EDGE,
+ GL_CLAMP_TO_EDGE, bPOT));
+ TextureMoverPtr pMover = TextureMover::create(memoryMode,
+ pOrigBmp->getSize(), pOrigBmp->getPixelFormat(), GL_DYNAMIC_DRAW);
+ BitmapPtr pTransferBmp = pMover->lock();
+ pTransferBmp->copyPixels(*pOrigBmp);
+ pMover->unlock();
+ pMover->moveToTexture(*pTex);
+ BitmapPtr pDestBmp = pTex->moveTextureToBmp();
+ testEqual(*pDestBmp, *pOrigBmp, sResultFName+"-lock", 0.01, 0.1);
+ }
+ }
+
+ void runMipmapTest(OGLMemoryMode memoryMode, const string& sFName)
+ {
+ cerr << " Testing mipmap support, " << sFName << ", " <<
+ oglMemoryMode2String(memoryMode) << endl;
+ BitmapPtr pOrigBmp = loadTestBmp(sFName);
+ GLTexturePtr pTex = GLTexturePtr(new GLTexture(pOrigBmp->getSize(),
+ pOrigBmp->getPixelFormat(), true));
+ pTex->moveBmpToTexture(pOrigBmp);
+ pTex->generateMipmaps();
+
+ if (GLContext::getCurrent()->isGLES()) {
+ // GLES doesn't support attaching texture mipmap levels other than 0 to
+ // FBOs, so moveTextureToBmp() will fail. Skip result image comparison.
+ return;
+ }
+ BitmapPtr pResultBmp = pTex->moveTextureToBmp(1);
+ IntPoint newSize(pOrigBmp->getSize()/2);
+ TEST(pResultBmp->getSize() == newSize);
+ FilterResizeBilinear resizer(newSize);
+ BitmapPtr pBaselineBmp = resizer.apply(pOrigBmp);
+ string sName;
+ if (memoryMode == MM_PBO) {
+ sName = "pbo-mipmap";
+ } else {
+ sName = "ogl-mipmap";
+ }
+ testEqual(*pResultBmp, *pBaselineBmp, sName, 7, 15);
+ }
+
+ void runCompressionTest(OGLMemoryMode memoryMode, const string& sFName)
+ {
+ cerr << " Testing B5G6R5 compression, " << sFName << ", " <<
+ oglMemoryMode2String(memoryMode) << endl;
+ BitmapPtr pOrigBmp = loadTestBmp(sFName);
+ TextureMoverPtr pMover = TextureMover::create(memoryMode, pOrigBmp->getSize(),
+ B5G6R5, GL_STATIC_DRAW);
+ BitmapPtr pMoverBmp = pMover->lock();
+ pMoverBmp->copyPixels(*pOrigBmp);
+ pMover->unlock();
+ GLTexturePtr pTex = GLTexturePtr(new GLTexture(pMoverBmp->getSize(),
+ pMoverBmp->getPixelFormat(), false, 0, GL_CLAMP_TO_EDGE,
+ GL_CLAMP_TO_EDGE, false));
+ pMover->moveToTexture(*pTex);
+ BitmapPtr pDestBmp = pTex->moveTextureToBmp();
+ }
+};
+
+
+class GPUTestSuite: public TestSuite {
+public:
+ GPUTestSuite(const string& sVariant)
+ : TestSuite("GPUTestSuite ("+sVariant+")")
+ {
+ addTest(TestPtr(new TextureMoverTest));
+ addTest(TestPtr(new BrightnessFilterTest));
+ addTest(TestPtr(new HueSatFilterTest));
+ addTest(TestPtr(new InvertFilterTest));
+ if (GLContext::getCurrent()->getShaderUsage() == GLConfig::FULL) {
+ addTest(TestPtr(new RGB2YUVFilterTest));
+ addTest(TestPtr(new ChromaKeyFilterTest));
+ addTest(TestPtr(new BlurFilterTest));
+ if (GLTexture::isFloatFormatSupported()) {
+ addTest(TestPtr(new BandpassFilterTest));
+ }
+ }
+ }
+};
+
+
+bool runTests(bool bGLES, GLConfig::ShaderUsage su)
+{
+ string sVariant = string("GLES: ") + toString(bGLES) + ", ShaderUsage: " +
+ GLConfig::shaderUsageToString(su);
+ cerr << "---------------------------------------------------" << endl;
+ cerr << sVariant << endl;
+ cerr << "---------------------------------------------------" << endl;
+ GLContext* pContext = GLContext::create(GLConfig(bGLES, false, true, 1, su, true));
+ GLContext::setMain(pContext);
+ pContext->enableErrorChecks(true);
+ glDisable(GL_BLEND);
+ GLContext::checkError("glDisable(GL_BLEND)");
+ ShaderRegistry::get()->setShaderPath("./shaders");
+ try {
+ GPUTestSuite suite(sVariant);
+ suite.runTests();
+ delete pContext;
+ return suite.isOk();
+ } catch (Exception& ex) {
+ cerr << "Exception: " << ex.getStr() << endl;
+ delete pContext;
+ return false;
+ }
+}
+
+
+int main(int nargs, char** args)
+{
+ bool bOK = true;
+ try {
+#ifndef AVG_ENABLE_EGL
+ BitmapLoader::init(true);
+ bOK = runTests(false, GLConfig::FULL);
+ bOK &= runTests(false, GLConfig::MINIMAL);
+#endif
+ if (GLContext::isGLESSupported()) {
+ BitmapLoader::init(false);
+ bOK &= runTests(true, GLConfig::MINIMAL);
+ }
+ } catch (Exception& ex) {
+ if (ex.getCode() == AVG_ERR_ASSERT_FAILED) {
+ cerr << ex.getStr() << endl;
+ bOK = false;
+ } else {
+ cerr << "Skipping GPU imaging test." << endl;
+ cerr << "Reason: " << ex.getStr() << endl;
+ bOK = true;
+ }
+ }
+
+ if (bOK) {
+ cerr << "testgpu succeeded" << endl;
+ return 0;
+ } else {
+ cerr << "testgpu failed" << endl;
+ return 1;
+ }
+}
+