Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.1 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 GLSL Shared variable tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es31fShaderSharedVarTests.hpp"
     25 #include "es31fShaderAtomicOpTests.hpp"
     26 #include "gluShaderProgram.hpp"
     27 #include "gluShaderUtil.hpp"
     28 #include "gluRenderContext.hpp"
     29 #include "gluObjectWrapper.hpp"
     30 #include "gluProgramInterfaceQuery.hpp"
     31 #include "tcuVector.hpp"
     32 #include "tcuTestLog.hpp"
     33 #include "tcuVectorUtil.hpp"
     34 #include "tcuFormatUtil.hpp"
     35 #include "deRandom.hpp"
     36 #include "deArrayUtil.hpp"
     37 #include "glwFunctions.hpp"
     38 #include "glwEnums.hpp"
     39 
     40 #include <algorithm>
     41 #include <set>
     42 
     43 namespace deqp
     44 {
     45 namespace gles31
     46 {
     47 namespace Functional
     48 {
     49 
     50 using std::string;
     51 using std::vector;
     52 using tcu::TestLog;
     53 using tcu::UVec3;
     54 using std::set;
     55 using namespace glu;
     56 
     57 enum
     58 {
     59 	MAX_VALUE_ARRAY_LENGTH	= 15	// * 2 * sizeof(mat4) + sizeof(int) = 481 uniform components (limit 512)
     60 };
     61 
     62 template<typename T, int Size>
     63 static inline T product (const tcu::Vector<T, Size>& v)
     64 {
     65 	T res = v[0];
     66 	for (int ndx = 1; ndx < Size; ndx++)
     67 		res *= v[ndx];
     68 	return res;
     69 }
     70 
     71 class SharedBasicVarCase : public TestCase
     72 {
     73 public:
     74 							SharedBasicVarCase		(Context& context, const char* name, DataType basicType, Precision precision, const tcu::UVec3& workGroupSize);
     75 							~SharedBasicVarCase		(void);
     76 
     77 	void					init					(void);
     78 	void					deinit					(void);
     79 	IterateResult			iterate					(void);
     80 
     81 private:
     82 							SharedBasicVarCase		(const SharedBasicVarCase& other);
     83 	SharedBasicVarCase&		operator=				(const SharedBasicVarCase& other);
     84 
     85 	const DataType			m_basicType;
     86 	const Precision			m_precision;
     87 	const tcu::UVec3		m_workGroupSize;
     88 
     89 	ShaderProgram*			m_program;
     90 };
     91 
     92 static std::string getBasicCaseDescription (DataType basicType, Precision precision, const tcu::UVec3& workGroupSize)
     93 {
     94 	std::ostringstream str;
     95 	if (precision != PRECISION_LAST)
     96 		str << getPrecisionName(precision) << " ";
     97 	str << getDataTypeName(basicType) << ", work group size = " << workGroupSize;
     98 	return str.str();
     99 }
    100 
    101 SharedBasicVarCase::SharedBasicVarCase (Context& context, const char* name, DataType basicType, Precision precision, const tcu::UVec3& workGroupSize)
    102 	: TestCase			(context, name, getBasicCaseDescription(basicType, precision, workGroupSize).c_str())
    103 	, m_basicType		(basicType)
    104 	, m_precision		(precision)
    105 	, m_workGroupSize	(workGroupSize)
    106 	, m_program			(DE_NULL)
    107 {
    108 }
    109 
    110 SharedBasicVarCase::~SharedBasicVarCase (void)
    111 {
    112 	SharedBasicVarCase::deinit();
    113 }
    114 
    115 void SharedBasicVarCase::init (void)
    116 {
    117 	const int			valArrayLength	= de::min<int>(MAX_VALUE_ARRAY_LENGTH, product(m_workGroupSize));
    118 	const char*			precName		= m_precision != glu::PRECISION_LAST ? getPrecisionName(m_precision) : "";
    119 	const char*			typeName		= getDataTypeName(m_basicType);
    120 	std::ostringstream	src;
    121 
    122 	src << "#version 310 es\n"
    123 		<< "layout (local_size_x = " << m_workGroupSize[0]
    124 		<< ", local_size_y = " << m_workGroupSize[1]
    125 		<< ", local_size_z = " << m_workGroupSize[2]
    126 		<< ") in;\n"
    127 		<< "const uint LOCAL_SIZE = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
    128 		<< "shared " << precName << " " << typeName << " s_var;\n"
    129 		<< "uniform " << precName << " " << typeName << " u_val[" << valArrayLength << "];\n"
    130 		<< "uniform " << precName << " " << typeName << " u_ref[" << valArrayLength << "];\n"
    131 		<< "uniform uint u_numIters;\n"
    132 		<< "layout(binding = 0) buffer Result\n"
    133 		<< "{\n"
    134 		<< "	bool isOk[LOCAL_SIZE];\n"
    135 		<< "};\n"
    136 		<< "\n"
    137 		<< "void main (void)\n"
    138 		<< "{\n"
    139 		<< "	bool allOk = true;\n"
    140 		<< "	for (uint ndx = 0u; ndx < u_numIters; ndx++)\n"
    141 		<< "	{\n"
    142 		<< "		if (ndx == gl_LocalInvocationIndex)\n"
    143 		<< "			s_var = u_val[ndx%uint(u_val.length())];\n"
    144 		<< "\n"
    145 		<< "		barrier();\n"
    146 		<< "\n"
    147 		<< "		if (s_var != u_ref[ndx%uint(u_ref.length())])\n"
    148 		<< "			allOk = false;\n"
    149 		<< "\n"
    150 		<< "		barrier();\n"
    151 		<< "	}\n"
    152 		<< "\n"
    153 		<< "	isOk[gl_LocalInvocationIndex] = allOk;\n"
    154 		<< "}\n";
    155 
    156 	DE_ASSERT(!m_program);
    157 	m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
    158 
    159 	m_testCtx.getLog() << *m_program;
    160 
    161 	if (!m_program->isOk())
    162 	{
    163 		delete m_program;
    164 		m_program = DE_NULL;
    165 		throw tcu::TestError("Compile failed");
    166 	}
    167 }
    168 
    169 void SharedBasicVarCase::deinit (void)
    170 {
    171 	delete m_program;
    172 	m_program = DE_NULL;
    173 }
    174 
    175 SharedBasicVarCase::IterateResult SharedBasicVarCase::iterate (void)
    176 {
    177 	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
    178 	const deUint32				program			= m_program->getProgram();
    179 	Buffer						outputBuffer	(m_context.getRenderContext());
    180 	const deUint32				outBlockNdx		= gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "Result");
    181 	const InterfaceBlockInfo	outBlockInfo	= getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, outBlockNdx);
    182 
    183 	gl.useProgram(program);
    184 
    185 	// Setup input values.
    186 	{
    187 		const int		numValues		= (int)product(m_workGroupSize);
    188 		const int		valLoc			= gl.getUniformLocation(program, "u_val[0]");
    189 		const int		refLoc			= gl.getUniformLocation(program, "u_ref[0]");
    190 		const int		iterCountLoc	= gl.getUniformLocation(program, "u_numIters");
    191 		const int		scalarSize		= getDataTypeScalarSize(m_basicType);
    192 
    193 		if (isDataTypeFloatOrVec(m_basicType))
    194 		{
    195 			const int		maxInt			= m_precision == glu::PRECISION_LOWP ? 2 : 1024;
    196 			const int		minInt			= -de::min(numValues/2, maxInt);
    197 			vector<float>	values			(numValues*scalarSize);
    198 
    199 			for (int ndx = 0; ndx < (int)values.size(); ndx++)
    200 				values[ndx] = float(minInt + (ndx % (maxInt-minInt+1)));
    201 
    202 			for (int uNdx = 0; uNdx < 2; uNdx++)
    203 			{
    204 				const int location = uNdx == 1 ? refLoc : valLoc;
    205 
    206 				if (scalarSize == 1)		gl.uniform1fv(location, numValues, &values[0]);
    207 				else if (scalarSize == 2)	gl.uniform2fv(location, numValues, &values[0]);
    208 				else if (scalarSize == 3)	gl.uniform3fv(location, numValues, &values[0]);
    209 				else if (scalarSize == 4)	gl.uniform4fv(location, numValues, &values[0]);
    210 			}
    211 		}
    212 		else if (isDataTypeIntOrIVec(m_basicType))
    213 		{
    214 			const int		maxInt			= m_precision == glu::PRECISION_LOWP ? 64 : 1024;
    215 			const int		minInt			= -de::min(numValues/2, maxInt);
    216 			vector<int>		values			(numValues*scalarSize);
    217 
    218 			for (int ndx = 0; ndx < (int)values.size(); ndx++)
    219 				values[ndx] = minInt + (ndx % (maxInt-minInt+1));
    220 
    221 			for (int uNdx = 0; uNdx < 2; uNdx++)
    222 			{
    223 				const int location = uNdx == 1 ? refLoc : valLoc;
    224 
    225 				if (scalarSize == 1)		gl.uniform1iv(location, numValues, &values[0]);
    226 				else if (scalarSize == 2)	gl.uniform2iv(location, numValues, &values[0]);
    227 				else if (scalarSize == 3)	gl.uniform3iv(location, numValues, &values[0]);
    228 				else if (scalarSize == 4)	gl.uniform4iv(location, numValues, &values[0]);
    229 			}
    230 		}
    231 		else if (isDataTypeUintOrUVec(m_basicType))
    232 		{
    233 			const deUint32		maxInt		= m_precision == glu::PRECISION_LOWP ? 128 : 1024;
    234 			vector<deUint32>	values		(numValues*scalarSize);
    235 
    236 			for (int ndx = 0; ndx < (int)values.size(); ndx++)
    237 				values[ndx] = ndx % (maxInt+1);
    238 
    239 			for (int uNdx = 0; uNdx < 2; uNdx++)
    240 			{
    241 				const int location = uNdx == 1 ? refLoc : valLoc;
    242 
    243 				if (scalarSize == 1)		gl.uniform1uiv(location, numValues, &values[0]);
    244 				else if (scalarSize == 2)	gl.uniform2uiv(location, numValues, &values[0]);
    245 				else if (scalarSize == 3)	gl.uniform3uiv(location, numValues, &values[0]);
    246 				else if (scalarSize == 4)	gl.uniform4uiv(location, numValues, &values[0]);
    247 			}
    248 		}
    249 		else if (isDataTypeBoolOrBVec(m_basicType))
    250 		{
    251 			de::Random		rnd				(0x324f);
    252 			vector<int>		values			(numValues*scalarSize);
    253 
    254 			for (int ndx = 0; ndx < (int)values.size(); ndx++)
    255 				values[ndx] = rnd.getBool() ? 1 : 0;
    256 
    257 			for (int uNdx = 0; uNdx < 2; uNdx++)
    258 			{
    259 				const int location = uNdx == 1 ? refLoc : valLoc;
    260 
    261 				if (scalarSize == 1)		gl.uniform1iv(location, numValues, &values[0]);
    262 				else if (scalarSize == 2)	gl.uniform2iv(location, numValues, &values[0]);
    263 				else if (scalarSize == 3)	gl.uniform3iv(location, numValues, &values[0]);
    264 				else if (scalarSize == 4)	gl.uniform4iv(location, numValues, &values[0]);
    265 			}
    266 		}
    267 		else if (isDataTypeMatrix(m_basicType))
    268 		{
    269 			const int		maxInt			= m_precision == glu::PRECISION_LOWP ? 2 : 1024;
    270 			const int		minInt			= -de::min(numValues/2, maxInt);
    271 			vector<float>	values			(numValues*scalarSize);
    272 
    273 			for (int ndx = 0; ndx < (int)values.size(); ndx++)
    274 				values[ndx] = float(minInt + (ndx % (maxInt-minInt+1)));
    275 
    276 			for (int uNdx = 0; uNdx < 2; uNdx++)
    277 			{
    278 				const int location = uNdx == 1 ? refLoc : valLoc;
    279 
    280 				switch (m_basicType)
    281 				{
    282 					case TYPE_FLOAT_MAT2:	gl.uniformMatrix2fv  (location, numValues, DE_FALSE, &values[0]);	break;
    283 					case TYPE_FLOAT_MAT2X3:	gl.uniformMatrix2x3fv(location, numValues, DE_FALSE, &values[0]);	break;
    284 					case TYPE_FLOAT_MAT2X4:	gl.uniformMatrix2x4fv(location, numValues, DE_FALSE, &values[0]);	break;
    285 					case TYPE_FLOAT_MAT3X2:	gl.uniformMatrix3x2fv(location, numValues, DE_FALSE, &values[0]);	break;
    286 					case TYPE_FLOAT_MAT3:	gl.uniformMatrix3fv  (location, numValues, DE_FALSE, &values[0]);	break;
    287 					case TYPE_FLOAT_MAT3X4:	gl.uniformMatrix3x4fv(location, numValues, DE_FALSE, &values[0]);	break;
    288 					case TYPE_FLOAT_MAT4X2:	gl.uniformMatrix4x2fv(location, numValues, DE_FALSE, &values[0]);	break;
    289 					case TYPE_FLOAT_MAT4X3:	gl.uniformMatrix4x3fv(location, numValues, DE_FALSE, &values[0]);	break;
    290 					case TYPE_FLOAT_MAT4:	gl.uniformMatrix4fv  (location, numValues, DE_FALSE, &values[0]);	break;
    291 					default:
    292 						DE_ASSERT(false);
    293 				}
    294 			}
    295 		}
    296 
    297 		gl.uniform1ui(iterCountLoc, product(m_workGroupSize));
    298 		GLU_EXPECT_NO_ERROR(gl.getError(), "Input value setup failed");
    299 	}
    300 
    301 	// Setup output buffer.
    302 	{
    303 		vector<deUint8> emptyData(outBlockInfo.dataSize);
    304 		std::fill(emptyData.begin(), emptyData.end(), 0);
    305 
    306 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
    307 		gl.bufferData(GL_SHADER_STORAGE_BUFFER, outBlockInfo.dataSize, &emptyData[0], GL_STATIC_READ);
    308 		gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
    309 		GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
    310 	}
    311 
    312 	gl.dispatchCompute(1, 1, 1);
    313 
    314 	// Read back and compare
    315 	{
    316 		const deUint32				numValues	= product(m_workGroupSize);
    317 		const InterfaceVariableInfo	outVarInfo	= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outBlockInfo.activeVariables[0]);
    318 		const void*					resPtr		= gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, outBlockInfo.dataSize, GL_MAP_READ_BIT);
    319 		const int					maxErrMsg	= 10;
    320 		int							numFailed	= 0;
    321 
    322 		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
    323 		TCU_CHECK(resPtr);
    324 
    325 		for (deUint32 ndx = 0; ndx < numValues; ndx++)
    326 		{
    327 			const int resVal = *((const int*)((const deUint8*)resPtr + outVarInfo.offset + outVarInfo.arrayStride*ndx));
    328 
    329 			if (resVal == 0)
    330 			{
    331 				if (numFailed < maxErrMsg)
    332 					m_testCtx.getLog() << TestLog::Message << "ERROR: isOk[" << ndx << "] = " << resVal << " != true" << TestLog::EndMessage;
    333 				else if (numFailed == maxErrMsg)
    334 					m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
    335 
    336 				numFailed += 1;
    337 			}
    338 		}
    339 
    340 		gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
    341 		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
    342 
    343 		m_testCtx.getLog() << TestLog::Message << (numValues-numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
    344 
    345 		m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    346 								numFailed == 0 ? "Pass"					: "Comparison failed");
    347 	}
    348 
    349 	return STOP;
    350 }
    351 
    352 ShaderSharedVarTests::ShaderSharedVarTests (Context& context)
    353 	: TestCaseGroup(context, "shared_var", "Shared Variable Tests")
    354 {
    355 }
    356 
    357 ShaderSharedVarTests::~ShaderSharedVarTests (void)
    358 {
    359 }
    360 
    361 void ShaderSharedVarTests::init (void)
    362 {
    363 	// .basic_type
    364 	{
    365 		tcu::TestCaseGroup *const basicTypeGroup = new tcu::TestCaseGroup(m_testCtx, "basic_type", "Basic Types");
    366 		addChild(basicTypeGroup);
    367 
    368 		for (int basicType = TYPE_FLOAT; basicType <= TYPE_BOOL_VEC4; basicType++)
    369 		{
    370 			if (glu::getDataTypeScalarType(DataType(basicType)) == glu::TYPE_DOUBLE)
    371 				continue;
    372 
    373 			if (glu::isDataTypeBoolOrBVec(DataType(basicType)))
    374 			{
    375 				const tcu::UVec3	workGroupSize	(2,1,3);
    376 				basicTypeGroup->addChild(new SharedBasicVarCase(m_context, getDataTypeName(DataType(basicType)), DataType(basicType), PRECISION_LAST, workGroupSize));
    377 			}
    378 			else
    379 			{
    380 				for (int precision = 0; precision < PRECISION_LAST; precision++)
    381 				{
    382 					const tcu::UVec3	workGroupSize	(2,1,3);
    383 					const string		name			= string(getDataTypeName(DataType(basicType))) + "_" + getPrecisionName(Precision(precision));
    384 
    385 					basicTypeGroup->addChild(new SharedBasicVarCase(m_context, name.c_str(), DataType(basicType), Precision(precision), workGroupSize));
    386 				}
    387 			}
    388 		}
    389 	}
    390 
    391 	// .work_group_size
    392 	{
    393 		tcu::TestCaseGroup *const workGroupSizeGroup = new tcu::TestCaseGroup(m_testCtx, "work_group_size", "Shared Variables with Various Work Group Sizes");
    394 		addChild(workGroupSizeGroup);
    395 
    396 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_1_1_1",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(1,1,1)));
    397 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_64_1_1",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(64,1,1)));
    398 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_1_64_1",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(1,64,1)));
    399 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_1_1_64",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(1,1,64)));
    400 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_128_1_1",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(128,1,1)));
    401 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_1_128_1",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(1,128,1)));
    402 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "float_13_2_4",		TYPE_FLOAT,			PRECISION_HIGHP,	tcu::UVec3(13,2,4)));
    403 
    404 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_1_1_1",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(1,1,1)));
    405 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_64_1_1",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(64,1,1)));
    406 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_1_64_1",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(1,64,1)));
    407 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_1_1_64",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(1,1,64)));
    408 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_128_1_1",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(128,1,1)));
    409 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_1_128_1",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(1,128,1)));
    410 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "vec4_13_2_4",		TYPE_FLOAT_VEC4,	PRECISION_HIGHP,	tcu::UVec3(13,2,4)));
    411 
    412 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_1_1_1",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(1,1,1)));
    413 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_64_1_1",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(64,1,1)));
    414 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_1_64_1",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(1,64,1)));
    415 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_1_1_64",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(1,1,64)));
    416 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_128_1_1",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(128,1,1)));
    417 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_1_128_1",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(1,128,1)));
    418 		workGroupSizeGroup->addChild(new SharedBasicVarCase(m_context, "mat4_13_2_4",		TYPE_FLOAT_MAT4,	PRECISION_HIGHP,	tcu::UVec3(13,2,4)));
    419 	}
    420 
    421 	// .atomic
    422 	addChild(new ShaderAtomicOpTests(m_context, "atomic", ATOMIC_OPERAND_SHARED_VARIABLE));
    423 }
    424 
    425 } // Functional
    426 } // gles31
    427 } // deqp
    428