• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 using std::string;
34 
35 namespace glu
36 {
37 
38 // Shader
39 
Shader(const RenderContext & renderCtx,ShaderType shaderType)40 Shader::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 
Shader(const glw::Functions & gl,ShaderType shaderType)50 Shader::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 
~Shader(void)60 Shader::~Shader (void)
61 {
62 	m_gl.deleteShader(m_shader);
63 }
64 
setSources(int numSourceStrings,const char * const * sourceStrings,const int * lengths)65 void 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 
compile(void)78 void 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 
135 // Program
136 
getProgramLinkStatus(const glw::Functions & gl,deUint32 program)137 static bool getProgramLinkStatus (const glw::Functions& gl, deUint32 program)
138 {
139 	int	linkStatus				= 0;
140 
141 	gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus);
142 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
143 	return (linkStatus != GL_FALSE);
144 }
145 
getProgramInfoLog(const glw::Functions & gl,deUint32 program)146 static std::string getProgramInfoLog (const glw::Functions& gl, deUint32 program)
147 {
148 	int infoLogLen = 0;
149 	int unusedLen;
150 
151 	gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
152 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv()");
153 
154 	if (infoLogLen > 0)
155 	{
156 		// The INFO_LOG_LENGTH query and the buffer query implementations have
157 		// very commonly off-by-one errors. Try to work around these issues.
158 
159 		// add tolerance for off-by-one in log length, buffer write, and for terminator
160 		std::vector<char> infoLog(infoLogLen + 3, '\0');
161 
162 		// claim buf size is one smaller to protect from off-by-one writing over buffer bounds
163 		gl.getProgramInfoLog(program, (int)infoLog.size() - 1, &unusedLen, &infoLog[0]);
164 
165 		// return whole buffer if null terminator was overwritten
166 		if (infoLog[(int)(infoLog.size()) - 1] != '\0')
167 			return std::string(&infoLog[0], infoLog.size());
168 
169 		// read as C string. infoLog is guaranteed to be 0-terminated
170 		return std::string(&infoLog[0]);
171 	}
172 	return std::string();
173 }
174 
Program(const RenderContext & renderCtx)175 Program::Program (const RenderContext& renderCtx)
176 	: m_gl		(renderCtx.getFunctions())
177 	, m_program	(0)
178 {
179 	m_program = m_gl.createProgram();
180 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateProgram()");
181 }
182 
Program(const glw::Functions & gl)183 Program::Program (const glw::Functions& gl)
184 	: m_gl		(gl)
185 	, m_program	(0)
186 {
187 	m_program = m_gl.createProgram();
188 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateProgram()");
189 }
190 
Program(const RenderContext & renderCtx,deUint32 program)191 Program::Program (const RenderContext& renderCtx, deUint32 program)
192 	: m_gl		(renderCtx.getFunctions())
193 	, m_program	(program)
194 {
195 	m_info.linkOk	= getProgramLinkStatus(m_gl, program);
196 	m_info.infoLog	= getProgramInfoLog(m_gl, program);
197 }
198 
~Program(void)199 Program::~Program (void)
200 {
201 	m_gl.deleteProgram(m_program);
202 }
203 
attachShader(deUint32 shader)204 void Program::attachShader (deUint32 shader)
205 {
206 	m_gl.attachShader(m_program, shader);
207 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glAttachShader()");
208 }
209 
detachShader(deUint32 shader)210 void Program::detachShader (deUint32 shader)
211 {
212 	m_gl.detachShader(m_program, shader);
213 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDetachShader()");
214 }
215 
bindAttribLocation(deUint32 location,const char * name)216 void Program::bindAttribLocation (deUint32 location, const char* name)
217 {
218 	m_gl.bindAttribLocation(m_program, location, name);
219 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindAttribLocation()");
220 }
221 
transformFeedbackVaryings(int count,const char * const * varyings,deUint32 bufferMode)222 void Program::transformFeedbackVaryings (int count, const char* const* varyings, deUint32 bufferMode)
223 {
224 	m_gl.transformFeedbackVaryings(m_program, count, varyings, bufferMode);
225 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTransformFeedbackVaryings()");
226 }
227 
link(void)228 void Program::link (void)
229 {
230 	m_info.linkOk		= false;
231 	m_info.linkTimeUs	= 0;
232 	m_info.infoLog.clear();
233 
234 	{
235 		deUint64 linkStart = deGetMicroseconds();
236 		m_gl.linkProgram(m_program);
237 		m_info.linkTimeUs = deGetMicroseconds() - linkStart;
238 	}
239 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glLinkProgram()");
240 
241 	m_info.linkOk	= getProgramLinkStatus(m_gl, m_program);
242 	m_info.infoLog	= getProgramInfoLog(m_gl, m_program);
243 }
244 
isSeparable(void) const245 bool Program::isSeparable (void) const
246 {
247 	int separable = GL_FALSE;
248 
249 	m_gl.getProgramiv(m_program, GL_PROGRAM_SEPARABLE, &separable);
250 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetProgramiv()");
251 
252 	return (separable != GL_FALSE);
253 }
254 
setSeparable(bool separable)255 void Program::setSeparable (bool separable)
256 {
257 	m_gl.programParameteri(m_program, GL_PROGRAM_SEPARABLE, separable);
258 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glProgramParameteri()");
259 }
260 
261 // ProgramPipeline
262 
ProgramPipeline(const RenderContext & renderCtx)263 ProgramPipeline::ProgramPipeline (const RenderContext& renderCtx)
264 	: m_gl			(renderCtx.getFunctions())
265 	, m_pipeline	(0)
266 {
267 	m_gl.genProgramPipelines(1, &m_pipeline);
268 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenProgramPipelines()");
269 }
270 
ProgramPipeline(const glw::Functions & gl)271 ProgramPipeline::ProgramPipeline (const glw::Functions& gl)
272 	: m_gl			(gl)
273 	, m_pipeline	(0)
274 {
275 	m_gl.genProgramPipelines(1, &m_pipeline);
276 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenProgramPipelines()");
277 }
278 
~ProgramPipeline(void)279 ProgramPipeline::~ProgramPipeline (void)
280 {
281 	m_gl.deleteProgramPipelines(1, &m_pipeline);
282 }
283 
useProgramStages(deUint32 stages,deUint32 program)284 void ProgramPipeline::useProgramStages (deUint32 stages, deUint32 program)
285 {
286 	m_gl.useProgramStages(m_pipeline, stages, program);
287 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgramStages()");
288 }
289 
activeShaderProgram(deUint32 program)290 void ProgramPipeline::activeShaderProgram (deUint32 program)
291 {
292 	m_gl.activeShaderProgram(m_pipeline, program);
293 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glActiveShaderProgram()");
294 }
295 
isValid(void)296 bool ProgramPipeline::isValid (void)
297 {
298 	glw::GLint status = GL_FALSE;
299 	m_gl.validateProgramPipeline(m_pipeline);
300 	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glValidateProgramPipeline()");
301 
302 	m_gl.getProgramPipelineiv(m_pipeline, GL_VALIDATE_STATUS, &status);
303 
304 	return (status != GL_FALSE);
305 }
306 
307 // ShaderProgram
308 
ShaderProgram(const RenderContext & renderCtx,const ProgramSources & sources)309 ShaderProgram::ShaderProgram (const RenderContext& renderCtx, const ProgramSources& sources)
310 	: m_program(renderCtx.getFunctions())
311 {
312 	init(renderCtx.getFunctions(), sources);
313 }
314 
ShaderProgram(const glw::Functions & gl,const ProgramSources & sources)315 ShaderProgram::ShaderProgram (const glw::Functions& gl, const ProgramSources& sources)
316 	: m_program(gl)
317 {
318 	init(gl, sources);
319 }
320 
init(const glw::Functions & gl,const ProgramSources & sources)321 void ShaderProgram::init (const glw::Functions& gl, const ProgramSources& sources)
322 {
323 	try
324 	{
325 		bool shadersOk = true;
326 
327 		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
328 		{
329 			for (int shaderNdx = 0; shaderNdx < (int)sources.sources[shaderType].size(); ++shaderNdx)
330 			{
331 				const char* source	= sources.sources[shaderType][shaderNdx].c_str();
332 				const int	length	= (int)sources.sources[shaderType][shaderNdx].size();
333 
334 				m_shaders[shaderType].reserve(m_shaders[shaderType].size() + 1);
335 
336 				m_shaders[shaderType].push_back(new Shader(gl, ShaderType(shaderType)));
337 				m_shaders[shaderType].back()->setSources(1, &source, &length);
338 				m_shaders[shaderType].back()->compile();
339 
340 				shadersOk = shadersOk && m_shaders[shaderType].back()->getCompileStatus();
341 			}
342 		}
343 
344 		if (shadersOk)
345 		{
346 			for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
347 				for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
348 					m_program.attachShader(m_shaders[shaderType][shaderNdx]->getShader());
349 
350 			for (std::vector<AttribLocationBinding>::const_iterator binding = sources.attribLocationBindings.begin(); binding != sources.attribLocationBindings.end(); ++binding)
351 				m_program.bindAttribLocation(binding->location, binding->name.c_str());
352 
353 			DE_ASSERT((sources.transformFeedbackBufferMode == GL_NONE) == sources.transformFeedbackVaryings.empty());
354 			if (sources.transformFeedbackBufferMode != GL_NONE)
355 			{
356 				std::vector<const char*> tfVaryings(sources.transformFeedbackVaryings.size());
357 				for (int ndx = 0; ndx < (int)tfVaryings.size(); ndx++)
358 					tfVaryings[ndx] = sources.transformFeedbackVaryings[ndx].c_str();
359 
360 				m_program.transformFeedbackVaryings((int)tfVaryings.size(), &tfVaryings[0], sources.transformFeedbackBufferMode);
361 			}
362 
363 			if (sources.separable)
364 				m_program.setSeparable(true);
365 
366 			m_program.link();
367 		}
368 	}
369 	catch (...)
370 	{
371 		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
372 			for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
373 				delete m_shaders[shaderType][shaderNdx];
374 		throw;
375 	}
376 }
377 
~ShaderProgram(void)378 ShaderProgram::~ShaderProgram (void)
379 {
380 	for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
381 		for (int shaderNdx = 0; shaderNdx < (int)m_shaders[shaderType].size(); ++shaderNdx)
382 			delete m_shaders[shaderType][shaderNdx];
383 }
384 
385 // Utilities
386 
getGLShaderType(ShaderType shaderType)387 deUint32 getGLShaderType (ShaderType shaderType)
388 {
389 	static const deUint32 s_typeMap[] =
390 	{
391 		GL_VERTEX_SHADER,
392 		GL_FRAGMENT_SHADER,
393 		GL_GEOMETRY_SHADER,
394 		GL_TESS_CONTROL_SHADER,
395 		GL_TESS_EVALUATION_SHADER,
396 		GL_COMPUTE_SHADER
397 	};
398 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST);
399 	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap)));
400 	return s_typeMap[shaderType];
401 }
402 
getGLShaderTypeBit(ShaderType shaderType)403 deUint32 getGLShaderTypeBit (ShaderType shaderType)
404 {
405 	static const deUint32 s_typebitMap[] =
406 	{
407 		GL_VERTEX_SHADER_BIT,
408 		GL_FRAGMENT_SHADER_BIT,
409 		GL_GEOMETRY_SHADER_BIT,
410 		GL_TESS_CONTROL_SHADER_BIT,
411 		GL_TESS_EVALUATION_SHADER_BIT,
412 		GL_COMPUTE_SHADER_BIT
413 	};
414 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typebitMap) == SHADERTYPE_LAST);
415 	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typebitMap)));
416 	return s_typebitMap[shaderType];
417 }
418 
getLogShaderType(ShaderType shaderType)419 qpShaderType getLogShaderType (ShaderType shaderType)
420 {
421 	static const qpShaderType s_typeMap[] =
422 	{
423 		QP_SHADER_TYPE_VERTEX,
424 		QP_SHADER_TYPE_FRAGMENT,
425 		QP_SHADER_TYPE_GEOMETRY,
426 		QP_SHADER_TYPE_TESS_CONTROL,
427 		QP_SHADER_TYPE_TESS_EVALUATION,
428 		QP_SHADER_TYPE_COMPUTE
429 	};
430 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_typeMap) == SHADERTYPE_LAST);
431 	DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_typeMap)));
432 	return s_typeMap[shaderType];
433 }
434 
operator <<(tcu::TestLog & log,const ShaderInfo & shaderInfo)435 tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderInfo& shaderInfo)
436 {
437 	return log << tcu::TestLog::Shader(getLogShaderType(shaderInfo.type), shaderInfo.source, shaderInfo.compileOk, shaderInfo.infoLog);
438 }
439 
operator <<(tcu::TestLog & log,const Shader & shader)440 tcu::TestLog& operator<< (tcu::TestLog& log, const Shader& shader)
441 {
442 	return log << tcu::TestLog::ShaderProgram(false, "Plain shader") << shader.getInfo() << tcu::TestLog::EndShaderProgram;
443 }
444 
logShaderProgram(tcu::TestLog & log,const ProgramInfo & programInfo,size_t numShaders,const ShaderInfo * const * shaderInfos)445 static void logShaderProgram (tcu::TestLog& log, const ProgramInfo& programInfo, size_t numShaders, const ShaderInfo* const* shaderInfos)
446 {
447 	log << tcu::TestLog::ShaderProgram(programInfo.linkOk, programInfo.infoLog);
448 	try
449 	{
450 		for (size_t shaderNdx = 0; shaderNdx < numShaders; ++shaderNdx)
451 			log << *shaderInfos[shaderNdx];
452 	}
453 	catch (...)
454 	{
455 		log << tcu::TestLog::EndShaderProgram;
456 		throw;
457 	}
458 	log << tcu::TestLog::EndShaderProgram;
459 
460 	// Write statistics.
461 	{
462 		static const struct
463 		{
464 			const char*		name;
465 			const char*		description;
466 		} s_compileTimeDesc[] =
467 		{
468 			{ "VertexCompileTime",			"Vertex shader compile time"					},
469 			{ "FragmentCompileTime",		"Fragment shader compile time"					},
470 			{ "GeometryCompileTime",		"Geometry shader compile time"					},
471 			{ "TessControlCompileTime",		"Tesselation control shader compile time"		},
472 			{ "TessEvaluationCompileTime",	"Tesselation evaluation shader compile time"	},
473 			{ "ComputeCompileTime",			"Compute shader compile time"					},
474 		};
475 		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_compileTimeDesc) == SHADERTYPE_LAST);
476 
477 		bool allShadersOk = true;
478 
479 		for (size_t shaderNdx = 0; shaderNdx < numShaders; ++shaderNdx)
480 		{
481 			const ShaderInfo&	shaderInfo	= *shaderInfos[shaderNdx];
482 
483 			log << tcu::TestLog::Float(s_compileTimeDesc[shaderInfo.type].name,
484 									   s_compileTimeDesc[shaderInfo.type].description,
485 									   "ms", QP_KEY_TAG_TIME, (float)shaderInfo.compileTimeUs / 1000.0f);
486 
487 			allShadersOk = allShadersOk && shaderInfo.compileOk;
488 		}
489 
490 		if (allShadersOk)
491 			log << tcu::TestLog::Float("LinkTime", "Link time", "ms", QP_KEY_TAG_TIME, (float)programInfo.linkTimeUs / 1000.0f);
492 	}
493 }
494 
operator <<(tcu::TestLog & log,const ShaderProgramInfo & shaderProgramInfo)495 tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderProgramInfo& shaderProgramInfo)
496 {
497 	std::vector<const ShaderInfo*>	shaderPtrs	(shaderProgramInfo.shaders.size());
498 
499 	for (size_t ndx = 0; ndx < shaderPtrs.size(); ndx++)
500 		shaderPtrs[ndx] = &shaderProgramInfo.shaders[ndx];
501 
502 	logShaderProgram(log, shaderProgramInfo.program, shaderPtrs.size(), shaderPtrs.empty() ? DE_NULL : &shaderPtrs[0]);
503 
504 	return log;
505 }
506 
operator <<(tcu::TestLog & log,const ShaderProgram & shaderProgram)507 tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderProgram& shaderProgram)
508 {
509 	std::vector<const ShaderInfo*>	shaderPtrs;
510 
511 	for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
512 	{
513 		for (int shaderNdx = 0; shaderNdx < shaderProgram.getNumShaders((ShaderType)shaderType); shaderNdx++)
514 			shaderPtrs.push_back(&shaderProgram.getShaderInfo((ShaderType)shaderType, shaderNdx));
515 	}
516 
517 	logShaderProgram(log, shaderProgram.getProgramInfo(), shaderPtrs.size(), shaderPtrs.empty() ? DE_NULL : &shaderPtrs[0]);
518 
519 	return log;
520 }
521 
operator <<(tcu::TestLog & log,const ProgramSources & sources)522 tcu::TestLog& operator<< (tcu::TestLog& log, const ProgramSources& sources)
523 {
524 	log << tcu::TestLog::ShaderProgram(false, "(Source only)");
525 
526 	try
527 	{
528 		for (int shaderType = 0; shaderType < SHADERTYPE_LAST; shaderType++)
529 		{
530 			for (size_t shaderNdx = 0; shaderNdx < sources.sources[shaderType].size(); shaderNdx++)
531 			{
532 				log << tcu::TestLog::Shader(getLogShaderType((ShaderType)shaderType),
533 											sources.sources[shaderType][shaderNdx],
534 											false, "");
535 			}
536 		}
537 	}
538 	catch (...)
539 	{
540 		log << tcu::TestLog::EndShaderProgram;
541 		throw;
542 	}
543 
544 	log << tcu::TestLog::EndShaderProgram;
545 
546 	return log;
547 }
548 
549 } // glu
550