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
135// Program
136
137static 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
146static 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
175Program::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
183Program::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
191Program::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
199Program::~Program (void)
200{
201	m_gl.deleteProgram(m_program);
202}
203
204void Program::attachShader (deUint32 shader)
205{
206	m_gl.attachShader(m_program, shader);
207	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glAttachShader()");
208}
209
210void Program::detachShader (deUint32 shader)
211{
212	m_gl.detachShader(m_program, shader);
213	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDetachShader()");
214}
215
216void 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
222void 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
228void 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
245bool 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
255void 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
263ProgramPipeline::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
271ProgramPipeline::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
279ProgramPipeline::~ProgramPipeline (void)
280{
281	m_gl.deleteProgramPipelines(1, &m_pipeline);
282}
283
284void 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
290void ProgramPipeline::activeShaderProgram (deUint32 program)
291{
292	m_gl.activeShaderProgram(m_pipeline, program);
293	GLU_EXPECT_NO_ERROR(m_gl.getError(), "glActiveShaderProgram()");
294}
295
296bool 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
309ShaderProgram::ShaderProgram (const RenderContext& renderCtx, const ProgramSources& sources)
310	: m_program(renderCtx.getFunctions())
311{
312	init(renderCtx.getFunctions(), sources);
313}
314
315ShaderProgram::ShaderProgram (const glw::Functions& gl, const ProgramSources& sources)
316	: m_program(gl)
317{
318	init(gl, sources);
319}
320
321void 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
378ShaderProgram::~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
387deUint32 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
403deUint32 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
419qpShaderType 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
435tcu::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
440tcu::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
445static 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
495tcu::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
507tcu::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
522tcu::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