C++程序  |  394行  |  11.92 KB

/*-------------------------------------------------------------------------
 * drawElements Quality Program EGL 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 Color clear case.
 *//*--------------------------------------------------------------------*/

#include "teglColorClearCase.hpp"
#include "tcuTestLog.hpp"
#include "deRandom.hpp"
#include "deString.h"
#include "tcuImageCompare.hpp"
#include "tcuVector.hpp"
#include "tcuTextureUtil.hpp"
#include "deThread.hpp"
#include "deSemaphore.hpp"
#include "deSharedPtr.hpp"
#include "teglGLES1RenderUtil.hpp"
#include "teglGLES2RenderUtil.hpp"
#include "teglVGRenderUtil.hpp"

#include <memory>
#include <iterator>

#include <EGL/eglext.h>

#if !defined(EGL_OPENGL_ES3_BIT_KHR)
#	define EGL_OPENGL_ES3_BIT_KHR	0x0040
#endif
#if !defined(EGL_CONTEXT_MAJOR_VERSION_KHR)
#	define EGL_CONTEXT_MAJOR_VERSION_KHR EGL_CONTEXT_CLIENT_VERSION
#endif

using tcu::TestLog;
using tcu::RGBA;

using std::vector;

namespace deqp
{
namespace egl
{

// Utilities.

struct ClearOp
{
	ClearOp (int x_, int y_, int width_, int height_, const tcu::RGBA& color_)
		: x			(x_)
		, y			(y_)
		, width		(width_)
		, height	(height_)
		, color		(color_)
	{
	}

	ClearOp (void)
		: x			(0)
		, y			(0)
		, width		(0)
		, height	(0)
		, color		(0)
	{
	}

	int			x;
	int			y;
	int			width;
	int			height;
	tcu::RGBA	color;
};

static ClearOp computeRandomClear (de::Random& rnd, int width, int height)
{
	int			w		= rnd.getInt(1, width);
	int			h		= rnd.getInt(1, height);
	int			x		= rnd.getInt(0, width-w);
	int			y		= rnd.getInt(0, height-h);
	tcu::RGBA	col		(rnd.getUint32());

	return ClearOp(x, y, w, h, col);
}

static void renderReference (tcu::Surface& dst, const vector<ClearOp>& clears, const tcu::PixelFormat& pixelFormat)
{
	for (vector<ClearOp>::const_iterator clearIter = clears.begin(); clearIter != clears.end(); clearIter++)
	{
		tcu::PixelBufferAccess access = tcu::getSubregion(dst.getAccess(), clearIter->x, clearIter->y, 0, clearIter->width, clearIter->height, 1);
		tcu::clear(access, pixelFormat.convertColor(clearIter->color).toIVec());
	}
}

static void renderClear (EGLint api, const ClearOp& clear)
{
	switch (api)
	{
		case EGL_OPENGL_ES_BIT:			gles1::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());	break;
		case EGL_OPENGL_ES2_BIT:		gles2::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());	break;
		case EGL_OPENGL_ES3_BIT_KHR:	gles2::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());	break;
		case EGL_OPENVG_BIT:			vg::clear	(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());	break;
		default:
			DE_ASSERT(DE_FALSE);
	}
}

static void readPixels (EGLint api, tcu::Surface& dst)
{
	switch (api)
	{
		case EGL_OPENGL_ES_BIT:			gles1::readPixels	(dst, 0, 0, dst.getWidth(), dst.getHeight());	break;
		case EGL_OPENGL_ES2_BIT:		gles2::readPixels	(dst, 0, 0, dst.getWidth(), dst.getHeight());	break;
		case EGL_OPENGL_ES3_BIT_KHR:	gles2::readPixels	(dst, 0, 0, dst.getWidth(), dst.getHeight());	break;
		case EGL_OPENVG_BIT:			vg::readPixels		(dst, 0, 0, dst.getWidth(), dst.getHeight());	break;
		default:
			DE_ASSERT(DE_FALSE);
	}
}

// SingleThreadColorClearCase

SingleThreadColorClearCase::SingleThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const std::vector<EGLint>& configIds, int numContextsPerApi)
	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, configIds, numContextsPerApi)
{
}

void SingleThreadColorClearCase::executeForContexts (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config, const std::vector<std::pair<EGLint, tcu::egl::Context*> >& contexts)
{
	int					width		= surface.getWidth();
	int					height		= surface.getHeight();

	TestLog&			log			= m_testCtx.getLog();

	tcu::Surface		refFrame	(width, height);
	tcu::Surface		frame		(width, height);
	tcu::PixelFormat	pixelFmt;

	de::Random			rnd			(deStringHash(getName()));
	vector<ClearOp>		clears;
	const int			ctxClears	= 2;
	const int			numIters	= 3;

	// Query pixel format.
	display.describeConfig(config, pixelFmt);

	// Clear to black using first context.
	{
		EGLint				api			= contexts[0].first;
		tcu::egl::Context*	context		= contexts[0].second;
		ClearOp				clear		(0, 0, width, height, RGBA::black);

		eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext());
		TCU_CHECK_EGL();

		renderClear(api, clear);
		clears.push_back(clear);
	}

	// Render.
	for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
	{
		for (vector<std::pair<EGLint, tcu::egl::Context*> >::const_iterator ctxIter = contexts.begin(); ctxIter != contexts.end(); ctxIter++)
		{
			EGLint				api			= ctxIter->first;
			tcu::egl::Context*	context		= ctxIter->second;

			eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext());
			TCU_CHECK_EGL();

			for (int clearNdx = 0; clearNdx < ctxClears; clearNdx++)
			{
				ClearOp clear = computeRandomClear(rnd, width, height);

				renderClear(api, clear);
				clears.push_back(clear);
			}
		}
	}

	// Read pixels using first context. \todo [pyry] Randomize?
	{
		EGLint				api		= contexts[0].first;
		tcu::egl::Context*	context	= contexts[0].second;

		eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext());
		TCU_CHECK_EGL();

		readPixels(api, frame);
	}

	// Render reference.
	renderReference(refFrame, clears, pixelFmt);

	// Compare images
	{
		bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);

		if (!imagesOk)
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
	}
}

