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