1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
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 Wrapper for GL program object.
22 *//*--------------------------------------------------------------------*/
23
24#include "gluShaderProgram.hpp"
25#include "gluRenderContext.hpp"
26#include "glwFunctions.hpp"
27#include "glwEnums.hpp"
28#include "tcuTestLog.hpp"
29#include "deClock.h"
30
31#include <cstring>
32
33using std::string;
34
35namespace glu
36{
37
38// Shader
39
40Shader::Shader (const RenderContext& renderCtx, ShaderType shaderType)
41	: m_gl		(renderCtx.getFunctions())
42	, m_shader	(0)
43{
44	m_info.type	= shaderType;
45	m_shader	= m_gl.createShader(getGLShaderType(shaderType));
46	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader()");
47	TCU_CHECK(m_shader);
48}
49
50Shader::Shader (const glw::Functions& gl, ShaderType shaderType)
51	: m_gl		(gl)
52	, m_shader	(0)
53{
54	m_info.type	= shaderType;
55	m_shader	= m_gl.createShader(getGLShaderType(shaderType));
56	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader()");
57	TCU_CHECK(m_shader);
58}
59
60Shader::~Shader (void)
61{
62	m_gl.deleteShader(m_shader);
63}
64
65void Shader::setSources (int numSourceStrings, const char* const* sourceStrings, const int* lengths)
66{
67	m_gl.shaderSource(m_shader, numSourceStrings, sourceStrings, lengths);
68	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glShaderSource()");
69
70	m_info.source.clear();
71	for (int ndx = 0; ndx < numSourceStrings; ndx++)
72	{
73		const size_t length = lengths && lengths[ndx] >= 0 ? lengths[ndx] : strlen(sourceStrings[ndx]);
74		m_info.source += std::string(sourceStrings[ndx], length);
75	}
76}
77
78void Shader::compile (void)
79{
80	m_info.compileOk		= false;
81	m_info.compileTimeUs	= 0;
82	m_info.infoLog.clear();
83
84	{
85		deUint64 compileStart = deGetMicroseconds();
86		m_gl.compileShader(m_shader);
87		m_info.compileTimeUs = deGetMicroseconds() - compileStart;
88	}
89
90	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCompileShader()");
91
92	// Query status
93	{
94		int compileStatus = 0;
95
96		m_gl.getShaderiv(m_shader, GL_COMPILE_STATUS, &compileStatus);
97		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()");
98
99		m_info.compileOk = compileStatus != GL_FALSE;
100	}
101
102	// Query log
103	{
104		int infoLogLen = 0;
105		int unusedLen;
106
107		m_gl.getShaderiv(m_shader, GL_INFO_LOG_LENGTH, &infoLogLen);
108		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()");
109
110		if (infoLogLen > 0)
111		{
112			// The INFO_LOG_LENGTH query and the buffer query implementations have
113			// very commonly off-by-one errors. Try to work around these issues.
114
115			// add tolerance for off-by-one in log length, buffer write, and for terminator
116			std::vector<char> infoLog(infoLogLen + 3, '\0');
117
118			// claim buf size is one smaller to protect from off-by-one writing over buffer bounds
119			m_gl.getShaderInfoLog(m_shader, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]);
120
121			if (infoLog[(int)(infoLog.size()) - 1] != '\0')
122			{
123				// return whole buffer if null terminator was overwritten
124				m_info.infoLog = std::string(&infoLog[0], infoLog.size());
125			}
126			else
127			{
128				// read as C string. infoLog is guaranteed to be 0-terminated
129				m_info.infoLog = std::string(&infoLog[0]);
130			}
131		}
132	}
133}
134
135void Shader::specialize (const char* entryPoint, glw::GLuint numSpecializationConstants,
136						 const glw::GLuint* constantIndex, const glw::GLuint* constantValue)
137{
138	m_info.compileOk		= false;
139	m_info.compileTimeUs	= 0;
140	m_info.infoLog.clear();
141
142	{
143		deUint64 compileStart = deGetMicroseconds();
144		m_gl.specializeShader(m_shader, entryPoint, numSpecializationConstants, constantIndex, constantValue);
145		m_info.compileTimeUs = deGetMicroseconds() - compileStart;
146	}
147
148	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glSpecializeShader()");
149
150	// Query status
151	{
152		int compileStatus = 0;
153
154		m_gl.getShaderiv(m_shader, GL_COMPILE_STATUS, &compileStatus);
155		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()");
156
157		m_info.compileOk = compileStatus != GL_FALSE;
158	}
159
160	// Query log
161	{
162		int infoLogLen = 0;
163		int unusedLen;
164
165		m_gl.getShaderiv(m_shader, GL_INFO_LOG_LENGTH, &infoLogLen);
166		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv()");
167
168		if (infoLogLen > 0)
169		{
170			// The INFO_LOG_LENGTH query and the buffer query implementations have
171			// very commonly off-by-one errors. Try to work around these issues.
172
173			// add tolerance for off-by-one in log length, buffer write, and for terminator
174			std::vector<char> infoLog(infoLogLen + 3, '\0');
175
176			// claim buf size is one smaller to protect from off-by-one writing over buffer bounds
177			m_gl.getShaderInfoLog(m_shader, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]);
178			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderInfoLog()");
179
180			if (infoLog[(int)(infoLog.size()) - 1] != '\0')
181			{
182				// return whole buffer if null terminator was overwritten
183				m_info.infoLog = std::string(&infoLog[0], infoLog.size());
184			}
185			else
186			{
187				// read as C string. infoLog is guaranteed to be 0-terminated
188				m_info.infoLog = std::string(&infoLog[0]);
189			}
190		}
191	}
192}
193
194// Program
195
196static bool getProgramLinkStatus (const glw::Functions& gl, deUint32 program)
197{
198	int	linkStatus				= 0;
199
200	gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus);
201	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
202	return (linkStatus != GL_FALSE);
203}
204
205static std::string getProgramInfoLog (const glw::Functions& gl, deUint32 program)
206{
207	int infoLogLen = 0;
208	int unusedLen;
209
210	gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
211	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
212
213	if (infoLogLen > 0)
214	{
215		// The INFO_LOG_LENGTH query and the buffer query implementations have
216		// very commonly off-by-one errors. Try to work around these issues.
217
218		// add tolerance for off-by-one in log length, buffer write, and for terminator
219		std::vector<char> infoLog(infoLogLen + 3, '\0');
220
221		// claim buf size is one smaller to protect from off-by-one writing over buffer bounds
222		gl.getProgramInfoLog(program, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]);
223
224		// return whole buffer if null terminator was overwritten
225		if (infoLog[(int)(infoLog.size()) - 1] != '\0')
226			return std::string(&infoLog[0], infoLog.size());
227
228		// read as C string. infoLog is guaranteed to be 0-terminated
229		return std::string(&infoLog[0]);
230	}
231	return std::string();
232}
233
234Program::Program (const RenderContext& renderCtx)
235	: m_gl		(renderCtx.getFunctions())
236	, m_program	(0)
237{
238	m_program = m_gl.createProgram();
239	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateProgram()");
240}
241
242Program::Program (const glw::Functions& gl)
243	: m_gl		(gl)
244	, m_program	(0)
245{
246	m_program = m_gl.createProgram();
247	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateProgram()");
248}
249
250Program::Program (const RenderContext& renderCtx, deUint32 program)
251	: m_gl		(renderCtx.getFunctions())
252	, m_program	(program)
253{
254	m_info.linkOk	= getProgramLinkStatus(m_gl, program);
255	m_info.infoLog	= getProgramInfoLog(m_gl, program);
256}
257
258Program::~Program (void)
259{
260	m_gl.deleteProgram(m_program);
261}
262
263void Program::attachShader (deUint32 shader)
264{
265	m_gl.attachShader(m_program, shader);
266	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glAttachShader()");
267}
268
269void Program::detachShader (deUint32 shader)
270{
271	m_gl.detachShader(m_program, shader);
272	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDetachShader()");
273}
274
275void Program::bindAttribLocation (deUint32 location, const char* name)
276{
277	m_gl.bindAttribLocation(m_program, location, name);
278	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindAttribLocation()");
279}
280
281void Program::transformFeedbackVaryings (int count, const char* const* varyings, deUint32 bufferMode)
282{
283	m_gl.transformFeedbackVaryings(m_program, count, varyings, bufferMode);
284	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTransformFeedbackVaryings()");
285}
286
287void Program::link (void)
288{
289	m_info.linkOk		= false;
290	m_info.linkTimeUs	= 0;
291	m_info.infoLog.clear();
292
293	{
294		deUint64 linkStart = deGetMicroseconds();
295		m_gl.linkProgram(m_program);
296		m_info.linkTimeUs = deGetMicroseconds() - linkStart;
297	}
298	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glLinkProgram()");
299
300	m_info.linkOk	= getProgramLinkStatus(m_gl, m_program);
301	m_info.infoLog	= getProgramInfoLog(m_gl, m_program);
302}
303
304bool Program::isSeparable (void) const
305{
306	int separable = GL_FALSE;
307
308	m_gl.getProgramiv(m_program, GL_PROGRAM_SEPARABLE, &separable);
309	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetProgramiv()");
310
311	return (separable != GL_FALSE);
312}
313
314void Program::setSeparable (bool separable)
315{
316	m_gl.programParameteri(m_program, GL_PROGRAM_SEPARABLE, separable);
317	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glProgramParameteri()");
318}
319
320// ProgramPipeline
321
322ProgramPipeline::ProgramPipeline (const RenderContext& renderCtx)
323	: m_gl			(renderCtx.getFunctions())
324	, m_pipeline	(0)
325{
326	m_gl.genProgramPipelines(1, &m_pipeline);
327	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenProgramPipelines()");
328}
329
330ProgramPipeline::ProgramPipeline (const glw::Functions& gl)
331	: m_gl			(gl)
332	, m_pipeline	(0)
333{
334	m_gl.genProgramPipelines(1, &m_pipeline);
335	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenProgramPipelines()");
336}
337
338ProgramPipeline::~ProgramPipeline (void)
339{
340	m_gl.deleteProgramPipelines(1, &m_pipeline);
341}
342
343void ProgramPipeline::useProgramStages (deUint32 stages, deUint32 program)
344{
345	m_gl.useProgramStages(m_pipeline, stages, program);
346	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgramStages()");
347}
348
349void ProgramPipeline::activeShaderProgram (deUint32 program)
350{
351	m_gl.activeShaderProgram(m_pipeline, program);
352	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glActiveShaderProgram()");
353}
354
355bool ProgramPipeline::isValid (void)
356{
357	glw::GLint status = GL_FALSE;
358	m_gl.validateProgramPipeline(m_pipeline);
359	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glValidateProgramPipeline()");
360
361	m_gl.getProgramPipelineiv(m_pipeline, GL_VALIDATE_STATUS, &status);
362
363	return (status != GL_FALSE);
364}
365
366// ShaderProgram
367
368ShaderProgram::ShaderProgram (const RenderContext& renderCtx, const ProgramSources& sources)
369	: m_program(renderCtx.getFunctions())
370{
371	init(renderCtx.getFunctions(), sources);
372}
373
374ShaderProgram::ShaderProgram (const RenderContext& renderCtx, const ProgramBinaries& binaries)
375	: m_program(renderCtx.getFunctions())
376{
377	init(renderCtx.getFunctions(), binaries);
378}
379
380ShaderProgram::ShaderProgram (const glw::Functions& gl, const ProgramSources& sources)
381	: m_program(gl)
382{
383	init(gl, sources);
384}
385
386ShaderProgram::ShaderProgram (const glw::Functions& gl, const ProgramBinaries& binaries)
387	: m_program(gl)
388{
389	init(gl, binaries);
390}
391
392void ShaderProgram::init (const glw::Functions& gl, const ProgramSources& sources)
393{
394	try
395	{
396		bool shadersOk = true;
397
398		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
399		{
400			for (int shaderNdx = 0; shaderNdx < (int)sources.sources[shaderType].size(); ++shaderNdx)
401			{
402				const char* source	= sources.sources[shaderType][shaderNdx].c_str();
403				const int	length	= (int)sources.sources[shaderType][shaderNdx].size();
404
405				m_shaders[shaderType].reserve(m_shaders[shaderType].size() + 1);
406
407				m_shaders[shaderType].push_back(new Shader(gl, ShaderType(shaderType)));
408				m_shaders[shaderType].back()->setSources(1, &source, &length);
409				m_shaders[shaderType].back()->compile();
410
411				shadersOk = shadersOk && m_shaders[shaderType].back()->getCompileStatus();
412			}
413		}
414
415		if (shadersOk)
416		{
417			for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
418				for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
419					m_program.attachShader(m_shaders[shaderType][shaderNdx]->getShader());
420
421			for (std::vector<AttribLocationBinding>::const_iterator binding = sources.attribLocationBindings.begin(); binding != sources.attribLocationBindings.end(); ++binding)
422				m_program.bindAttribLocation(binding->location, binding->name.c_str());
423
424			DE_ASSERT((sources.transformFeedbackBufferMode == GL_NONE) == sources.transformFeedbackVaryings.empty());
425			if (sources.transformFeedbackBufferMode != GL_NONE)
426			{
427				std::vector<const char*> tfVaryings(sources.transformFeedbackVaryings.size());
428				for (int ndx = 0; ndx < (int)tfVaryings.size(); ndx++)
429					tfVaryings[ndx] = sources.transformFeedbackVaryings[ndx].c_str();
430
431				m_program.transformFeedbackVaryings((int)tfVaryings.size(), &tfVaryings[0], sources.transformFeedbackBufferMode);
432			}
433
434			if (sources.separable)
435				m_program.setSeparable(true);
436
437			m_program.link();
438		}
439	}
440	catch (...)
441	{
442		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
443			for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
444				delete m_shaders[shaderType][shaderNdx];
445		throw;
446	}
447}
448
449void ShaderProgram::init (const glw::Functions& gl, const ProgramBinaries& binaries)
450{
451	try
452	{
453		bool shadersOk = true;
454
455		for (deUint32 binaryNdx = 0; binaryNdx < binaries.binaries.size(); ++binaryNdx)
456		{
457			ShaderBinary shaderBinary = binaries.binaries[binaryNdx];
458			if (!shaderBinary.binary.empty())
459			{
460				const char* binary	= (const char*)shaderBinary.binary.data();
461				const int	length	= (int)(shaderBinary.binary.size() * sizeof(deUint32));
462
463				DE_ASSERT(shaderBinary.shaderEntryPoints.size() == shaderBinary.shaderTypes.size());
464
465				std::vector<Shader*> shaders;
466				for (deUint32 shaderTypeNdx = 0; shaderTypeNdx < shaderBinary.shaderTypes.size(); ++shaderTypeNdx)
467				{
468					ShaderType shaderType = shaderBinary.shaderTypes[shaderTypeNdx];
469
470					Shader* shader = new Shader(gl, ShaderType(shaderType));
471
472					m_shaders[shaderType].reserve(m_shaders[shaderType].size() + 1);
473					m_shaders[shaderType].push_back(shader);
474					shaders.push_back(shader);
475				}
476
477				setBinary(gl, shaders, binaries.binaryFormat, binary, length);
478
479				for (deUint32 shaderNdx = 0; shaderNdx < shaders.size(); ++shaderNdx)
480				{
481					shaders[shaderNdx]->specialize(shaderBinary.shaderEntryPoints[shaderNdx].c_str(),
482												   (deUint32)shaderBinary.specializationIndices.size(),
483												   shaderBinary.specializationIndices.data(),
484												   shaderBinary.specializationValues.data());
485
486					shadersOk = shadersOk && shaders[shaderNdx]->getCompileStatus();
487				}
488			}
489		}
490
491		if (shadersOk)
492		{
493			for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
494				for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
495					m_program.attachShader(m_shaders[shaderType][shaderNdx]->getShader());
496
497			m_program.link();
498		}
499	}
500	catch (...)
501	{
502		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
503			for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
504				delete m_shaders[shaderType][shaderNdx];
505		throw;
506	}
507}
508
509void ShaderProgram::setBinary (const glw::Functions& gl, std::vector<Shader*>& shaders, glw::GLenum binaryFormat, const void* binaryData, const int length)
510{
511	std::vector<glw::GLuint> shaderVec;
512	for (deUint32 shaderNdx = 0; shaderNdx < shaders.size(); ++shaderNdx)
513		shaderVec.push_back(shaders[shaderNdx]->getShader());
514
515	gl.shaderBinary((glw::GLsizei)shaderVec.size(), shaderVec.data(), binaryFormat, binaryData, length);
516	GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderBinary");
517
518	for (deUint32 shaderNdx = 0; shaderNdx < shaders.size(); ++shaderNdx)
519	{
520		glw::GLint shaderState;
521		gl.getShaderiv(shaders[shaderNdx]->getShader(), GL_SPIR_V_BINARY_ARB, &shaderState);
522		GLU_EXPECT_NO_ERROR(gl.getError(), "getShaderiv");
523
524		DE_ASSERT(shaderState == GL_TRUE);
525	}
526}
527
528ShaderProgram::~ShaderProgram (void)
529{
530	for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
531		for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
532			delete m_shaders[shaderType][shaderNdx];
533}
534
535// Utilities
536
537deUint32 getGLShaderType (ShaderType shaderType)
538{
539	static const deUint32 s_typeMap[] =
540	{
541		GL_VERTEX_SHADER,
542		GL_FRAGMENT_SHADER,
543		GL_GEOMETRY_SHADER,
544		GL_TESS_CONTROL_SHADER,
545		GL_TESS_EVALUATION_SHADER,
546		GL_COMPUTE_SHADER,
547		0,
548		0,
549		0,
550		0,
551		0,
552		0,
553		0,
554		0,
555	};
556	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST);
557	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap)));
558	return s_typeMap[shaderType];
559}
560
561deUint32 getGLShaderTypeBit (ShaderType shaderType)
562{
563	static const deUint32 s_typebitMap[] =
564	{
565		GL_VERTEX_SHADER_BIT,
566		GL_FRAGMENT_SHADER_BIT,
567		GL_GEOMETRY_SHADER_BIT,
568		GL_TESS_CONTROL_SHADER_BIT,
569		GL_TESS_EVALUATION_SHADER_BIT,
570		GL_COMPUTE_SHADER_BIT,
571		0,
572		0,
573		0,
574		0,
575		0,
576		0,
577		0,
578		0,
579	};
580	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typebitMap) == SHADERTYPE_LAST);
581	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typebitMap)));
582	return s_typebitMap[shaderType];
583}
584
585qpShaderType getLogShaderType (ShaderType shaderType)
586{
587	static const qpShaderType s_typeMap[] =
588	{
589		QP_SHADER_TYPE_VERTEX,
590		QP_SHADER_TYPE_FRAGMENT,
591		QP_SHADER_TYPE_GEOMETRY,
592		QP_SHADER_TYPE_TESS_CONTROL,
593		QP_SHADER_TYPE_TESS_EVALUATION,
594		QP_SHADER_TYPE_COMPUTE,
595		QP_SHADER_TYPE_RAYGEN,
596		QP_SHADER_TYPE_ANY_HIT,
597		QP_SHADER_TYPE_CLOSEST_HIT,
598		QP_SHADER_TYPE_MISS,
599		QP_SHADER_TYPE_INTERSECTION,
600		QP_SHADER_TYPE_CALLABLE,
601		QP_SHADER_TYPE_TASK,
602		QP_SHADER_TYPE_MESH,
603	};
604	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST);
605	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap)));
606	return s_typeMap[shaderType];
607}
608
609tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderInfo& shaderInfo)
610{
611	return log << tcu::TestLog::Shader(getLogShaderType(shaderInfo.type), shaderInfo.source, shaderInfo.compileOk, shaderInfo.infoLog);
612}
613
614tcu::TestLog& operator<< (tcu::TestLog& log, const Shader& shader)
615{
616	return log << tcu::TestLog::ShaderProgram(false, "Plain shader") << shader.getInfo() << tcu::TestLog::EndShaderProgram;
617}
618
619static void logShaderProgram (tcu::TestLog& log, const ProgramInfo& programInfo, size_t numShaders, const ShaderInfo* const* shaderInfos)
620{
621	log << tcu::TestLog::ShaderProgram(programInfo.linkOk, programInfo.infoLog);
622	try
623	{
624		for (size_t shaderNdx = 0; shaderNdx < numShaders; ++shaderNdx)
625			log << *shaderInfos[shaderNdx];
626	}
627	catch (...)
628	{
629		log << tcu::TestLog::EndShaderProgram;
630		throw;
631	}
632	log << tcu::TestLog::EndShaderProgram;
633
634	// Write statistics.
635	{
636		static const struct
637		{
638			const char*		name;
639			const char*		description;
640		} s_compileTimeDesc[] =
641		{
642			{ "VertexCompileTime",			"Vertex shader compile time"					},
643			{ "FragmentCompileTime",		"Fragment shader compile time"					},
644			{ "GeometryCompileTime",		"Geometry shader compile time"					},
645			{ "TessControlCompileTime",		"Tesselation control shader compile time"		},
646			{ "TessEvaluationCompileTime",	"Tesselation evaluation shader compile time"	},
647			{ "ComputeCompileTime",			"Compute shader compile time"					},
648			{ "RaygenCompileTime",			"Raygen shader compile time"					},
649			{ "AnyHitCompileTime",			"Any hit shader compile time"					},
650			{ "ClosestHitCompileTime",		"Closest hit shader compile time"				},
651			{ "MissCompileTime",			"Miss shader compile time"						},
652			{ "IntersectionCompileTime",	"Intersection shader compile time"				},
653			{ "CallableCompileTime",		"Callable shader compile time"					},
654			{ "TaskCompileTime",			"Task shader compile time"						},
655			{ "MeshCompileTime",			"Mesh shader compile time"						},
656		};
657		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_compileTimeDesc) == SHADERTYPE_LAST);
658
659		bool allShadersOk = true;
660
661		for (size_t shaderNdx = 0; shaderNdx < numShaders; ++shaderNdx)
662		{
663			const ShaderInfo&	shaderInfo	= *shaderInfos[shaderNdx];
664
665			log << tcu::TestLog::Float(s_compileTimeDesc[shaderInfo.type].name,
666									   s_compileTimeDesc[shaderInfo.type].description,
667									   "ms", QP_KEY_TAG_TIME, (float)shaderInfo.compileTimeUs / 1000.0f);
668
669			allShadersOk = allShadersOk && shaderInfo.compileOk;
670		}
671
672		if (allShadersOk)
673			log << tcu::TestLog::Float("LinkTime", "Link time", "ms", QP_KEY_TAG_TIME, (float)programInfo.linkTimeUs / 1000.0f);
674	}
675}
676
677tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderProgramInfo& shaderProgramInfo)
678{
679	std::vector<const ShaderInfo*>	shaderPtrs	(shaderProgramInfo.shaders.size());
680
681	for (size_t ndx = 0; ndx < shaderPtrs.size(); ndx++)
682		shaderPtrs[ndx] = &shaderProgramInfo.shaders[ndx];
683
684	logShaderProgram(log, shaderProgramInfo.program, shaderPtrs.size(), shaderPtrs.empty() ? DE_NULL : &shaderPtrs[0]);
685
686	return log;
687}
688
689tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderProgram& shaderProgram)
690{
691	std::vector<const ShaderInfo*>	shaderPtrs;
692
693	for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
694	{
695		for (int shaderNdx = 0; shaderNdx < shaderProgram.getNumShaders((ShaderType)shaderType); shaderNdx++)
696			shaderPtrs.push_back(&shaderProgram.getShaderInfo((ShaderType)shaderType, shaderNdx));
697	}
698
699	logShaderProgram(log, shaderProgram.getProgramInfo(), shaderPtrs.size(), shaderPtrs.empty() ? DE_NULL : &shaderPtrs[0]);
700
701	return log;
702}
703
704tcu::TestLog& operator<< (tcu::TestLog& log, const ProgramSources& sources)
705{
706	log << tcu::TestLog::ShaderProgram(false, "(Source only)");
707
708	try
709	{
710		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
711		{
712			for (size_t shaderNdx = 0; shaderNdx < sources.sources[shaderType].size(); shaderNdx++)
713			{
714				log << tcu::TestLog::Shader(getLogShaderType((ShaderType)shaderType),
715											sources.sources[shaderType][shaderNdx],
716											false, "");
717			}
718		}
719	}
720	catch (...)
721	{
722		log << tcu::TestLog::EndShaderProgram;
723		throw;
724	}
725
726	log << tcu::TestLog::EndShaderProgram;
727
728	return log;
729}
730
731} // glu
732