15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*-------------------------------------------------------------------------
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * drawElements Quality Program OpenGL ES Utilities
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * ------------------------------------------------
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright 2014 The Android Open Source Project
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Licensed under the Apache License, Version 2.0 (the "License");
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * you may not use this file except in compliance with the License.
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * You may obtain a copy of the License at
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *      http://www.apache.org/licenses/LICENSE-2.0
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Unless required by applicable law or agreed to in writing, software
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * distributed under the License is distributed on an "AS IS" BASIS,
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * See the License for the specific language governing permissions and
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * limitations under the License.
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *//*!
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * \file
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * \brief Wrapper for GL program object.
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *//*--------------------------------------------------------------------*/
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "gluShaderProgram.hpp"
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "gluRenderContext.hpp"
2653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "glwFunctions.hpp"
27e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "glwEnums.hpp"
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "tcuTestLog.hpp"
29c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)#include "deClock.h"
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
31df95704c49daea886ddad70775bda23618d6274dBen Murdoch#include <cstring>
32df95704c49daea886ddad70775bda23618d6274dBen Murdoch
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using std::string;
347242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace glu
365267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles){
3709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
38521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)// Shader
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
40df95704c49daea886ddad70775bda23618d6274dBen MurdochShader::Shader (const RenderContext& renderCtx, ShaderType shaderType)
418abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)	: m_renderCtx	(renderCtx)
428abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)	, m_shader		(0)
438abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles){
448abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)	const glw::Functions& gl = m_renderCtx.getFunctions();
458abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)
468abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)	m_info.type	= shaderType;
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	m_shader	= gl.createShader(getGLShaderType(shaderType));
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader()");
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	TCU_CHECK(m_shader);
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
521e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)Shader::~Shader (void)
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	const glw::Functions& gl = m_renderCtx.getFunctions();
558abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)	gl.deleteShader(m_shader);
568abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)}
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void Shader::setSources (int numSourceStrings, const char* const* sourceStrings, const int* lengths)
59926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles){
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	const glw::Functions& gl = m_renderCtx.getFunctions();
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	gl.shaderSource(m_shader, numSourceStrings, sourceStrings, lengths);
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource()");
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	m_info.source.clear();
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	for (int ndx = 0; ndx < numSourceStrings; ndx++)
675267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)	{
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		const size_t length = lengths && lengths[ndx] >= 0 ? lengths[ndx] : strlen(sourceStrings[ndx]);
69e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)		m_info.source += std::string(sourceStrings[ndx], length);
70e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)	}
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
7253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
73e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochvoid Shader::compile (void)
7453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
7553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)	const glw::Functions& gl = m_renderCtx.getFunctions();
7653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
778abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)	m_info.compileOk		= false;
78c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)	m_info.compileTimeUs	= 0;
79c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)	m_info.infoLog.clear();
80c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	{
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		deUint64 compileStart = deGetMicroseconds();
831e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)		gl.compileShader(m_shader);
848abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)		m_info.compileTimeUs = deGetMicroseconds() - compileStart;
85c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)	}
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader()");
88
89	// Query status & log.
90	{
91		int	compileStatus	= 0;
92		int	infoLogLen		= 0;
93		int	unusedLen;
94
95		gl.getShaderiv(m_shader, GL_COMPILE_STATUS,		&compileStatus);
96		gl.getShaderiv(m_shader, GL_INFO_LOG_LENGTH,	&infoLogLen);
97		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv()");
98
99		m_info.compileOk = compileStatus != GL_FALSE;
100
101		if (infoLogLen > 0)
102		{
103			std::vector<char> infoLog(infoLogLen);
104			gl.getShaderInfoLog(m_shader, (int)infoLog.size(), &unusedLen, &infoLog[0]);
105			m_info.infoLog = std::string(&infoLog[0], infoLogLen);
106		}
107	}
108}
109
110// Program
111
112static bool getProgramLinkStatus (const RenderContext& renderCtx, deUint32 program)
113{
114	const glw::Functions& gl	= renderCtx.getFunctions();
115	int	linkStatus				= 0;
116
117	gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus);
118	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
119	return (linkStatus != GL_FALSE);
120}
121
122static std::string getProgramInfoLog (const RenderContext& renderCtx, deUint32 program)
123{
124	const glw::Functions& gl = renderCtx.getFunctions();
125
126	int	infoLogLen	= 0;
127	int	unusedLen;
128
129	gl.getProgramiv(program, GL_INFO_LOG_LENGTH,	&infoLogLen);
130	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
131
132	if (infoLogLen > 0)
133	{
134		std::vector<char> infoLog(infoLogLen);
135		gl.getProgramInfoLog(program, (int)infoLog.size(), &unusedLen, &infoLog[0]);
136		return std::string(&infoLog[0], infoLogLen);
137	}
138	return std::string();
139}
140
141Program::Program (const RenderContext& renderCtx)
142	: m_renderCtx	(renderCtx)
143	, m_program		(0)
144{
145	const glw::Functions& gl = m_renderCtx.getFunctions();
146
147	m_program = gl.createProgram();
148	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram()");
149}
150
151Program::Program (const RenderContext& renderCtx, deUint32 program)
152	: m_renderCtx	(renderCtx)
153	, m_program		(program)
154{
155	m_info.linkOk	= getProgramLinkStatus(renderCtx, program);
156	m_info.infoLog	= getProgramInfoLog(renderCtx, program);
157}
158
159Program::~Program (void)
160{
161	const glw::Functions& gl = m_renderCtx.getFunctions();
162	gl.deleteProgram(m_program);
163}
164
165void Program::attachShader (deUint32 shader)
166{
167	const glw::Functions& gl = m_renderCtx.getFunctions();
168
169	gl.attachShader(m_program, shader);
170	GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader()");
171}
172
173void Program::detachShader (deUint32 shader)
174{
175	const glw::Functions& gl = m_renderCtx.getFunctions();
176
177	gl.detachShader(m_program, shader);
178	GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader()");
179}
180
181void Program::bindAttribLocation (deUint32 location, const char* name)
182{
183	const glw::Functions& gl = m_renderCtx.getFunctions();
184
185	gl.bindAttribLocation(m_program, location, name);
186	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindAttribLocation()");
187}
188
189void Program::transformFeedbackVaryings (int count, const char* const* varyings, deUint32 bufferMode)
190{
191	const glw::Functions& gl = m_renderCtx.getFunctions();
192
193	gl.transformFeedbackVaryings(m_program, count, varyings, bufferMode);
194	GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings()");
195}
196
197void Program::link (void)
198{
199	const glw::Functions& gl = m_renderCtx.getFunctions();
200
201	m_info.linkOk		= false;
202	m_info.linkTimeUs	= 0;
203	m_info.infoLog.clear();
204
205	{
206		deUint64 linkStart = deGetMicroseconds();
207		gl.linkProgram(m_program);
208		m_info.linkTimeUs = deGetMicroseconds() - linkStart;
209	}
210	GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram()");
211
212	m_info.linkOk	= getProgramLinkStatus(m_renderCtx, m_program);
213	m_info.infoLog	= getProgramInfoLog(m_renderCtx, m_program);
214}
215
216bool Program::isSeparable (void) const
217{
218	const glw::Functions& gl = m_renderCtx.getFunctions();
219	int separable = GL_FALSE;
220
221	gl.getProgramiv(m_program, GL_PROGRAM_SEPARABLE, &separable);
222	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
223
224	return (separable != GL_FALSE);
225}
226
227void Program::setSeparable (bool separable)
228{
229	const glw::Functions& gl = m_renderCtx.getFunctions();
230
231	gl.programParameteri(m_program, GL_PROGRAM_SEPARABLE, separable);
232	GLU_EXPECT_NO_ERROR(gl.getError(), "glProgramParameteri()");
233}
234
235// ProgramPipeline
236
237ProgramPipeline::ProgramPipeline (const RenderContext& renderCtx)
238	: m_renderCtx	(renderCtx)
239	, m_pipeline	(0)
240{
241	const glw::Functions& gl = m_renderCtx.getFunctions();
242
243	gl.genProgramPipelines(1, &m_pipeline);
244	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenProgramPipelines()");
245}
246
247ProgramPipeline::~ProgramPipeline (void)
248{
249	const glw::Functions& gl = m_renderCtx.getFunctions();
250	gl.deleteProgramPipelines(1, &m_pipeline);
251}
252
253void ProgramPipeline::useProgramStages (deUint32 stages, deUint32 program)
254{
255	const glw::Functions& gl = m_renderCtx.getFunctions();
256
257	gl.useProgramStages(m_pipeline, stages, program);
258	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages()");
259}
260
261void ProgramPipeline::activeShaderProgram (deUint32 program)
262{
263	const glw::Functions& gl = m_renderCtx.getFunctions();
264
265	gl.activeShaderProgram(m_pipeline, program);
266	GLU_EXPECT_NO_ERROR(gl.getError(), "glActiveShaderProgram()");
267}
268
269bool ProgramPipeline::isValid (void)
270{
271	const glw::Functions& gl = m_renderCtx.getFunctions();
272	glw::GLint status = GL_FALSE;
273	gl.validateProgramPipeline(m_pipeline);
274	GLU_EXPECT_NO_ERROR(gl.getError(), "glValidateProgramPipeline()");
275
276	gl.getProgramPipelineiv(m_pipeline, GL_VALIDATE_STATUS, &status);
277
278	return (status != GL_FALSE);
279}
280
281// ShaderProgram
282
283ShaderProgram::ShaderProgram (const RenderContext& renderCtx, const ProgramSources& sources)
284	: m_program(renderCtx)
285{
286	try
287	{
288		bool shadersOk = true;
289
290		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
291		{
292			for (int shaderNdx = 0; shaderNdx < (int)sources.sources[shaderType].size(); ++shaderNdx)
293			{
294				const char* source	= sources.sources[shaderType][shaderNdx].c_str();
295				const int	length	= (int)sources.sources[shaderType][shaderNdx].size();
296
297				m_shaders[shaderType].reserve(m_shaders[shaderType].size() + 1);
298
299				m_shaders[shaderType].push_back(new Shader(renderCtx, ShaderType(shaderType)));
300				m_shaders[shaderType].back()->setSources(1, &source, &length);
301				m_shaders[shaderType].back()->compile();
302
303				shadersOk = shadersOk && m_shaders[shaderType].back()->getCompileStatus();
304			}
305		}
306
307		if (shadersOk)
308		{
309			for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
310				for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
311					m_program.attachShader(m_shaders[shaderType][shaderNdx]->getShader());
312
313			for (std::vector<AttribLocationBinding>::const_iterator binding = sources.attribLocationBindings.begin(); binding != sources.attribLocationBindings.end(); ++binding)
314				m_program.bindAttribLocation(binding->location, binding->name.c_str());
315
316			DE_ASSERT((sources.transformFeedbackBufferMode == GL_NONE) == sources.transformFeedbackVaryings.empty());
317			if (sources.transformFeedbackBufferMode != GL_NONE)
318			{
319				std::vector<const char*> tfVaryings(sources.transformFeedbackVaryings.size());
320				for (int ndx = 0; ndx < (int)tfVaryings.size(); ndx++)
321					tfVaryings[ndx] = sources.transformFeedbackVaryings[ndx].c_str();
322
323				m_program.transformFeedbackVaryings((int)tfVaryings.size(), &tfVaryings[0], sources.transformFeedbackBufferMode);
324			}
325
326			if (sources.separable)
327				m_program.setSeparable(true);
328
329			m_program.link();
330		}
331	}
332	catch (...)
333	{
334		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
335			for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
336				delete m_shaders[shaderType][shaderNdx];
337		throw;
338	}
339}
340
341ShaderProgram::~ShaderProgram (void)
342{
343	for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
344		for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
345			delete m_shaders[shaderType][shaderNdx];
346}
347
348// Utilities
349
350deUint32 getGLShaderType (ShaderType shaderType)
351{
352	static const deUint32 s_typeMap[] =
353	{
354		GL_VERTEX_SHADER,
355		GL_FRAGMENT_SHADER,
356		GL_GEOMETRY_SHADER,
357		GL_TESS_CONTROL_SHADER,
358		GL_TESS_EVALUATION_SHADER,
359		GL_COMPUTE_SHADER
360	};
361	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST);
362	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap)));
363	return s_typeMap[shaderType];
364}
365
366deUint32 getGLShaderTypeBit (ShaderType shaderType)
367{
368	static const deUint32 s_typebitMap[] =
369	{
370		GL_VERTEX_SHADER_BIT,
371		GL_FRAGMENT_SHADER_BIT,
372		GL_GEOMETRY_SHADER_BIT,
373		GL_TESS_CONTROL_SHADER_BIT,
374		GL_TESS_EVALUATION_SHADER_BIT,
375		GL_COMPUTE_SHADER_BIT
376	};
377	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typebitMap) == SHADERTYPE_LAST);
378	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typebitMap)));
379	return s_typebitMap[shaderType];
380}
381
382qpShaderType getLogShaderType (ShaderType shaderType)
383{
384	static const qpShaderType s_typeMap[] =
385	{
386		QP_SHADER_TYPE_VERTEX,
387		QP_SHADER_TYPE_FRAGMENT,
388		QP_SHADER_TYPE_GEOMETRY,
389		QP_SHADER_TYPE_TESS_CONTROL,
390		QP_SHADER_TYPE_TESS_EVALUATION,
391		QP_SHADER_TYPE_COMPUTE
392	};
393	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST);
394	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap)));
395	return s_typeMap[shaderType];
396}
397
398static tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderInfo& shaderInfo)
399{
400	return log << tcu::TestLog::Shader(getLogShaderType(shaderInfo.type), shaderInfo.source, shaderInfo.compileOk, shaderInfo.infoLog);
401}
402
403tcu::TestLog& operator<< (tcu::TestLog& log, const Shader& shader)
404{
405	return log << tcu::TestLog::ShaderProgram(false, "Plain shader") << shader.getInfo() << tcu::TestLog::EndShaderProgram;
406}
407
408tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderProgram& program)
409{
410	const ProgramInfo& progInfo = program.getProgramInfo();
411
412	log << tcu::TestLog::ShaderProgram(progInfo.linkOk, progInfo.infoLog);
413	try
414	{
415		for (int shaderTypeNdx = 0; shaderTypeNdx < SHADERTYPE_LAST; shaderTypeNdx++)
416		{
417			const glu::ShaderType shaderType = (glu::ShaderType)shaderTypeNdx;
418
419			for (int shaderNdx = 0; shaderNdx < program.getNumShaders(shaderType); ++shaderNdx)
420				log << program.getShaderInfo(shaderType, shaderNdx);
421		}
422	}
423	catch (...)
424	{
425		log << tcu::TestLog::EndShaderProgram;
426		throw;
427	}
428	log << tcu::TestLog::EndShaderProgram;
429
430	// Write statistics.
431	{
432		static const struct
433		{
434			const char*		name;
435			const char*		description;
436		} s_compileTimeDesc[] =
437		{
438			{ "VertexCompileTime",			"Vertex shader compile time"					},
439			{ "FragmentCompileTime",		"Fragment shader compile time"					},
440			{ "GeometryCompileTime",		"Geometry shader compile time"					},
441			{ "TessControlCompileTime",		"Tesselation control shader compile time"		},
442			{ "TessEvaluationCompileTime",	"Tesselation evaluation shader compile time"	},
443			{ "ComputeCompileTime",			"Compute shader compile time"					},
444		};
445		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_compileTimeDesc) == SHADERTYPE_LAST);
446
447		bool allShadersOk = true;
448
449		for (int shaderTypeNdx = 0; shaderTypeNdx < SHADERTYPE_LAST; shaderTypeNdx++)
450		{
451			const glu::ShaderType shaderType = (glu::ShaderType)shaderTypeNdx;
452
453			for (int shaderNdx = 0; shaderNdx < program.getNumShaders(shaderType); ++shaderNdx)
454			{
455				const ShaderInfo& shaderInfo = program.getShaderInfo(shaderType, shaderNdx);
456				log << tcu::TestLog::Float(s_compileTimeDesc[shaderType].name, s_compileTimeDesc[shaderType].description, "ms", QP_KEY_TAG_TIME, (float)shaderInfo.compileTimeUs / 1000.0f);
457				allShadersOk = allShadersOk && shaderInfo.compileOk;
458			}
459		}
460
461		if (allShadersOk)
462			log << tcu::TestLog::Float("LinkTime", "Link time", "ms", QP_KEY_TAG_TIME, (float)progInfo.linkTimeUs / 1000.0f);
463	}
464
465	return log;
466}
467
468} // glu
469