Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.0 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Randomized per-fragment operation tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es2fRandomFragmentOpTests.hpp"
     25 #include "glsFragmentOpUtil.hpp"
     26 #include "glsInteractionTestUtil.hpp"
     27 #include "tcuRenderTarget.hpp"
     28 #include "tcuTestLog.hpp"
     29 #include "tcuSurface.hpp"
     30 #include "tcuCommandLine.hpp"
     31 #include "tcuImageCompare.hpp"
     32 #include "tcuVectorUtil.hpp"
     33 #include "tcuTextureUtil.hpp"
     34 #include "gluPixelTransfer.hpp"
     35 #include "gluCallLogWrapper.hpp"
     36 #include "gluRenderContext.hpp"
     37 #include "deStringUtil.hpp"
     38 #include "deRandom.hpp"
     39 #include "deMath.h"
     40 #include "glwFunctions.hpp"
     41 #include "glwEnums.hpp"
     42 #include "rrFragmentOperations.hpp"
     43 #include "sglrReferenceUtils.hpp"
     44 
     45 #include <algorithm>
     46 
     47 namespace deqp
     48 {
     49 namespace gles2
     50 {
     51 namespace Functional
     52 {
     53 
     54 using std::vector;
     55 using tcu::TestLog;
     56 using tcu::Vec2;
     57 using tcu::Vec4;
     58 using tcu::IVec2;
     59 using tcu::BVec4;
     60 
     61 enum
     62 {
     63 	VIEWPORT_WIDTH				= 64,
     64 	VIEWPORT_HEIGHT				= 64,
     65 	NUM_CALLS_PER_ITERATION		= 3,
     66 	NUM_ITERATIONS_PER_CASE		= 10
     67 };
     68 
     69 static const tcu::Vec4		CLEAR_COLOR			(0.25f, 0.5f, 0.75f, 1.0f);
     70 static const float			CLEAR_DEPTH			= 1.0f;
     71 static const int			CLEAR_STENCIL		= 0;
     72 static const bool			ENABLE_CALL_LOG		= true;
     73 
     74 using namespace gls::FragmentOpUtil;
     75 using namespace gls::InteractionTestUtil;
     76 
     77 void translateStencilState (const StencilState& src, rr::StencilState& dst)
     78 {
     79 	dst.func		= sglr::rr_util::mapGLTestFunc(src.function);
     80 	dst.ref			= src.reference;
     81 	dst.compMask	= src.compareMask;
     82 	dst.sFail		= sglr::rr_util::mapGLStencilOp(src.stencilFailOp);
     83 	dst.dpFail		= sglr::rr_util::mapGLStencilOp(src.depthFailOp);
     84 	dst.dpPass		= sglr::rr_util::mapGLStencilOp(src.depthPassOp);
     85 	dst.writeMask	= src.writeMask;
     86 }
     87 
     88 void translateBlendState (const BlendState& src, rr::BlendState& dst)
     89 {
     90 	dst.equation	= sglr::rr_util::mapGLBlendEquation(src.equation);
     91 	dst.srcFunc		= sglr::rr_util::mapGLBlendFunc(src.srcFunc);
     92 	dst.dstFunc		= sglr::rr_util::mapGLBlendFunc(src.dstFunc);
     93 }
     94 
     95 void translateState (const RenderState& src, rr::FragmentOperationState& dst, const tcu::RenderTarget& renderTarget)
     96 {
     97 	bool hasDepth		= renderTarget.getDepthBits() > 0;
     98 	bool hasStencil		= renderTarget.getStencilBits() > 0;
     99 
    100 	dst.scissorTestEnabled		= src.scissorTestEnabled;
    101 	dst.scissorRectangle		= src.scissorRectangle;
    102 	dst.stencilTestEnabled		= hasStencil && src.stencilTestEnabled;
    103 	dst.depthTestEnabled		= hasDepth && src.depthTestEnabled;
    104 	dst.blendMode				= src.blendEnabled ? rr::BLENDMODE_STANDARD : rr::BLENDMODE_NONE;
    105 	dst.numStencilBits			= renderTarget.getStencilBits();
    106 
    107 	dst.colorMask = src.colorMask;
    108 
    109 	if (dst.depthTestEnabled)
    110 	{
    111 		dst.depthFunc	= sglr::rr_util::mapGLTestFunc(src.depthFunc);
    112 		dst.depthMask	= src.depthWriteMask;
    113 	}
    114 
    115 	if (dst.stencilTestEnabled)
    116 	{
    117 		translateStencilState(src.stencil[rr::FACETYPE_BACK],	dst.stencilStates[rr::FACETYPE_BACK]);
    118 		translateStencilState(src.stencil[rr::FACETYPE_FRONT],	dst.stencilStates[rr::FACETYPE_FRONT]);
    119 	}
    120 
    121 	if (src.blendEnabled)
    122 	{
    123 		translateBlendState(src.blendRGBState, dst.blendRGBState);
    124 		translateBlendState(src.blendAState, dst.blendAState);
    125 		dst.blendColor = tcu::clamp(src.blendColor, Vec4(0.0f), Vec4(1.0f));
    126 	}
    127 }
    128 
    129 static void renderQuad (const glw::Functions& gl, gls::FragmentOpUtil::QuadRenderer& renderer, const gls::FragmentOpUtil::IntegerQuad& quad, int baseX, int baseY)
    130 {
    131 	gls::FragmentOpUtil::Quad translated;
    132 
    133 	std::copy(DE_ARRAY_BEGIN(quad.color), DE_ARRAY_END(quad.color), DE_ARRAY_BEGIN(translated.color));
    134 
    135 	bool	flipX		= quad.posB.x() < quad.posA.x();
    136 	bool	flipY		= quad.posB.y() < quad.posA.y();
    137 	int		viewportX	= de::min(quad.posA.x(), quad.posB.x());
    138 	int		viewportY	= de::min(quad.posA.y(), quad.posB.y());
    139 	int		viewportW	= de::abs(quad.posA.x()-quad.posB.x())+1;
    140 	int		viewportH	= de::abs(quad.posA.y()-quad.posB.y())+1;
    141 
    142 	translated.posA = Vec2(flipX ? 1.0f : -1.0f, flipY ? 1.0f : -1.0f);
    143 	translated.posB = Vec2(flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f);
    144 
    145 	// \todo [2012-12-18 pyry] Pass in DepthRange parameters.
    146 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(quad.depth); ndx++)
    147 		translated.depth[ndx] = quad.depth[ndx]*2.0f - 1.0f;
    148 
    149 	gl.viewport(baseX+viewportX, baseY+viewportY, viewportW, viewportH);
    150 	renderer.render(translated);
    151 }
    152 
    153 static void setGLState (glu::CallLogWrapper& wrapper, const RenderState& state, int viewportX, int viewportY)
    154 {
    155 	if (state.scissorTestEnabled)
    156 	{
    157 		wrapper.glEnable(GL_SCISSOR_TEST);
    158 		wrapper.glScissor(viewportX+state.scissorRectangle.left, viewportY+state.scissorRectangle.bottom,
    159 						  state.scissorRectangle.width, state.scissorRectangle.height);
    160 	}
    161 	else
    162 		wrapper.glDisable(GL_SCISSOR_TEST);
    163 
    164 	if (state.stencilTestEnabled)
    165 	{
    166 		wrapper.glEnable(GL_STENCIL_TEST);
    167 
    168 		for (int face = 0; face < rr::FACETYPE_LAST; face++)
    169 		{
    170 			deUint32				glFace	= face == rr::FACETYPE_BACK ? GL_BACK : GL_FRONT;
    171 			const StencilState&		sParams	= state.stencil[face];
    172 
    173 			wrapper.glStencilFuncSeparate(glFace, sParams.function, sParams.reference, sParams.compareMask);
    174 			wrapper.glStencilOpSeparate(glFace, sParams.stencilFailOp, sParams.depthFailOp, sParams.depthPassOp);
    175 			wrapper.glStencilMaskSeparate(glFace, sParams.writeMask);
    176 		}
    177 	}
    178 	else
    179 		wrapper.glDisable(GL_STENCIL_TEST);
    180 
    181 	if (state.depthTestEnabled)
    182 	{
    183 		wrapper.glEnable(GL_DEPTH_TEST);
    184 		wrapper.glDepthFunc(state.depthFunc);
    185 		wrapper.glDepthMask(state.depthWriteMask ? GL_TRUE : GL_FALSE);
    186 	}
    187 	else
    188 		wrapper.glDisable(GL_DEPTH_TEST);
    189 
    190 	if (state.blendEnabled)
    191 	{
    192 		wrapper.glEnable(GL_BLEND);
    193 		wrapper.glBlendEquationSeparate(state.blendRGBState.equation, state.blendAState.equation);
    194 		wrapper.glBlendFuncSeparate(state.blendRGBState.srcFunc, state.blendRGBState.dstFunc, state.blendAState.srcFunc, state.blendAState.dstFunc);
    195 		wrapper.glBlendColor(state.blendColor.x(), state.blendColor.y(), state.blendColor.z(), state.blendColor.w());
    196 	}
    197 	else
    198 		wrapper.glDisable(GL_BLEND);
    199 
    200 	if (state.ditherEnabled)
    201 		wrapper.glEnable(GL_DITHER);
    202 	else
    203 		wrapper.glDisable(GL_DITHER);
    204 
    205 	wrapper.glColorMask(state.colorMask[0] ? GL_TRUE : GL_FALSE,
    206 						state.colorMask[1] ? GL_TRUE : GL_FALSE,
    207 						state.colorMask[2] ? GL_TRUE : GL_FALSE,
    208 						state.colorMask[3] ? GL_TRUE : GL_FALSE);
    209 }
    210 
    211 class RandomFragmentOpCase : public TestCase
    212 {
    213 public:
    214 						RandomFragmentOpCase		(Context& context, const char* name, const char* desc, deUint32 seed);
    215 						~RandomFragmentOpCase		(void);
    216 
    217 	void				init						(void);
    218 	void				deinit						(void);
    219 	IterateResult		iterate						(void);
    220 
    221 private:
    222 	tcu::UVec4			getCompareThreshold			(void) const;
    223 
    224 	deUint32										m_seed;
    225 
    226 	glu::CallLogWrapper						m_callLogWrapper;
    227 
    228 	gls::FragmentOpUtil::QuadRenderer*				m_renderer;
    229 	tcu::TextureLevel*								m_refColorBuffer;
    230 	tcu::TextureLevel*								m_refDepthBuffer;
    231 	tcu::TextureLevel*								m_refStencilBuffer;
    232 	gls::FragmentOpUtil::ReferenceQuadRenderer*		m_refRenderer;
    233 
    234 	int												m_iterNdx;
    235 };
    236 
    237 RandomFragmentOpCase::RandomFragmentOpCase (Context& context, const char* name, const char* desc, deUint32 seed)
    238 	: TestCase				(context, name, desc)
    239 	, m_seed				(seed)
    240 	, m_callLogWrapper		(context.getRenderContext().getFunctions(), context.getTestContext().getLog())
    241 	, m_renderer			(DE_NULL)
    242 	, m_refColorBuffer		(DE_NULL)
    243 	, m_refDepthBuffer		(DE_NULL)
    244 	, m_refStencilBuffer	(DE_NULL)
    245 	, m_refRenderer			(DE_NULL)
    246 	, m_iterNdx				(0)
    247 {
    248 	m_callLogWrapper.enableLogging(ENABLE_CALL_LOG);
    249 }
    250 
    251 RandomFragmentOpCase::~RandomFragmentOpCase (void)
    252 {
    253 	delete m_renderer;
    254 	delete m_refColorBuffer;
    255 	delete m_refDepthBuffer;
    256 	delete m_refStencilBuffer;
    257 	delete m_refRenderer;
    258 }
    259 
    260 void RandomFragmentOpCase::init (void)
    261 {
    262 	DE_ASSERT(!m_renderer && !m_refColorBuffer && !m_refDepthBuffer && !m_refStencilBuffer && !m_refRenderer);
    263 
    264 	int		width	= de::min<int>(m_context.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
    265 	int		height	= de::min<int>(m_context.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
    266 	bool	useRGB	= m_context.getRenderTarget().getPixelFormat().alphaBits == 0;
    267 
    268 	m_renderer			= new gls::FragmentOpUtil::QuadRenderer(m_context.getRenderContext(), glu::GLSL_VERSION_100_ES);
    269 	m_refColorBuffer	= new tcu::TextureLevel(tcu::TextureFormat(useRGB ? tcu::TextureFormat::RGB : tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), width, height);
    270 	m_refDepthBuffer	= new tcu::TextureLevel(tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT),			width, height);
    271 	m_refStencilBuffer	= new tcu::TextureLevel(tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT32),	width, height);
    272 	m_refRenderer		= new gls::FragmentOpUtil::ReferenceQuadRenderer();
    273 	m_iterNdx			= 0;
    274 
    275 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    276 }
    277 
    278 void RandomFragmentOpCase::deinit (void)
    279 {
    280 	delete m_renderer;
    281 	delete m_refColorBuffer;
    282 	delete m_refDepthBuffer;
    283 	delete m_refStencilBuffer;
    284 	delete m_refRenderer;
    285 
    286 	m_renderer			= DE_NULL;
    287 	m_refColorBuffer	= DE_NULL;
    288 	m_refDepthBuffer	= DE_NULL;
    289 	m_refStencilBuffer	= DE_NULL;
    290 	m_refRenderer		= DE_NULL;
    291 }
    292 
    293 RandomFragmentOpCase::IterateResult RandomFragmentOpCase::iterate (void)
    294 {
    295 	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
    296 	const bool				isMSAA			= m_context.getRenderTarget().getNumSamples() > 1;
    297 	const deUint32			iterSeed		= deUint32Hash(m_seed) ^ deInt32Hash(m_iterNdx) ^ deInt32Hash(m_testCtx.getCommandLine().getBaseSeed());
    298 	de::Random				rnd				(iterSeed);
    299 
    300 	const int				width			= m_refColorBuffer->getWidth();
    301 	const int				height			= m_refColorBuffer->getHeight();
    302 	const int				viewportX		= rnd.getInt(0, m_context.getRenderTarget().getWidth()-width);
    303 	const int				viewportY		= rnd.getInt(0, m_context.getRenderTarget().getHeight()-height);
    304 
    305 	tcu::Surface			renderedImg		(width, height);
    306 	tcu::Surface			referenceImg	(width, height);
    307 
    308 	const Vec4				clearColor		= CLEAR_COLOR;
    309 	const float				clearDepth		= CLEAR_DEPTH;
    310 	const int				clearStencil	= CLEAR_STENCIL;
    311 
    312 	bool					gotError		= false;
    313 
    314 	const tcu::ScopedLogSection	iterSection	(m_testCtx.getLog(), std::string("Iteration") + de::toString(m_iterNdx), std::string("Iteration ") + de::toString(m_iterNdx));
    315 
    316 	// Compute randomized rendering commands.
    317 	vector<RenderCommand> commands;
    318 	computeRandomRenderCommands(rnd, glu::ApiType::es(2,0), NUM_CALLS_PER_ITERATION, width, height, commands);
    319 
    320 	// Reset default fragment state.
    321 	gl.disable(GL_SCISSOR_TEST);
    322 	gl.colorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    323 	gl.depthMask(GL_TRUE);
    324 	gl.stencilMask(~0u);
    325 
    326 	// Render using GL.
    327 	m_callLogWrapper.glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
    328 	m_callLogWrapper.glClearDepthf(clearDepth);
    329 	m_callLogWrapper.glClearStencil(clearStencil);
    330 	m_callLogWrapper.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    331 	m_callLogWrapper.glViewport(viewportX, viewportY, width, height);
    332 
    333 	for (vector<RenderCommand>::const_iterator cmd = commands.begin(); cmd != commands.end(); cmd++)
    334 	{
    335 		setGLState(m_callLogWrapper, cmd->state, viewportX, viewportY);
    336 
    337 		if (ENABLE_CALL_LOG)
    338 			m_testCtx.getLog() << TestLog::Message << "// Quad: " << cmd->quad.posA << " -> " << cmd->quad.posB
    339 												   << ", color: [" << cmd->quad.color[0] << ", " << cmd->quad.color[1] << ", " << cmd->quad.color[2] << ", " << cmd->quad.color[3] << "]"
    340 												   << ", depth: [" << cmd->quad.depth[0] << ", " << cmd->quad.depth[1] << ", " << cmd->quad.depth[2] << ", " << cmd->quad.depth[3] << "]"
    341 								<< TestLog::EndMessage;
    342 
    343 		renderQuad(gl, *m_renderer, cmd->quad, viewportX, viewportY);
    344 	}
    345 
    346 	// Check error.
    347 	if (m_callLogWrapper.glGetError() != GL_NO_ERROR)
    348 		gotError = true;
    349 
    350 	gl.flush();
    351 
    352 	// Render reference while GPU is doing work.
    353 	tcu::clear			(m_refColorBuffer->getAccess(),		clearColor);
    354 	tcu::clearDepth		(m_refDepthBuffer->getAccess(),		clearDepth);
    355 	tcu::clearStencil	(m_refStencilBuffer->getAccess(),	clearStencil);
    356 
    357 	for (vector<RenderCommand>::const_iterator cmd = commands.begin(); cmd != commands.end(); cmd++)
    358 	{
    359 		rr::FragmentOperationState refState;
    360 		translateState(cmd->state, refState, m_context.getRenderTarget());
    361 		m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
    362 							  gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
    363 							  gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()),
    364 							  cmd->quad, refState);
    365 	}
    366 
    367 	// Expand reference color buffer to RGBA8
    368 	copy(referenceImg.getAccess(), m_refColorBuffer->getAccess());
    369 
    370 	// Read rendered image.
    371 	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedImg.getAccess());
    372 
    373 	m_iterNdx += 1;
    374 
    375 	// Compare to reference.
    376 	bool				isLastIter	= m_iterNdx >= NUM_ITERATIONS_PER_CASE;
    377 	const tcu::UVec4	threshold	= getCompareThreshold();
    378 	bool				compareOk;
    379 
    380 	if (isMSAA)
    381 	{
    382 		// in MSAA cases, the sampling points could be anywhere in the pixel and we could
    383 		// even have multiple samples that are combined in resolve. Allow arbitrary sample
    384 		// positions by using bilinearCompare.
    385 		compareOk = tcu::bilinearCompare(m_testCtx.getLog(),
    386 										 "CompareResult",
    387 										 "Image Comparison Result",
    388 										 referenceImg.getAccess(),
    389 										 renderedImg.getAccess(),
    390 										 tcu::RGBA(threshold.x(), threshold.y(), threshold.z(), threshold.w()),
    391 										 tcu::COMPARE_LOG_RESULT);
    392 	}
    393 	else
    394 		compareOk = tcu::intThresholdCompare(m_testCtx.getLog(),
    395 											 "CompareResult",
    396 											 "Image Comparison Result",
    397 											 referenceImg.getAccess(),
    398 											 renderedImg.getAccess(),
    399 											 threshold,
    400 											 tcu::COMPARE_LOG_RESULT);
    401 
    402 	m_testCtx.getLog() << TestLog::Message << (compareOk ? "  Passed." : "  FAILED!") << TestLog::EndMessage;
    403 
    404 	if (!compareOk)
    405 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
    406 	else if (gotError)
    407 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GL error");
    408 
    409 	if (compareOk && !gotError && !isLastIter)
    410 		return CONTINUE;
    411 	else
    412 		return STOP;
    413 }
    414 
    415 tcu::UVec4 RandomFragmentOpCase::getCompareThreshold (void) const
    416 {
    417 	tcu::PixelFormat format = m_context.getRenderTarget().getPixelFormat();
    418 
    419 	if (format == tcu::PixelFormat(8, 8, 8, 8) || format == tcu::PixelFormat(8, 8, 8, 0))
    420 		return format.getColorThreshold().toIVec().asUint() + tcu::UVec4(6); // Default threshold.
    421 	else
    422 		return format.getColorThreshold().toIVec().asUint()
    423 			   * tcu::UVec4(5) + tcu::UVec4(2); // \note Non-scientific ad hoc formula. Need big threshold when few color bits; especially multiple blendings bring extra inaccuracy.
    424 }
    425 
    426 RandomFragmentOpTests::RandomFragmentOpTests (Context& context)
    427 	: TestCaseGroup(context, "random", "Randomized Per-Fragment Operation Tests")
    428 {
    429 }
    430 
    431 RandomFragmentOpTests::~RandomFragmentOpTests (void)
    432 {
    433 }
    434 
    435 void RandomFragmentOpTests::init (void)
    436 {
    437 	for (int ndx = 0; ndx < 100; ndx++)
    438 		addChild(new RandomFragmentOpCase(m_context, de::toString(ndx).c_str(), "", (deUint32)(ndx*NUM_ITERATIONS_PER_CASE)));
    439 }
    440 
    441 } // Functional
    442 } // gles2
    443 } // deqp
    444