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