1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.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 Shader API tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fShaderApiTests.hpp"
25#include "es2fApiCase.hpp"
26#include "tcuTestLog.hpp"
27
28#include "gluRenderContext.hpp"
29#include "gluShaderProgram.hpp"
30#include "gluContextInfo.hpp"
31#include "glwFunctions.hpp"
32#include "glwDefs.hpp"
33#include "glwEnums.hpp"
34
35#include "deString.h"
36
37#include "deRandom.hpp"
38#include "deStringUtil.hpp"
39
40#include <string>
41#include <vector>
42#include <map>
43
44using namespace glw; // GL types
45
46namespace deqp
47{
48namespace gles2
49{
50namespace Functional
51{
52
53using tcu::TestLog;
54
55namespace
56{
57
58enum ShaderSourceCaseFlags
59{
60	CASE_EXPLICIT_SOURCE_LENGTHS	= 1,
61	CASE_RANDOM_NULL_TERMINATED		= 2
62};
63
64struct ShaderSources
65{
66	std::vector<std::string>	strings;
67	std::vector<int>			lengths;
68};
69
70// Simple shaders
71
72const char* getSimpleShaderSource (const glu::ShaderType shaderType)
73{
74	const char* simpleVertexShaderSource	= "void main (void) { gl_Position = vec4(0.0); }\n";
75	const char* simpleFragmentShaderSource	= "void main (void) { gl_FragColor = vec4(0.0); }\n";
76
77	switch (shaderType)
78	{
79		case glu::SHADERTYPE_VERTEX:
80			return simpleVertexShaderSource;
81		case glu::SHADERTYPE_FRAGMENT:
82			return simpleFragmentShaderSource;
83		default:
84			DE_ASSERT(DE_FALSE);
85	}
86
87	return 0;
88}
89
90void setShaderSources (glu::Shader& shader, const ShaderSources& sources)
91{
92	std::vector<const char*> cStrings (sources.strings.size(), 0);
93
94	for (size_t ndx = 0; ndx < sources.strings.size(); ndx++)
95		cStrings[ndx] = sources.strings[ndx].c_str();
96
97	if (sources.lengths.size() > 0)
98		shader.setSources((int)cStrings.size(), &cStrings[0], &sources.lengths[0]);
99	else
100		shader.setSources((int)cStrings.size(), &cStrings[0], 0);
101}
102
103void sliceSourceString (const std::string& in, ShaderSources& out, const int numSlices, const size_t paddingLength = 0)
104{
105	DE_ASSERT(numSlices > 0);
106
107	const size_t		sliceSize			= in.length() / numSlices;
108	const size_t		sliceSizeRemainder	= in.length() - (sliceSize * numSlices);
109	const std::string	padding				(paddingLength, 'E');
110
111	for (int i = 0; i < numSlices; i++)
112	{
113		out.strings.push_back(in.substr(i * sliceSize, sliceSize) + padding);
114
115		if (paddingLength > 0)
116			out.lengths.push_back((int)sliceSize);
117	}
118
119	if (sliceSizeRemainder > 0)
120	{
121		const std::string	lastString			= in.substr(numSlices * sliceSize);
122		const int			lastStringLength	= (int)lastString.length();
123
124		out.strings.push_back(lastString + padding);
125
126		if (paddingLength > 0)
127			out.lengths.push_back(lastStringLength);
128	}
129}
130
131void queryShaderInfo (glu::RenderContext& renderCtx, deUint32 shader, glu::ShaderInfo& info)
132{
133	const glw::Functions& gl = renderCtx.getFunctions();
134
135	info.compileOk		= false;
136	info.compileTimeUs	= 0;
137	info.infoLog.clear();
138
139	// Query source, status & log.
140	{
141		int	compileStatus	= 0;
142		int sourceLen		= 0;
143		int	infoLogLen		= 0;
144		int	unusedLen;
145
146		gl.getShaderiv(shader, GL_COMPILE_STATUS,			&compileStatus);
147		gl.getShaderiv(shader, GL_SHADER_SOURCE_LENGTH,	&sourceLen);
148		gl.getShaderiv(shader, GL_INFO_LOG_LENGTH,		&infoLogLen);
149		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv()");
150
151		info.compileOk = compileStatus != GL_FALSE;
152
153		if (sourceLen > 0)
154		{
155			std::vector<char> source(sourceLen);
156			gl.getShaderSource(shader, (int)source.size(), &unusedLen, &source[0]);
157			info.source = std::string(&source[0], sourceLen);
158		}
159
160		if (infoLogLen > 0)
161		{
162			std::vector<char> infoLog(infoLogLen);
163			gl.getShaderInfoLog(shader, (int)infoLog.size(), &unusedLen, &infoLog[0]);
164			info.infoLog = std::string(&infoLog[0], infoLogLen);
165		}
166	}
167}
168
169// Shader source generator
170
171class SourceGenerator
172{
173public:
174	virtual				~SourceGenerator	(void)	{}
175
176	virtual std::string	next				(const glu::ShaderType shaderType)			= 0;
177	virtual bool		finished			(const glu::ShaderType shaderType) const	= 0;
178};
179
180class ConstantShaderGenerator : public SourceGenerator
181{
182public:
183				ConstantShaderGenerator		(de::Random& rnd)	: m_rnd(rnd)	{}
184				~ConstantShaderGenerator	(void)								{}
185
186	bool		finished					(const glu::ShaderType shaderType) const	{ DE_UNREF(shaderType); return false; }
187
188	std::string	next						(const glu::ShaderType shaderType);
189
190private:
191	de::Random	m_rnd;
192};
193
194std::string ConstantShaderGenerator::next (const glu::ShaderType shaderType)
195{
196	DE_ASSERT(shaderType == glu::SHADERTYPE_VERTEX || shaderType == glu::SHADERTYPE_FRAGMENT);
197
198	const float			value		= m_rnd.getFloat(0.0f, 1.0f);
199	const std::string	valueString	= de::toString(value);
200	const std::string	outputName	= (shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "gl_FragColor";
201
202	std::string source =
203		"#version 100\n"
204		"void main (void) { " + outputName + " = vec4(" + valueString + "); }\n";
205
206	return source;
207}
208
209// Shader allocation utility
210
211class ShaderAllocator
212{
213public:
214					ShaderAllocator		(glu::RenderContext& context, SourceGenerator& generator);
215					~ShaderAllocator	(void);
216
217	bool			hasShader			(const glu::ShaderType shaderType);
218
219	void			setSource			(const glu::ShaderType shaderType);
220
221	glu::Shader&	createShader		(const glu::ShaderType shaderType);
222	void			deleteShader		(const glu::ShaderType shaderType);
223
224	glu::Shader&	get					(const glu::ShaderType shaderType)	{ DE_ASSERT(hasShader(shaderType)); return *m_shaders[shaderType]; }
225
226private:
227	const glu::RenderContext&				m_context;
228	SourceGenerator&						m_srcGen;
229	std::map<glu::ShaderType, glu::Shader*>	m_shaders;
230};
231
232ShaderAllocator::ShaderAllocator (glu::RenderContext& context, SourceGenerator& generator)
233	: m_context	(context)
234	, m_srcGen	(generator)
235{
236}
237
238ShaderAllocator::~ShaderAllocator (void)
239{
240	for (std::map<glu::ShaderType, glu::Shader*>::iterator shaderIter = m_shaders.begin(); shaderIter != m_shaders.end(); shaderIter++)
241		delete shaderIter->second;
242	m_shaders.clear();
243}
244
245bool ShaderAllocator::hasShader (const glu::ShaderType shaderType)
246{
247	if (m_shaders.find(shaderType) != m_shaders.end())
248		return true;
249	else
250		return false;
251}
252
253glu::Shader& ShaderAllocator::createShader (const glu::ShaderType shaderType)
254{
255	DE_ASSERT(!this->hasShader(shaderType));
256
257	glu::Shader* const	shader	= new glu::Shader(m_context, shaderType);
258
259	m_shaders[shaderType] = shader;
260	this->setSource(shaderType);
261
262	return *shader;
263}
264
265void ShaderAllocator::deleteShader (const glu::ShaderType shaderType)
266{
267	DE_ASSERT(this->hasShader(shaderType));
268
269	delete m_shaders[shaderType];
270	m_shaders.erase(shaderType);
271}
272
273void ShaderAllocator::setSource (const glu::ShaderType shaderType)
274{
275	DE_ASSERT(this->hasShader(shaderType));
276	DE_ASSERT(!m_srcGen.finished(shaderType));
277
278	const std::string	source	= m_srcGen.next(shaderType);
279	const char* const	cSource	= source.c_str();
280
281	m_shaders[shaderType]->setSources(1, &cSource, 0);
282}
283
284// Logging utilities
285
286void logShader (TestLog& log, glu::RenderContext& renderCtx, glu::Shader& shader)
287{
288	glu::ShaderInfo info;
289	queryShaderInfo(renderCtx, shader.getShader(), info);
290
291	log << TestLog::Shader(getLogShaderType(shader.getType()), info.source, info.compileOk, info.infoLog);
292}
293
294void logProgram (TestLog& log, glu::RenderContext& renderCtx, glu::Program& program, ShaderAllocator& shaders)
295{
296	log << TestLog::ShaderProgram(program.getLinkStatus(), program.getInfoLog());
297
298	for (int shaderTypeInt = 0; shaderTypeInt < glu::SHADERTYPE_LAST; shaderTypeInt++)
299	{
300		const glu::ShaderType shaderType = (glu::ShaderType)shaderTypeInt;
301
302		if (shaders.hasShader(shaderType))
303			logShader(log, renderCtx, shaders.get(shaderType));
304	}
305
306	log << TestLog::EndShaderProgram;
307}
308
309void logVertexFragmentProgram (TestLog& log, glu::RenderContext& renderCtx, glu::Program& program, glu::Shader& vertShader, glu::Shader& fragShader)
310{
311	DE_ASSERT(vertShader.getType() == glu::SHADERTYPE_VERTEX && fragShader.getType() == glu::SHADERTYPE_FRAGMENT);
312
313	log << TestLog::ShaderProgram(program.getLinkStatus(), program.getInfoLog());
314
315	logShader(log, renderCtx, vertShader);
316	logShader(log, renderCtx, fragShader);
317
318	log << TestLog::EndShaderProgram;
319}
320
321} // anonymous
322
323// Simple glCreateShader() case
324
325class CreateShaderCase : public ApiCase
326{
327public:
328	CreateShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
329		: ApiCase		(context, name, desc)
330		, m_shaderType	(shaderType)
331	{
332	}
333
334	void test (void)
335	{
336		const GLuint shaderObject = glCreateShader(glu::getGLShaderType(m_shaderType));
337
338		TCU_CHECK(shaderObject != 0);
339
340		glDeleteShader(shaderObject);
341	}
342
343private:
344	const glu::ShaderType m_shaderType;
345};
346
347// Simple glCompileShader() case
348
349class CompileShaderCase : public ApiCase
350{
351public:
352	CompileShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
353		: ApiCase		(context, name, desc)
354		, m_shaderType	(shaderType)
355	{
356	}
357
358	bool checkCompileStatus (const GLuint shaderObject)
359	{
360		GLint compileStatus = -1;
361		glGetShaderiv(shaderObject, GL_COMPILE_STATUS, &compileStatus);
362		GLU_CHECK();
363
364		return (compileStatus == GL_TRUE);
365	}
366
367	void test (void)
368	{
369		const char*		shaderSource	= getSimpleShaderSource(m_shaderType);
370		const GLuint	shaderObject	= glCreateShader(glu::getGLShaderType(m_shaderType));
371
372		TCU_CHECK(shaderObject != 0);
373
374		glShaderSource(shaderObject, 1, &shaderSource, 0);
375		glCompileShader(shaderObject);
376
377		TCU_CHECK(checkCompileStatus(shaderObject));
378
379		glDeleteShader(shaderObject);
380	}
381
382private:
383	const glu::ShaderType m_shaderType;
384};
385
386// Base class for simple program API tests
387
388class SimpleProgramCase : public ApiCase
389{
390public:
391	SimpleProgramCase (Context& context, const char* name, const char* desc)
392		: ApiCase 		(context, name, desc)
393		, m_vertShader	(0)
394		, m_fragShader	(0)
395		, m_program		(0)
396	{
397	}
398
399	virtual ~SimpleProgramCase (void)
400	{
401	}
402
403	virtual void compileShaders (void)
404	{
405		const char*		vertSource	= getSimpleShaderSource(glu::SHADERTYPE_VERTEX);
406		const char*		fragSource	= getSimpleShaderSource(glu::SHADERTYPE_FRAGMENT);
407
408		const GLuint	vertShader	= glCreateShader(GL_VERTEX_SHADER);
409		const GLuint	fragShader	= glCreateShader(GL_FRAGMENT_SHADER);
410
411		TCU_CHECK(vertShader != 0);
412		TCU_CHECK(fragShader != 0);
413
414		glShaderSource(vertShader, 1, &vertSource, 0);
415		glCompileShader(vertShader);
416
417		glShaderSource(fragShader, 1, &fragSource, 0);
418		glCompileShader(fragShader);
419
420		GLU_CHECK();
421
422		m_vertShader = vertShader;
423		m_fragShader = fragShader;
424	}
425
426	void linkProgram (void)
427	{
428		const GLuint program = glCreateProgram();
429
430		TCU_CHECK(program != 0);
431
432		glAttachShader(program, m_vertShader);
433		glAttachShader(program, m_fragShader);
434		GLU_CHECK();
435
436		glLinkProgram(program);
437
438		m_program = program;
439	}
440
441	void cleanup (void)
442	{
443		glDeleteShader(m_vertShader);
444		glDeleteShader(m_fragShader);
445		glDeleteProgram(m_program);
446	}
447
448protected:
449	GLuint	m_vertShader;
450	GLuint	m_fragShader;
451	GLuint	m_program;
452};
453
454// glDeleteShader() case
455
456class DeleteShaderCase : public SimpleProgramCase
457{
458public:
459	DeleteShaderCase (Context& context, const char* name, const char* desc)
460		: SimpleProgramCase (context, name, desc)
461	{
462	}
463
464	bool checkDeleteStatus(GLuint shader)
465	{
466		GLint deleteStatus = -1;
467		glGetShaderiv(shader, GL_DELETE_STATUS, &deleteStatus);
468		GLU_CHECK();
469
470		return (deleteStatus == GL_TRUE);
471	}
472
473	void deleteShaders (void)
474	{
475		glDeleteShader(m_vertShader);
476		glDeleteShader(m_fragShader);
477		GLU_CHECK();
478	}
479
480	void test (void)
481	{
482		compileShaders();
483		linkProgram();
484		GLU_CHECK();
485
486		deleteShaders();
487
488		TCU_CHECK(checkDeleteStatus(m_vertShader) && checkDeleteStatus(m_fragShader));
489
490		glDeleteProgram(m_program);
491
492		TCU_CHECK(!(glIsShader(m_vertShader) || glIsShader(m_fragShader)));
493	}
494};
495
496// Simple glLinkProgram() case
497
498class LinkVertexFragmentCase : public SimpleProgramCase
499{
500public:
501	LinkVertexFragmentCase (Context& context, const char* name, const char* desc)
502		: SimpleProgramCase (context, name, desc)
503	{
504	}
505
506	bool checkLinkStatus (const GLuint programObject)
507	{
508		GLint linkStatus = -1;
509		glGetProgramiv(programObject, GL_LINK_STATUS, &linkStatus);
510		GLU_CHECK();
511
512		return (linkStatus == GL_TRUE);
513	}
514
515	void test (void)
516	{
517		compileShaders();
518		linkProgram();
519
520		GLU_CHECK_MSG("Linking failed.");
521		TCU_CHECK_MSG(checkLinkStatus(m_program), "Fail, expected LINK_STATUS to be TRUE.");
522
523		cleanup();
524	}
525};
526
527class ShaderSourceReplaceCase : public ApiCase
528{
529public:
530	ShaderSourceReplaceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
531		: ApiCase		(context, name, desc)
532		, m_shaderType	(shaderType)
533	{
534	}
535
536	std::string generateFirstSource (void)
537	{
538		return getSimpleShaderSource(m_shaderType);
539	}
540
541	std::string generateSecondSource (void)
542	{
543		std::string str;
544
545		str  = "#version 100\n";
546		str += "precision highp float;\n\n";
547
548		str += "void main()\n";
549		str += "{\n";
550		str += "	float variable = 1.0;\n";
551
552		if		(m_shaderType == glu::SHADERTYPE_VERTEX)	str += "	gl_Position = vec4(variable);\n";
553		else if	(m_shaderType == glu::SHADERTYPE_FRAGMENT)	str += "	gl_FragColor = vec4(variable);\n";
554
555		str += "}\n";
556
557		return str;
558	}
559
560	GLint getSourceLength (glu::Shader& shader)
561	{
562		GLint sourceLength = 0;
563		glGetShaderiv(shader.getShader(), GL_SHADER_SOURCE_LENGTH, &sourceLength);
564		GLU_CHECK();
565
566		return sourceLength;
567	}
568
569	std::string readSource (glu::Shader& shader)
570	{
571		const GLint			sourceLength	= getSourceLength(shader);
572		std::vector<char>	sourceBuffer	(sourceLength + 1);
573
574		glGetShaderSource(shader.getShader(), (GLsizei)sourceBuffer.size(), 0, &sourceBuffer[0]);
575
576		return std::string(&sourceBuffer[0]);
577	}
578
579	void verifyShaderSourceReplaced (glu::Shader& shader, const std::string& firstSource, const std::string& secondSource)
580	{
581		TestLog&			log		= m_testCtx.getLog();
582		const std::string	result	= readSource(shader);
583
584		if (result == firstSource)
585		{
586			log << TestLog::Message << "Fail, source was not replaced." << TestLog::EndMessage;
587			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Shader source nor replaced");
588		}
589		else if (result != secondSource)
590		{
591			log << TestLog::Message << "Fail, invalid shader source." << TestLog::EndMessage;
592			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid source");
593		}
594	}
595
596	void test (void)
597	{
598		TestLog&			log				= m_testCtx.getLog();
599
600		glu::Shader			shader			(m_context.getRenderContext(), m_shaderType);
601
602		const std::string	firstSourceStr	= generateFirstSource();
603		const std::string	secondSourceStr	= generateSecondSource();
604
605		const char*			firstSource		= firstSourceStr.c_str();
606		const char*			secondSource	= secondSourceStr.c_str();
607
608		log << TestLog::Message << "Setting shader source." << TestLog::EndMessage;
609
610		shader.setSources(1, &firstSource, 0);
611		GLU_CHECK();
612
613		log << TestLog::Message << "Replacing shader source." << TestLog::EndMessage;
614
615		shader.setSources(1, &secondSource, 0);
616		GLU_CHECK();
617
618		verifyShaderSourceReplaced(shader, firstSourceStr, secondSourceStr);
619	}
620
621private:
622	glu::ShaderType	m_shaderType;
623};
624
625// glShaderSource() split source case
626
627class ShaderSourceSplitCase : public ApiCase
628{
629public:
630	ShaderSourceSplitCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType, const int numSlices, const deUint32 flags = 0)
631		: ApiCase			(context, name, desc)
632		, m_rnd				(deStringHash(getName()) ^ 0x4fb2337d)
633		, m_shaderType		(shaderType)
634		, m_numSlices		(numSlices)
635		, m_explicitLengths	((flags & CASE_EXPLICIT_SOURCE_LENGTHS)	!= 0)
636		, m_randomNullTerm	((flags & CASE_RANDOM_NULL_TERMINATED)	!= 0)
637	{
638		DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT);
639	}
640
641	virtual ~ShaderSourceSplitCase (void)
642	{
643	}
644
645	std::string generateFullSource (void)
646	{
647		std::string str;
648
649		str  = "#version 100\n";
650		str += "precision highp float;\n\n";
651
652		str += "void main()\n";
653		str += "{\n";
654		str += "	float variable = 1.0;\n";
655
656		if		(m_shaderType == glu::SHADERTYPE_VERTEX)	str += "	gl_Position = vec4(variable);\n";
657		else if	(m_shaderType == glu::SHADERTYPE_FRAGMENT)	str += "	gl_FragColor = vec4(variable);\n";
658
659		str += "}\n";
660
661		return str;
662	}
663
664	void insertRandomNullTermStrings (ShaderSources& sources)
665	{
666		const int			numInserts	= de::max(m_numSlices >> 2, 1);
667		std::vector<int>	indices		(sources.strings.size(), 0);
668
669		DE_ASSERT(sources.lengths.size() > 0);
670		DE_ASSERT(sources.lengths.size() == sources.strings.size());
671
672		for (int i = 0; i < (int)sources.strings.size(); i++)
673			indices[i] = i;
674
675		m_rnd.shuffle(indices.begin(), indices.end());
676
677		for (int i = 0; i < numInserts; i++)
678		{
679			const int			ndx				= indices[i];
680			const int			unpaddedLength	= sources.lengths[ndx];
681			const std::string	unpaddedString	= sources.strings[ndx].substr(0, unpaddedLength);
682
683			sources.strings[ndx] = unpaddedString;
684			sources.lengths[ndx] = m_rnd.getInt(-10, -1);
685		}
686	}
687
688	void generateSources (ShaderSources& sources)
689	{
690		const size_t	paddingLength	= (m_explicitLengths ? 10 : 0);
691		std::string		str				= generateFullSource();
692
693		sliceSourceString(str, sources, m_numSlices, paddingLength);
694
695		if (m_randomNullTerm)
696			insertRandomNullTermStrings(sources);
697	}
698
699	void buildProgram (glu::Shader& shader)
700	{
701		TestLog&				log					= m_testCtx.getLog();
702		glu::RenderContext&		renderCtx			= m_context.getRenderContext();
703
704		const glu::ShaderType	supportShaderType	= (m_shaderType == glu::SHADERTYPE_FRAGMENT ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT);
705		const char*				supportShaderSource	= getSimpleShaderSource(supportShaderType);
706		glu::Shader				supportShader		(renderCtx, supportShaderType);
707
708		glu::Program			program				(renderCtx);
709
710		supportShader.setSources(1, &supportShaderSource, 0);
711		supportShader.compile();
712
713		program.attachShader(shader.getShader());
714		program.attachShader(supportShader.getShader());
715
716		program.link();
717
718		if (m_shaderType == glu::SHADERTYPE_VERTEX)
719			logVertexFragmentProgram(log, renderCtx, program, shader, supportShader);
720		else
721			logVertexFragmentProgram(log, renderCtx, program, supportShader, shader);
722	}
723
724	void test (void)
725	{
726		TestLog&			log			= m_testCtx.getLog();
727		glu::RenderContext&	renderCtx	= m_context.getRenderContext();
728
729		ShaderSources		sources;
730		glu::Shader			shader		(renderCtx, m_shaderType);
731
732		generateSources(sources);
733		setShaderSources(shader, sources);
734		shader.compile();
735
736		buildProgram(shader);
737
738		if (!shader.getCompileStatus())
739		{
740			log << TestLog::Message << "Compilation failed." << TestLog::EndMessage;
741			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
742		}
743	}
744
745private:
746	de::Random		m_rnd;
747
748	glu::ShaderType	m_shaderType;
749	const int		m_numSlices;
750
751	const bool		m_explicitLengths;
752	const bool		m_randomNullTerm;
753};
754
755// Base class for program state persistence cases
756
757class ProgramStateCase : public ApiCase
758{
759public:
760					ProgramStateCase	(Context& context, const char* name, const char* desc, glu::ShaderType shaderType);
761	virtual			~ProgramStateCase	(void)	{}
762
763	void			buildProgram		(glu::Program& program, ShaderAllocator& shaders);
764	void			verify				(glu::Program& program, const glu::ProgramInfo& reference);
765
766	void			test				(void);
767
768	virtual void	executeForProgram	(glu::Program& program, ShaderAllocator& shaders)	= 0;
769
770protected:
771	de::Random					m_rnd;
772	const glu::ShaderType		m_shaderType;
773};
774
775ProgramStateCase::ProgramStateCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
776	: ApiCase		(context, name, desc)
777	, m_rnd			(deStringHash(name) ^ 0x713de0ca)
778	, m_shaderType	(shaderType)
779{
780	DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT);
781}
782
783void ProgramStateCase::buildProgram (glu::Program& program, ShaderAllocator& shaders)
784{
785	TestLog&		log			= m_testCtx.getLog();
786
787	glu::Shader&	vertShader	= shaders.createShader(glu::SHADERTYPE_VERTEX);
788	glu::Shader&	fragShader	= shaders.createShader(glu::SHADERTYPE_FRAGMENT);
789
790	vertShader.compile();
791	fragShader.compile();
792
793	program.attachShader(vertShader.getShader());
794	program.attachShader(fragShader.getShader());
795	program.link();
796
797	logProgram(log, m_context.getRenderContext(), program, shaders);
798}
799
800void ProgramStateCase::verify (glu::Program& program, const glu::ProgramInfo& reference)
801{
802	TestLog&				log			= m_testCtx.getLog();
803	const glu::ProgramInfo&	programInfo	= program.getInfo();
804
805	if (!programInfo.linkOk)
806	{
807		log << TestLog::Message << "Fail, link status may only change as a result of linking or loading a program binary." << TestLog::EndMessage;
808		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Link status changed");
809	}
810
811	if (programInfo.linkTimeUs != reference.linkTimeUs)
812	{
813		log << TestLog::Message << "Fail, reported link time changed." << TestLog::EndMessage;
814		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Link time changed");
815	}
816
817	if (programInfo.infoLog != reference.infoLog)
818	{
819		log << TestLog::Message << "Fail, program infolog changed." << TestLog::EndMessage;
820		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Infolog changed");
821	}
822}
823
824void ProgramStateCase::test (void)
825{
826	TestLog&				log			= m_testCtx.getLog();
827	glu::RenderContext&		renderCtx	= m_context.getRenderContext();
828
829	ConstantShaderGenerator	sourceGen	(m_rnd);
830
831	ShaderAllocator			shaders		(renderCtx, sourceGen);
832	glu::Program			program		(renderCtx);
833
834	buildProgram(program, shaders);
835
836	if (program.getLinkStatus())
837	{
838		glu::ProgramInfo programInfo = program.getInfo();
839
840		executeForProgram(program, shaders);
841
842		verify(program, programInfo);
843
844		logProgram(log, renderCtx, program, shaders);
845	}
846	else
847	{
848		log << TestLog::Message << "Fail, couldn't link program." << TestLog::EndMessage;
849		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Linking failed");
850	}
851}
852
853// Program state case utilities
854
855namespace
856{
857
858template<class T>
859void addProgramStateCase (TestCaseGroup* group, Context& context, const std::string& name, const std::string& desc)
860{
861	for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++)
862	{
863		const glu::ShaderType	shaderType		= (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX;
864		const std::string		shaderTypeName	= getShaderTypeName(shaderType);
865
866		const std::string		caseName		= name + "_" + shaderTypeName;
867		const std::string		caseDesc		= "Build program, " + desc + ", for " + shaderTypeName + " shader.";
868
869		group->addChild(new T(context, caseName.c_str(), caseDesc.c_str(), shaderType));
870	}
871}
872
873} // anonymous
874
875// Specialized program state cases
876
877class ProgramStateDetachShaderCase : public ProgramStateCase
878{
879public:
880	ProgramStateDetachShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
881		: ProgramStateCase (context, name, desc, shaderType)
882	{
883	}
884
885	virtual ~ProgramStateDetachShaderCase (void)
886	{
887	}
888
889	void executeForProgram (glu::Program& program, ShaderAllocator& shaders)
890	{
891		TestLog&		log			= m_testCtx.getLog();
892		glu::Shader&	caseShader	= shaders.get(m_shaderType);
893
894		log << TestLog::Message << "Detaching " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage;
895		program.detachShader(caseShader.getShader());
896	}
897};
898
899class ProgramStateReattachShaderCase : public ProgramStateCase
900{
901public:
902	ProgramStateReattachShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
903		: ProgramStateCase (context, name, desc, shaderType)
904	{
905	}
906
907	virtual ~ProgramStateReattachShaderCase (void)
908	{
909	}
910
911	void executeForProgram (glu::Program& program, ShaderAllocator& shaders)
912	{
913		TestLog&		log			= m_testCtx.getLog();
914		glu::Shader&	caseShader	= shaders.get(m_shaderType);
915
916		log << TestLog::Message << "Reattaching " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage;
917		program.detachShader(caseShader.getShader());
918		program.attachShader(caseShader.getShader());
919	}
920};
921
922class ProgramStateDeleteShaderCase : public ProgramStateCase
923{
924public:
925	ProgramStateDeleteShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
926		: ProgramStateCase (context, name, desc, shaderType)
927	{
928	}
929
930	virtual ~ProgramStateDeleteShaderCase (void)
931	{
932	}
933
934	void executeForProgram (glu::Program& program, ShaderAllocator& shaders)
935	{
936		TestLog&		log			= m_testCtx.getLog();
937		glu::Shader&	caseShader	= shaders.get(m_shaderType);
938
939		log << TestLog::Message << "Deleting " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage;
940		program.detachShader(caseShader.getShader());
941		shaders.deleteShader(m_shaderType);
942	}
943};
944
945class ProgramStateReplaceShaderCase : public ProgramStateCase
946{
947public:
948	ProgramStateReplaceShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
949		: ProgramStateCase (context, name, desc, shaderType)
950	{
951	}
952
953	virtual ~ProgramStateReplaceShaderCase (void)
954	{
955	}
956
957	void executeForProgram (glu::Program& program, ShaderAllocator& shaders)
958	{
959		TestLog&		log			= m_testCtx.getLog();
960		glu::Shader&	caseShader	= shaders.get(m_shaderType);
961
962		log << TestLog::Message << "Deleting and replacing " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage;
963		program.detachShader(caseShader.getShader());
964		shaders.deleteShader(m_shaderType);
965		program.attachShader(shaders.createShader(m_shaderType).getShader());
966	}
967};
968
969class ProgramStateRecompileShaderCase : public ProgramStateCase
970{
971public:
972	ProgramStateRecompileShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
973		: ProgramStateCase (context, name, desc, shaderType)
974	{
975	}
976
977	virtual ~ProgramStateRecompileShaderCase (void)
978	{
979	}
980
981	void executeForProgram (glu::Program& program, ShaderAllocator& shaders)
982	{
983		TestLog&		log			= m_testCtx.getLog();
984		glu::Shader&	caseShader	= shaders.get(m_shaderType);
985
986		log << TestLog::Message << "Recompiling " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage;
987		caseShader.compile();
988		DE_UNREF(program);
989	}
990};
991
992class ProgramStateReplaceSourceCase : public ProgramStateCase
993{
994public:
995	ProgramStateReplaceSourceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType)
996		: ProgramStateCase (context, name, desc, shaderType)
997	{
998	}
999
1000	virtual ~ProgramStateReplaceSourceCase (void)
1001	{
1002	}
1003
1004	void executeForProgram (glu::Program& program, ShaderAllocator& shaders)
1005	{
1006		TestLog&		log			= m_testCtx.getLog();
1007		glu::Shader&	caseShader	= shaders.get(m_shaderType);
1008
1009		log << TestLog::Message << "Replacing " + std::string(getShaderTypeName(m_shaderType)) + " shader source and recompiling" << TestLog::EndMessage;
1010		shaders.setSource(m_shaderType);
1011		caseShader.compile();
1012		DE_UNREF(program);
1013	}
1014};
1015
1016// Test group
1017
1018ShaderApiTests::ShaderApiTests (Context& context)
1019	: TestCaseGroup(context, "shader_api", "Shader API Cases")
1020{
1021}
1022
1023ShaderApiTests::~ShaderApiTests (void)
1024{
1025}
1026
1027void ShaderApiTests::init (void)
1028{
1029	// create and delete shaders
1030	{
1031		TestCaseGroup* createDeleteGroup = new TestCaseGroup(m_context, "create_delete", "glCreateShader() tests");
1032		addChild(createDeleteGroup);
1033
1034		createDeleteGroup->addChild(new CreateShaderCase(m_context,	"create_vertex_shader",		"Create vertex shader object",		glu::SHADERTYPE_VERTEX));
1035		createDeleteGroup->addChild(new CreateShaderCase(m_context,	"create_fragment_shader",	"Create fragment shader object",	glu::SHADERTYPE_FRAGMENT));
1036
1037		createDeleteGroup->addChild(new DeleteShaderCase(m_context,	"delete_vertex_fragment",	"Delete vertex shader and fragment shader"));
1038	}
1039
1040	// compile and link
1041	{
1042		TestCaseGroup* compileLinkGroup = new TestCaseGroup(m_context, "compile_link", "Compile and link tests");
1043		addChild(compileLinkGroup);
1044
1045		compileLinkGroup->addChild(new CompileShaderCase(m_context,	"compile_vertex_shader",	"Compile vertex shader",	glu::SHADERTYPE_VERTEX));
1046		compileLinkGroup->addChild(new CompileShaderCase(m_context,	"compile_fragment_shader",	"Compile fragment shader",	glu::SHADERTYPE_FRAGMENT));
1047
1048		compileLinkGroup->addChild(new LinkVertexFragmentCase(m_context,	"link_vertex_fragment",	"Link vertex and fragment shaders"));
1049	}
1050
1051	// shader source
1052	{
1053		TestCaseGroup* shaderSourceGroup = new TestCaseGroup(m_context, "shader_source", "glShaderSource() tests");
1054		addChild(shaderSourceGroup);
1055
1056		for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++)
1057		{
1058			const glu::ShaderType	shaderType	= (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX;
1059
1060			const std::string		caseName	= std::string("replace_source") + ((shaderType == glu::SHADERTYPE_FRAGMENT) ? "_fragment" : "_vertex");
1061			const std::string		caseDesc	= std::string("Replace source code of ") + ((shaderType == glu::SHADERTYPE_FRAGMENT) ? "fragment" : "vertex") + " shader.";
1062
1063			shaderSourceGroup->addChild(new ShaderSourceReplaceCase(m_context, caseName.c_str(), caseDesc.c_str(), shaderType));
1064		}
1065
1066		for (int stringLengthsInt	= 0; stringLengthsInt < 3; stringLengthsInt++)
1067		for (int caseNdx = 1; caseNdx <= 3; caseNdx++)
1068		for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++)
1069		{
1070			const int				numSlices		= 1 << caseNdx;
1071			const glu::ShaderType	shaderType		= (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX;
1072
1073			const bool				explicitLengths	= (stringLengthsInt != 0);
1074			const bool				randomNullTerm	= (stringLengthsInt == 2);
1075
1076			const deUint32			flags			= (explicitLengths	? CASE_EXPLICIT_SOURCE_LENGTHS	: 0)
1077													| (randomNullTerm	? CASE_RANDOM_NULL_TERMINATED	: 0);
1078
1079			const std::string		caseName		= "split_source_"
1080													+ de::toString(numSlices)
1081													+ (randomNullTerm ? "_random_negative_length" : (explicitLengths ? "_specify_lengths" : "_null_terminated"))
1082													+ ((shaderType == glu::SHADERTYPE_FRAGMENT) ? "_fragment" : "_vertex");
1083
1084			const std::string		caseDesc		= std::string((shaderType == glu::SHADERTYPE_FRAGMENT) ? "Fragment" : "Vertex")
1085													+ " shader source split into "
1086													+ de::toString(numSlices)
1087													+ " pieces"
1088													+ (explicitLengths ? ", using explicitly specified string lengths" : "")
1089													+ (randomNullTerm ? " with random negative length values" : "");
1090
1091			shaderSourceGroup->addChild(new ShaderSourceSplitCase(m_context, caseName.c_str(), caseDesc.c_str(), shaderType, numSlices, flags));
1092		}
1093	}
1094
1095	// link status and infolog
1096	{
1097		TestCaseGroup* linkStatusGroup = new TestCaseGroup(m_context, "program_state", "Program state persistence tests");
1098		addChild(linkStatusGroup);
1099
1100		addProgramStateCase<ProgramStateDetachShaderCase>		(linkStatusGroup,	m_context,	"detach_shader",	"detach shader");
1101		addProgramStateCase<ProgramStateReattachShaderCase>		(linkStatusGroup,	m_context,	"reattach_shader",	"reattach shader");
1102		addProgramStateCase<ProgramStateDeleteShaderCase>		(linkStatusGroup,	m_context,	"delete_shader",	"delete shader");
1103		addProgramStateCase<ProgramStateReplaceShaderCase>		(linkStatusGroup,	m_context,	"replace_shader",	"replace shader object");
1104		addProgramStateCase<ProgramStateRecompileShaderCase>	(linkStatusGroup,	m_context,	"recompile_shader",	"recompile shader");
1105		addProgramStateCase<ProgramStateReplaceSourceCase>		(linkStatusGroup,	m_context,	"replace_source",	"replace shader source");
1106	}
1107}
1108
1109} // Functional
1110} // gles2
1111} // deqp
1112