1/*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2016 Google Inc.
6 * Copyright (c) 2016 The Khronos Group Inc.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */ /*!
21 * \file
22 * \brief GLSL vector constructor tests.
23 */ /*-------------------------------------------------------------------*/
24#include "glcGLSLVectorConstructorTests.hpp"
25
26#include "gluDefs.hpp"
27#include "gluTextureUtil.hpp"
28#include "gluDrawUtil.hpp"
29#include "gluShaderProgram.hpp"
30
31#include "glwDefs.hpp"
32#include "glwFunctions.hpp"
33#include "glwEnums.hpp"
34
35#include "tcuTestLog.hpp"
36#include "tcuRenderTarget.hpp"
37#include "tcuStringTemplate.hpp"
38
39#include <functional>
40#include <map>
41#include <vector>
42#include <sstream>
43#include <string>
44#include <tuple>
45
46namespace deqp
47{
48
49namespace
50{
51using std::string;
52
53using std::map;
54using std::vector;
55
56using std::function;
57using std::bind;
58using namespace std::placeholders;
59
60using std::ostringstream;
61
62enum struct TestType
63{
64	VERTEX_SHADER_ERROR = 0,
65	FRAGMENT_SHADER_ERROR,
66	VERTEX_SHADER,
67	FRAGMENT_SHADER
68};
69
70struct TestDefinition
71{
72	vector<string>			outputTypes;
73	vector<vector<string>>	inputTypeLists;
74	string					extraFields;
75};
76
77const TestDefinition					tests[]					=
78{
79	{
80		{ "vec2", "vec3", "vec4" },								// vector<string>			outputTypes
81		{														// vector<vector<string>>	inputTypeLists
82			{ "mat2" },
83			{ "mat2x3" },
84			{ "mat2x4" },
85			{ "mat3" },
86			{ "mat3x2" },
87			{ "mat3x4" },
88			{ "mat4" },
89			{ "mat4x2" },
90			{ "mat4x3" },
91			{ "float", "mat2" },
92			{ "float", "mat2x3" },
93			{ "float", "mat2x4" },
94			{ "float", "mat3" },
95			{ "float", "mat3x2" },
96			{ "float", "mat3x4" },
97			{ "float", "mat4" },
98			{ "float", "mat4x2" },
99			{ "float", "mat4x3" },
100		},
101		"const float errorBound = 1.0E-5;\n"					// deUint32					extraFields;
102	},
103	{
104		{ "ivec2", "ivec3", "ivec4" },							// vector<string>			outputTypes
105		{														// vector<vector<string>>	inputTypeLists
106			{ "mat2" },
107			{ "mat2x3" },
108			{ "mat2x4" },
109			{ "mat3" },
110			{ "mat3x2" },
111			{ "mat3x4" },
112			{ "mat4" },
113			{ "mat4x2" },
114			{ "mat4x3" },
115			{ "int", "mat2" },
116			{ "int", "mat2x3" },
117			{ "int", "mat2x4" },
118			{ "int", "mat3" },
119			{ "int", "mat3x2" },
120			{ "int", "mat3x4" },
121			{ "int", "mat4" },
122			{ "int", "mat4x2" },
123			{ "int", "mat4x3" },
124		},
125		""														// deUint32					extraFields;
126	},
127	{
128		{ "bvec2", "bvec3", "bvec4" },							// vector<string>			outputTypes
129		{														// vector<vector<string>>	inputTypeLists
130			{ "mat2" },
131			{ "mat2x3" },
132			{ "mat2x4" },
133			{ "mat3" },
134			{ "mat3x2" },
135			{ "mat3x4" },
136			{ "mat4" },
137			{ "mat4x2" },
138			{ "mat4x3" },
139			{ "bool", "mat2" },
140			{ "bool", "mat2x3" },
141			{ "bool", "mat2x4" },
142			{ "bool", "mat3" },
143			{ "bool", "mat3x2" },
144			{ "bool", "mat3x4" },
145			{ "bool", "mat4" },
146			{ "bool", "mat4x2" },
147			{ "bool", "mat4x3" },
148		},
149		""														// deUint32					extraFields;
150	},
151};
152
153struct TestParams
154{
155	string			name;
156	string			description;
157	TestType		testType;
158	string			outputType;
159	vector<string>	inputTypes;
160	string			extraFields;
161};
162
163vector<TestParams> generateTestParams()
164{
165	vector<TestParams> result;
166	result.reserve(64);
167	for(const auto& test : tests)
168	{
169		for(const auto& outputType : test.outputTypes)
170		{
171			for(const auto& inputTypes : test.inputTypeLists)
172			{
173				ostringstream testNameVs, testNameFs;
174				ostringstream testDescriptionVs, testDescriptionFs;
175				testNameVs << outputType << "_from";
176				testNameFs << outputType << "_from";
177				testDescriptionVs << outputType << "(";
178				testDescriptionFs << outputType << "(";
179				for(vector<string>::size_type i = 0; i < inputTypes.size(); ++i)
180				{
181					const auto& inputType = inputTypes[i];
182					testNameVs << "_" << inputType;
183					testNameFs << "_" << inputType;
184					if (i > 0) {
185						testDescriptionVs << ",";
186						testDescriptionFs << ",";
187					}
188					testDescriptionVs << inputType;
189				}
190				ostringstream testNameInvalidVs, testNameInvalidFs;
191				testNameInvalidVs << testNameVs.str() << "_" << inputTypes[0] << "_invalid_vs";
192				testNameInvalidFs << testNameFs.str() << "_" << inputTypes[0] << "_invalid_fs";
193
194				testNameVs << "_vs";
195				testNameFs << "_fs";
196				testDescriptionVs << ") vertex shader";
197				testDescriptionFs << ") fragment shader";
198				result.push_back({ testNameVs.str(), testDescriptionVs.str(), TestType::VERTEX_SHADER, outputType, inputTypes, test.extraFields });
199				result.push_back({ testNameFs.str(), testDescriptionFs.str(), TestType::FRAGMENT_SHADER, outputType, inputTypes, test.extraFields });
200
201				vector<string> failInputTypes;
202				failInputTypes.insert(failInputTypes.end(), inputTypes.begin(), inputTypes.end());
203				failInputTypes.push_back(inputTypes[0]);
204				testDescriptionVs << " invalid";
205				testDescriptionFs << " invalid";
206				result.push_back({ testNameInvalidVs.str(), testDescriptionVs.str(), TestType::VERTEX_SHADER_ERROR, outputType, failInputTypes, test.extraFields });
207				result.push_back({ testNameInvalidFs.str(), testDescriptionFs.str(), TestType::FRAGMENT_SHADER_ERROR, outputType, failInputTypes, test.extraFields });
208
209			}
210		}
211	}
212	return result;
213}
214
215const string									defaultVertexShader		=
216	"${GLSL_VERSION}\n"
217	"in vec4 vPosition;\n"
218	"void main()\n"
219	"{\n"
220	"    gl_Position = vPosition;\n"
221	"}\n";
222
223const string									defaultFragmentShader	=
224	"${GLSL_VERSION}\n"
225	"precision mediump float;\n"
226	"in vec4 vColor;\n"
227	"out vec4 my_FragColor;\n"
228	"void main() {\n"
229	"    my_FragColor = vColor;\n"
230	"}\n";
231
232const string									vertexShaderTemplate	=
233	"${GLSL_VERSION}\n"
234	"in vec4 vPosition;\n"
235	"precision mediump int;\n"
236	"precision mediump float;\n"
237	"const vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
238	"const vec4 red	  = vec4(1.0, 0.0, 0.0, 1.0);\n"
239	"${TEST_CONSTANTS}"
240	"out vec4 vColor;\n"
241	"void main() {\n"
242	"    ${TEST_CODE}\n"
243	"    if ${TEST_CONDITION}\n"
244	"        vColor = green;\n"
245	"    else\n"
246	"        vColor = red;\n"
247	"    gl_Position = vPosition;\n"
248	"}\n";
249
250const string									fragmentShaderTemplate	=
251	"${GLSL_VERSION}\n"
252	"precision mediump int;\n"
253	"precision mediump float;\n"
254	"const vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
255	"const vec4 red	  = vec4(1.0, 0.0, 0.0, 1.0);\n"
256	"${TEST_CONSTANTS}"
257	"out vec4 my_FragColor;\n"
258	"void main() {\n"
259	"    ${TEST_CODE}\n"
260	"    if ${TEST_CONDITION}\n"
261	"        my_FragColor = green;\n"
262	"    else\n"
263	"        my_FragColor = red;\n"
264	"}\n";
265
266const map<string, string>						testConditions			=
267{
268	{ "vec2"	, "(abs(v[0] - 0.0) <= errorBound && abs(v[1] - 1.0) <= errorBound)" },
269	{ "vec3"	, "(abs(v[0] - 0.0) <= errorBound && abs(v[1] - 1.0) <= errorBound && abs(v[2] - 2.0) <= errorBound)" },
270	{ "vec4"	, "(abs(v[0] - 0.0) <= errorBound && abs(v[1] - 1.0) <= errorBound && abs(v[2] - 2.0) <= errorBound && abs(v[3] - 3.0) <= errorBound)" },
271	{ "ivec2"	, "(v[0] == 0 && v[1] == 1)" },
272	{ "ivec3"	, "(v[0] == 0 && v[1] == 1 && v[2] == 2)" },
273	{ "ivec4"	, "(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)" },
274	{ "bvec2"	, "(v[0] == false && v[1] == true)" },
275	{ "bvec3"	, "(v[0] == false && v[1] == true && v[2] == true)" },
276	{ "bvec4"	, "(v[0] == false && v[1] == true && v[2] == true && v[3] == true)" }
277};
278
279typedef function<void (ostringstream&, size_t)> GeneratorFn;
280
281struct DataTypeInfo
282{
283	size_t			numElements;
284	GeneratorFn		valueFn;
285	GeneratorFn		beforeValueFn;
286	GeneratorFn		afterValueFn;
287};
288
289void generateValueFloat(ostringstream& out, const size_t index)
290{
291	out << index << ".0";
292}
293
294void generateValueInt(ostringstream& out, const size_t index)
295{
296	out << index;
297}
298
299void generateValueBool(ostringstream& out, const size_t index)
300{
301	out << ((index != 0) ? "true" : "false");
302}
303
304void generateCtorOpen(const char* className, ostringstream& out, const size_t)
305{
306	out << className << "(";
307}
308
309void generateCtorClose(ostringstream &out, const size_t)
310{
311	out << ")";
312}
313
314const map<string, DataTypeInfo>					dataTypeInfos			=
315{
316	//				numElements	, valueFn			, beforeValueFn								, afterValueFn
317	{ "float"	, { 1			, generateValueFloat, DE_NULL									, DE_NULL			} },
318	{ "vec2"	, { 2			, generateValueFloat, bind(generateCtorOpen, "vec2", _1, _2)	, generateCtorClose	} },
319	{ "vec3"	, { 3			, generateValueFloat, bind(generateCtorOpen, "vec3", _1, _2)	, generateCtorClose	} },
320	{ "vec4"	, { 4			, generateValueFloat, bind(generateCtorOpen, "vec4", _1, _2)	, generateCtorClose	} },
321	{ "int"		, { 1			, generateValueInt	, DE_NULL									, DE_NULL			} },
322	{ "ivec2"	, { 2			, generateValueInt	, bind(generateCtorOpen, "ivec2", _1, _2)	, generateCtorClose	} },
323	{ "ivec3"	, { 3			, generateValueInt	, bind(generateCtorOpen, "ivec3", _1, _2)	, generateCtorClose	} },
324	{ "ivec4"	, { 4			, generateValueInt	, bind(generateCtorOpen, "ivec4", _1, _2)	, generateCtorClose	} },
325	{ "bool"	, { 1			, generateValueBool	, DE_NULL									, DE_NULL			} },
326	{ "bvec2"	, { 2			, generateValueBool	, bind(generateCtorOpen, "bvec2", _1, _2)	, generateCtorClose	} },
327	{ "bvec3"	, { 3			, generateValueBool	, bind(generateCtorOpen, "bvec3", _1, _2)	, generateCtorClose	} },
328	{ "bvec4"	, { 4			, generateValueBool	, bind(generateCtorOpen, "bvec4", _1, _2)	, generateCtorClose	} },
329	{ "mat2"	, { 4			, generateValueFloat, bind(generateCtorOpen, "mat2", _1, _2)	, generateCtorClose	} },
330	{ "mat2x3"	, { 6			, generateValueFloat, bind(generateCtorOpen, "mat2x3", _1, _2)	, generateCtorClose	} },
331	{ "mat2x4"	, { 8			, generateValueFloat, bind(generateCtorOpen, "mat2x4", _1, _2)	, generateCtorClose	} },
332	{ "mat3"	, { 9			, generateValueFloat, bind(generateCtorOpen, "mat3", _1, _2)	, generateCtorClose	} },
333	{ "mat3x2"	, { 6			, generateValueFloat, bind(generateCtorOpen, "mat3x2", _1, _2)	, generateCtorClose	} },
334	{ "mat3x4"	, { 12			, generateValueFloat, bind(generateCtorOpen, "mat3x4", _1, _2)	, generateCtorClose	} },
335	{ "mat4"	, { 16			, generateValueFloat, bind(generateCtorOpen, "mat4", _1, _2)	, generateCtorClose	} },
336	{ "mat4x2"	, { 8			, generateValueFloat, bind(generateCtorOpen, "mat4x2", _1, _2)	, generateCtorClose	} },
337	{ "mat4x3"	, { 12			, generateValueFloat, bind(generateCtorOpen, "mat4x3", _1, _2)	, generateCtorClose	} },
338};
339
340string generateTestCode(const string& outputType, const vector<string>& inputTypes)
341{
342	ostringstream output;
343	const auto outputTypeInfo = dataTypeInfos.find(outputType);
344	DE_ASSERT(outputTypeInfo != dataTypeInfos.end());
345
346	output << outputType << " v = ";
347	if (outputTypeInfo->second.beforeValueFn != DE_NULL)
348		outputTypeInfo->second.beforeValueFn(output, -1);
349	int outputElementsRemaining = outputTypeInfo->second.numElements;
350	int outputElementIndex = 0;
351	for(size_t i = 0; i < inputTypes.size() && outputElementsRemaining > 0; ++i)
352	{
353		const auto& inputType = inputTypes[i];
354		const auto inputTypeInfo = dataTypeInfos.find(inputType);
355		DE_ASSERT(inputTypeInfo != dataTypeInfos.end());
356
357		if (outputElementIndex > 0)
358			output << ", ";
359		if (inputTypeInfo->second.beforeValueFn != DE_NULL)
360			inputTypeInfo->second.beforeValueFn(output, i);
361		for(size_t j = 0; j < inputTypeInfo->second.numElements; ++j)
362		{
363			if (j > 0)
364				output << ", ";
365
366			inputTypeInfo->second.valueFn(output, outputElementIndex++);
367			--outputElementsRemaining;
368		}
369		if (inputTypeInfo->second.afterValueFn != DE_NULL)
370			inputTypeInfo->second.afterValueFn(output, i);
371	}
372	if (outputTypeInfo->second.afterValueFn != DE_NULL)
373		outputTypeInfo->second.afterValueFn(output, -1);
374	output << ";";
375	return output.str();
376}
377
378string replacePlaceholders(const string& shaderTemplate, const TestParams& params, const glu::GLSLVersion glslVersion)
379{
380	const auto condition = testConditions.find(params.outputType);
381	return tcu::StringTemplate(shaderTemplate).specialize(
382	{
383		{ "GLSL_VERSION"	, glu::getGLSLVersionDeclaration(glslVersion) },
384		{ "TEST_CONSTANTS"	, params.extraFields },
385		{ "TEST_CODE"		, generateTestCode(params.outputType, params.inputTypes) },
386		{ "TEST_CONDITION"	, (condition != testConditions.end()) ? condition->second : "" }
387	});
388}
389
390const vector<float>								positions				=
391{
392	-1.0f, -1.0f,
393	 1.0f, -1.0f,
394	-1.0f,	1.0f,
395	 1.0f,	1.0f
396};
397
398const vector<deUint32>							indices					= { 0, 1, 2, 3 };
399
400const int										RENDERTARGET_WIDTH		= 16;
401const int										RENDERTARGET_HEIGHT		= 16;
402
403class GLSLVectorConstructorTestCase : public deqp::TestCase
404{
405public:
406	GLSLVectorConstructorTestCase(deqp::Context& context, glu::GLSLVersion glslVersion, const TestParams& params);
407
408	void init(void);
409	void deinit(void);
410	IterateResult iterate();
411
412private:
413	void setupRenderTarget();
414	void releaseRenderTarget();
415
416	const glu::GLSLVersion		m_glslVersion;
417	const TestParams			m_params;
418	glw::GLuint					m_fboId;
419	glw::GLuint					m_rboId;
420
421	string						m_vertexShader;
422	string						m_fragmentShader;
423};
424
425GLSLVectorConstructorTestCase::GLSLVectorConstructorTestCase(deqp::Context& context, glu::GLSLVersion glslVersion, const TestParams& params)
426	: TestCase(context, params.name.c_str(), params.description.c_str())
427	, m_glslVersion(glslVersion)
428	, m_params(params)
429	, m_fboId(0)
430	, m_rboId(0)
431{
432	switch(m_params.testType)
433	{
434	case TestType::VERTEX_SHADER_ERROR:
435	case TestType::VERTEX_SHADER:
436		m_vertexShader = replacePlaceholders(vertexShaderTemplate, m_params, m_glslVersion);
437		m_fragmentShader = replacePlaceholders(defaultFragmentShader, m_params, m_glslVersion);
438		break;
439	case TestType::FRAGMENT_SHADER_ERROR:
440	case TestType::FRAGMENT_SHADER:
441		m_vertexShader = replacePlaceholders(defaultVertexShader, m_params, m_glslVersion);
442		m_fragmentShader = replacePlaceholders(fragmentShaderTemplate, m_params, m_glslVersion);
443		break;
444	}
445}
446
447void GLSLVectorConstructorTestCase::init(void)
448{
449	deqp::TestCase::init();
450}
451
452void GLSLVectorConstructorTestCase::deinit(void)
453{
454	deqp::TestCase::deinit();
455}
456
457GLSLVectorConstructorTestCase::IterateResult GLSLVectorConstructorTestCase::iterate()
458{
459	const auto&								renderContext	= m_context.getRenderContext();
460	const auto&								gl				= renderContext.getFunctions();
461	const auto								textureFormat	= tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNORM_INT8);
462	const auto								transferFormat	= glu::getTransferFormat(textureFormat);
463
464	setupRenderTarget();
465
466	glu::ShaderProgram program(renderContext, glu::makeVtxFragSources(m_vertexShader, m_fragmentShader));
467	if (!program.isOk())
468	{
469		switch(m_params.testType)
470		{
471		case TestType::VERTEX_SHADER_ERROR:
472		case TestType::FRAGMENT_SHADER_ERROR:
473			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
474			return STOP;
475		default:
476			TCU_FAIL("Shader compilation failed:\nVertex shader:\n" + m_vertexShader + "\nFragment shader:\n" + m_fragmentShader);
477		}
478	}
479
480	const vector<glu::VertexArrayBinding>	vertexArrays	=
481	{
482		glu::va::Float("vPosition", 2, positions.size() / 2, 0, positions.data()),
483	};
484
485	gl.useProgram(program.getProgram());
486	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram failed");
487
488	gl.clear(GL_COLOR_BUFFER_BIT);
489
490	glu::draw(renderContext, program.getProgram(),
491			  static_cast<int>(vertexArrays.size()), vertexArrays.data(),
492			  glu::pr::TriangleStrip(static_cast<int>(indices.size()), indices.data()));
493
494	const auto								pixelSize		= tcu::getPixelSize(textureFormat);
495	vector<deUint8>							fbData			(RENDERTARGET_WIDTH * RENDERTARGET_HEIGHT * pixelSize);
496
497	if (pixelSize < 4)
498		gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
499
500	gl.readPixels(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, transferFormat.format, transferFormat.dataType, fbData.data());
501	GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
502
503	tcu::ConstPixelBufferAccess				fbAccess		{ textureFormat, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, 1, fbData.data() };
504	const auto								expectedColor	= tcu::RGBA::green().toVec();
505	bool pass = true;
506	for(int y = 0; pass && y < RENDERTARGET_HEIGHT; ++y)
507		for(int x = 0; x < RENDERTARGET_WIDTH; ++x)
508			if (fbAccess.getPixel(x,y) != expectedColor)
509			{
510				pass = false;
511				break;
512			}
513
514	releaseRenderTarget();
515
516	const qpTestResult						result			= (pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL);
517	const char*								desc			= (pass ? "Pass" : "Pixel mismatch; vector initialization failed");
518
519	m_testCtx.setTestResult(result, desc);
520
521	return STOP;
522}
523
524void GLSLVectorConstructorTestCase::setupRenderTarget()
525{
526	const auto&		renderContext	= m_context.getRenderContext();
527	const auto&		gl				= renderContext.getFunctions();
528
529	gl.genFramebuffers(1, &m_fboId);
530	GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
531
532	gl.genRenderbuffers(1, &m_rboId);
533	GLU_EXPECT_NO_ERROR(gl.getError(), "GenRenderBuffers");
534
535	gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboId);
536	GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderBuffer");
537
538	gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
539	GLU_EXPECT_NO_ERROR(gl.getError(), "RenderBufferStorage");
540
541	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboId);
542	GLU_EXPECT_NO_ERROR(gl.getError(), "BindFrameBuffer");
543
544	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rboId);
545	GLU_EXPECT_NO_ERROR(gl.getError(), "FrameBufferRenderBuffer");
546
547	glw::GLenum		drawBuffer		= GL_COLOR_ATTACHMENT0;
548	gl.drawBuffers(1, &drawBuffer);
549	GLU_EXPECT_NO_ERROR(gl.getError(), "DrawBuffers");
550
551	glw::GLfloat	clearColor[4]	= { 0, 0, 0, 0 };
552	gl.clearBufferfv(GL_COLOR, 0, clearColor);
553	GLU_EXPECT_NO_ERROR(gl.getError(), "ClearBuffers");
554
555	gl.viewport(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
556	GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
557}
558
559void GLSLVectorConstructorTestCase::releaseRenderTarget()
560{
561	const auto&	renderContext	= m_context.getRenderContext();
562	const auto&	gl				= renderContext.getFunctions();
563	if (m_fboId != 0)
564	{
565		gl.deleteFramebuffers(1, &m_fboId);
566		m_fboId = 0;
567	}
568	if (m_rboId != 0)
569	{
570		gl.deleteRenderbuffers(1, &m_rboId);
571		m_rboId = 0;
572	}
573}
574
575}
576
577GLSLVectorConstructorTests::GLSLVectorConstructorTests(Context& context, glu::GLSLVersion glslVersion)
578	: deqp::TestCaseGroup(context, "glsl_constructors", "GLSL vector constructor tests")
579	, m_glslVersion(glslVersion)
580{
581}
582
583GLSLVectorConstructorTests::~GLSLVectorConstructorTests()
584{
585}
586
587void GLSLVectorConstructorTests::init()
588{
589	for(const auto& params : generateTestParams())
590		addChild(new GLSLVectorConstructorTestCase(m_context, m_glslVersion, params));
591}
592
593} // deqp
594