Home | History | Annotate | Download | only in opengl
      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 
     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 
     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 
     60 Shader::~Shader (void)
     61 {
     62 	m_gl.deleteShader(m_shader);
     63 }
     64 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    199 Program::~Program (void)
    200 {
    201 	m_gl.deleteProgram(m_program);
    202 }
    203 
    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 
    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 
    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 
    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 
    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 
    245 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 
    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 
    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 
    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 
    279 ProgramPipeline::~ProgramPipeline (void)
    280 {
    281 	m_gl.deleteProgramPipelines(1, &m_pipeline);
    282 }
    283 
    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 
    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 
    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 
    309 ShaderProgram::ShaderProgram (const RenderContext& renderCtx, const ProgramSources& sources)
    310 	: m_program(renderCtx.getFunctions())
    311 {
    312 	init(renderCtx.getFunctions(), sources);
    313 }
    314 
    315 ShaderProgram::ShaderProgram (const glw::Functions& gl, const ProgramSources& sources)
    316 	: m_program(gl)
    317 {
    318 	init(gl, sources);
    319 }
    320 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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