// MultiThreadColorClearCase

enum
{
	NUM_CLEARS_PER_PACKET	= 2 //!< Number of clears performed in one context activation in one thread.
};

class ColorClearThread;

typedef de::SharedPtr<ColorClearThread>	ColorClearThreadSp;
typedef de::SharedPtr<de::Semaphore>	SemaphoreSp;

struct ClearPacket
{
	ClearPacket (void)
	{
	}

	ClearOp			clears[NUM_CLEARS_PER_PACKET];
	SemaphoreSp		wait;
	SemaphoreSp		signal;
};

class ColorClearThread : public de::Thread
{
public:
	ColorClearThread (tcu::egl::Display& display, tcu::egl::Surface& surface, tcu::egl::Context& context, EGLint api, const std::vector<ClearPacket>& packets)
		: m_display	(display)
		, m_surface	(surface)
		, m_context	(context)
		, m_api		(api)
		, m_packets	(packets)
	{
	}

	void run (void)
	{
		for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
		{
			// Wait until it is our turn.
			packetIter->wait->decrement();

			// Acquire context.
			eglMakeCurrent(m_display.getEGLDisplay(), m_surface.getEGLSurface(), m_surface.getEGLSurface(), m_context.getEGLContext());

			// Execute clears.
			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++)
				renderClear(m_api, packetIter->clears[ndx]);

			// Release context.
			eglMakeCurrent(m_display.getEGLDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

			// Signal completion.
			packetIter->signal->increment();
		}
	}

private:
	tcu::egl::Display&				m_display;
	tcu::egl::Surface&				m_surface;
	tcu::egl::Context&				m_context;
	EGLint							m_api;
	const std::vector<ClearPacket>&	m_packets;
};

MultiThreadColorClearCase::MultiThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const std::vector<EGLint>& configIds, int numContextsPerApi)
	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, configIds, numContextsPerApi)
{
}

void MultiThreadColorClearCase::executeForContexts (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config, const std::vector<std::pair<EGLint, tcu::egl::Context*> >& contexts)
{
	int					width		= surface.getWidth();
	int					height		= surface.getHeight();

	TestLog&			log			= m_testCtx.getLog();

	tcu::Surface		refFrame	(width, height);
	tcu::Surface		frame		(width, height);
	tcu::PixelFormat	pixelFmt;

	de::Random			rnd			(deStringHash(getName()));

	// Query pixel format.
	display.describeConfig(config, pixelFmt);

	// Create clear packets.
	const int						numPacketsPerThread		= 2;
	int								numThreads				= (int)contexts.size();
	int								numPackets				= numThreads * numPacketsPerThread;

	vector<SemaphoreSp>				semaphores				(numPackets+1);
	vector<vector<ClearPacket> >	packets					(numThreads);
	vector<ColorClearThreadSp>		threads					(numThreads);

	// Initialize semaphores.
	for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
		*sem = SemaphoreSp(new de::Semaphore(0));

	// Create packets.
	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
	{
		packets[threadNdx].resize(numPacketsPerThread);

		for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
		{
			ClearPacket& packet = packets[threadNdx][packetNdx];

			// Threads take turns with packets.
			packet.wait		= semaphores[packetNdx*numThreads + threadNdx];
			packet.signal	= semaphores[packetNdx*numThreads + threadNdx + 1];

			for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
			{
				// First clear is always full-screen black.
				if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0)
					packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black);
				else
					packet.clears[clearNdx] = computeRandomClear(rnd, width, height);
			}
		}
	}

	// Create and launch threads (actual rendering starts once first semaphore is signaled).
	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
	{
		threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(display, surface, *contexts[threadNdx].second, contexts[threadNdx].first, packets[threadNdx]));
		threads[threadNdx]->start();
	}

	// Signal start and wait until complete.
	semaphores.front()->increment();
	semaphores.back()->decrement();

	// Read pixels using first context. \todo [pyry] Randomize?
	{
		EGLint				api		= contexts[0].first;
		tcu::egl::Context*	context	= contexts[0].second;

		eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext());
		TCU_CHECK_EGL();

		readPixels(api, frame);
	}

	// Join threads.
	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
		threads[threadNdx]->join();

	// Render reference.
	for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
	{
		for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
		{
			const ClearPacket& packet = packets[threadNdx][packetNdx];
			for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
			{
				tcu::PixelBufferAccess access = tcu::getSubregion(refFrame.getAccess(),
																  packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0,
																  packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1);
				tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec());
			}
		}
	}

	// Compare images
	{
		bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);

		if (!imagesOk)
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
	}
}

} // egl
} // deqp