1 //
2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "util/shader_utils.h"
8
9 #include <cstring>
10 #include <fstream>
11 #include <iostream>
12 #include <vector>
13
14 #include "common/utilities.h"
15 #include "util/test_utils.h"
16
17 namespace
18 {
ReadEntireFile(const std::string & filePath,std::string * contentsOut)19 bool ReadEntireFile(const std::string &filePath, std::string *contentsOut)
20 {
21 constexpr uint32_t kMaxBufferSize = 2000;
22 char buffer[kMaxBufferSize] = {};
23 if (!angle::ReadEntireFileToString(filePath.c_str(), buffer, kMaxBufferSize) ||
24 strlen(buffer) == 0)
25 return false;
26 *contentsOut = buffer;
27 return true;
28 }
29
CompileProgramInternal(const char * vsSource,const char * tcsSource,const char * tesSource,const char * gsSource,const char * fsSource,const std::function<void (GLuint)> & preLinkCallback)30 GLuint CompileProgramInternal(const char *vsSource,
31 const char *tcsSource,
32 const char *tesSource,
33 const char *gsSource,
34 const char *fsSource,
35 const std::function<void(GLuint)> &preLinkCallback)
36 {
37 GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
38 GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
39
40 if (vs == 0 || fs == 0)
41 {
42 glDeleteShader(fs);
43 glDeleteShader(vs);
44 return 0;
45 }
46
47 GLuint program = glCreateProgram();
48
49 glAttachShader(program, vs);
50 glDeleteShader(vs);
51
52 glAttachShader(program, fs);
53 glDeleteShader(fs);
54
55 GLuint tcs = 0;
56 GLuint tes = 0;
57 GLuint gs = 0;
58
59 if (strlen(tcsSource) > 0)
60 {
61 tcs = CompileShader(GL_TESS_CONTROL_SHADER_EXT, tcsSource);
62 if (tcs == 0)
63 {
64 glDeleteShader(vs);
65 glDeleteShader(fs);
66 glDeleteProgram(program);
67 return 0;
68 }
69
70 glAttachShader(program, tcs);
71 glDeleteShader(tcs);
72 }
73
74 if (strlen(tesSource) > 0)
75 {
76 tes = CompileShader(GL_TESS_EVALUATION_SHADER_EXT, tesSource);
77 if (tes == 0)
78 {
79 glDeleteShader(vs);
80 glDeleteShader(fs);
81 glDeleteShader(tcs);
82 glDeleteProgram(program);
83 return 0;
84 }
85
86 glAttachShader(program, tes);
87 glDeleteShader(tes);
88 }
89
90 if (strlen(gsSource) > 0)
91 {
92 gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
93 if (gs == 0)
94 {
95 glDeleteShader(vs);
96 glDeleteShader(fs);
97 glDeleteShader(tcs);
98 glDeleteShader(tes);
99 glDeleteProgram(program);
100 return 0;
101 }
102
103 glAttachShader(program, gs);
104 glDeleteShader(gs);
105 }
106
107 if (preLinkCallback)
108 {
109 preLinkCallback(program);
110 }
111
112 glLinkProgram(program);
113
114 return CheckLinkStatusAndReturnProgram(program, true);
115 }
116
117 const void *gCallbackChainUserParam;
118
DebugMessageCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)119 void KHRONOS_APIENTRY DebugMessageCallback(GLenum source,
120 GLenum type,
121 GLuint id,
122 GLenum severity,
123 GLsizei length,
124 const GLchar *message,
125 const void *userParam)
126 {
127 std::string sourceText = gl::GetDebugMessageSourceString(source);
128 std::string typeText = gl::GetDebugMessageTypeString(type);
129 std::string severityText = gl::GetDebugMessageSeverityString(severity);
130 std::cerr << sourceText << ", " << typeText << ", " << severityText << ": " << message << "\n";
131
132 GLDEBUGPROC callbackChain = reinterpret_cast<GLDEBUGPROC>(const_cast<void *>(userParam));
133 if (callbackChain)
134 {
135 callbackChain(source, type, id, severity, length, message, gCallbackChainUserParam);
136 }
137 }
138 } // namespace
139
CompileShader(GLenum type,const char * source)140 GLuint CompileShader(GLenum type, const char *source)
141 {
142 GLuint shader = glCreateShader(type);
143
144 const char *sourceArray[1] = {source};
145 glShaderSource(shader, 1, sourceArray, nullptr);
146 glCompileShader(shader);
147
148 GLint compileResult;
149 glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
150
151 if (compileResult == 0)
152 {
153 GLint infoLogLength;
154 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
155
156 // Info log length includes the null terminator, so 1 means that the info log is an empty
157 // string.
158 if (infoLogLength > 1)
159 {
160 std::vector<GLchar> infoLog(infoLogLength);
161 glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
162 std::cerr << "shader compilation failed: " << &infoLog[0];
163 }
164 else
165 {
166 std::cerr << "shader compilation failed. <Empty log message>";
167 }
168
169 std::cerr << std::endl;
170
171 glDeleteShader(shader);
172 shader = 0;
173 }
174
175 return shader;
176 }
177
CompileShaderFromFile(GLenum type,const std::string & sourcePath)178 GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
179 {
180 std::string source;
181 if (!ReadEntireFile(sourcePath, &source))
182 {
183 std::cerr << "Error reading shader file: " << sourcePath << "\n";
184 return 0;
185 }
186
187 return CompileShader(type, source.c_str());
188 }
189
CheckLinkStatusAndReturnProgram(GLuint program,bool outputErrorMessages)190 GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
191 {
192 if (glGetError() != GL_NO_ERROR)
193 return 0;
194
195 GLint linkStatus;
196 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
197 if (linkStatus == 0)
198 {
199 if (outputErrorMessages)
200 {
201 GLint infoLogLength;
202 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
203
204 // Info log length includes the null terminator, so 1 means that the info log is an
205 // empty string.
206 if (infoLogLength > 1)
207 {
208 std::vector<GLchar> infoLog(infoLogLength);
209 glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
210 &infoLog[0]);
211
212 std::cerr << "program link failed: " << &infoLog[0];
213 }
214 else
215 {
216 std::cerr << "program link failed. <Empty log message>";
217 }
218 }
219
220 glDeleteProgram(program);
221 return 0;
222 }
223
224 return program;
225 }
226
GetProgramShader(GLuint program,GLint requestedType)227 GLuint GetProgramShader(GLuint program, GLint requestedType)
228 {
229 static constexpr GLsizei kMaxShaderCount = 16;
230 GLuint attachedShaders[kMaxShaderCount] = {0u};
231 GLsizei count = 0;
232 glGetAttachedShaders(program, kMaxShaderCount, &count, attachedShaders);
233 for (int i = 0; i < count; ++i)
234 {
235 GLint type = 0;
236 glGetShaderiv(attachedShaders[i], GL_SHADER_TYPE, &type);
237 if (type == requestedType)
238 {
239 return attachedShaders[i];
240 }
241 }
242
243 return 0;
244 }
245
CompileProgramWithTransformFeedback(const char * vsSource,const char * fsSource,const std::vector<std::string> & transformFeedbackVaryings,GLenum bufferMode)246 GLuint CompileProgramWithTransformFeedback(
247 const char *vsSource,
248 const char *fsSource,
249 const std::vector<std::string> &transformFeedbackVaryings,
250 GLenum bufferMode)
251 {
252 auto preLink = [&](GLuint program) {
253 if (transformFeedbackVaryings.size() > 0)
254 {
255 std::vector<const char *> constCharTFVaryings;
256
257 for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
258 {
259 constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
260 }
261
262 glTransformFeedbackVaryings(program,
263 static_cast<GLsizei>(transformFeedbackVaryings.size()),
264 &constCharTFVaryings[0], bufferMode);
265 }
266 };
267
268 return CompileProgramInternal(vsSource, "", "", "", fsSource, preLink);
269 }
270
CompileProgram(const char * vsSource,const char * fsSource)271 GLuint CompileProgram(const char *vsSource, const char *fsSource)
272 {
273 return CompileProgramInternal(vsSource, "", "", "", fsSource, nullptr);
274 }
275
CompileProgram(const char * vsSource,const char * fsSource,const std::function<void (GLuint)> & preLinkCallback)276 GLuint CompileProgram(const char *vsSource,
277 const char *fsSource,
278 const std::function<void(GLuint)> &preLinkCallback)
279 {
280 return CompileProgramInternal(vsSource, "", "", "", fsSource, preLinkCallback);
281 }
282
CompileProgramWithGS(const char * vsSource,const char * gsSource,const char * fsSource)283 GLuint CompileProgramWithGS(const char *vsSource, const char *gsSource, const char *fsSource)
284 {
285 return CompileProgramInternal(vsSource, "", "", gsSource, fsSource, nullptr);
286 }
287
CompileProgramWithTESS(const char * vsSource,const char * tcsSource,const char * tesSource,const char * fsSource)288 GLuint CompileProgramWithTESS(const char *vsSource,
289 const char *tcsSource,
290 const char *tesSource,
291 const char *fsSource)
292 {
293 return CompileProgramInternal(vsSource, tcsSource, tesSource, "", fsSource, nullptr);
294 }
295
CompileProgramFromFiles(const std::string & vsPath,const std::string & fsPath)296 GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)
297 {
298 std::string vsSource;
299 if (!ReadEntireFile(vsPath, &vsSource))
300 {
301 std::cerr << "Error reading shader: " << vsPath << "\n";
302 return 0;
303 }
304
305 std::string fsSource;
306 if (!ReadEntireFile(fsPath, &fsSource))
307 {
308 std::cerr << "Error reading shader: " << fsPath << "\n";
309 return 0;
310 }
311
312 return CompileProgram(vsSource.c_str(), fsSource.c_str());
313 }
314
CompileComputeProgram(const char * csSource,bool outputErrorMessages)315 GLuint CompileComputeProgram(const char *csSource, bool outputErrorMessages)
316 {
317 GLuint program = glCreateProgram();
318
319 GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
320 if (cs == 0)
321 {
322 glDeleteProgram(program);
323 return 0;
324 }
325
326 glAttachShader(program, cs);
327
328 glLinkProgram(program);
329
330 return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
331 }
332
LoadBinaryProgramOES(const std::vector<uint8_t> & binary,GLenum binaryFormat)333 GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
334 {
335 GLuint program = glCreateProgram();
336 glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
337 return CheckLinkStatusAndReturnProgram(program, true);
338 }
339
LoadBinaryProgramES3(const std::vector<uint8_t> & binary,GLenum binaryFormat)340 GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
341 {
342 GLuint program = glCreateProgram();
343 glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
344 return CheckLinkStatusAndReturnProgram(program, true);
345 }
346
LinkAttachedProgram(GLuint program)347 bool LinkAttachedProgram(GLuint program)
348 {
349 glLinkProgram(program);
350 return (CheckLinkStatusAndReturnProgram(program, true) != 0);
351 }
352
EnableDebugCallback(GLDEBUGPROC callbackChain,const void * userParam)353 void EnableDebugCallback(GLDEBUGPROC callbackChain, const void *userParam)
354 {
355 gCallbackChainUserParam = userParam;
356
357 glEnable(GL_DEBUG_OUTPUT);
358 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
359 // Enable medium and high priority messages.
360 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr,
361 GL_TRUE);
362 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr,
363 GL_TRUE);
364 // Disable low and notification priority messages.
365 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr,
366 GL_FALSE);
367 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr,
368 GL_FALSE);
369 // Disable performance messages to reduce spam.
370 glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE, GL_DONT_CARE, 0, nullptr,
371 GL_FALSE);
372 glDebugMessageCallbackKHR(DebugMessageCallback, reinterpret_cast<const void *>(callbackChain));
373 }
374
375 namespace angle
376 {
377
378 namespace essl1_shaders
379 {
380
PositionAttrib()381 const char *PositionAttrib()
382 {
383 return "a_position";
384 }
ColorUniform()385 const char *ColorUniform()
386 {
387 return "u_color";
388 }
389
Texture2DUniform()390 const char *Texture2DUniform()
391 {
392 return "u_tex2D";
393 }
394
395 namespace vs
396 {
397
398 // A shader that sets gl_Position to zero.
Zero()399 const char *Zero()
400 {
401 return R"(void main()
402 {
403 gl_Position = vec4(0);
404 })";
405 }
406
407 // A shader that sets gl_Position to attribute a_position.
Simple()408 const char *Simple()
409 {
410 return R"(precision highp float;
411 attribute vec4 a_position;
412
413 void main()
414 {
415 gl_Position = a_position;
416 })";
417 }
418
419 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
420 // v_position.
Passthrough()421 const char *Passthrough()
422 {
423 return R"(precision highp float;
424 attribute vec4 a_position;
425 varying vec4 v_position;
426
427 void main()
428 {
429 gl_Position = a_position;
430 v_position = a_position;
431 })";
432 }
433
434 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
435 // texcoord.
Texture2D()436 const char *Texture2D()
437 {
438 return R"(precision highp float;
439 attribute vec4 a_position;
440 varying vec2 v_texCoord;
441
442 void main()
443 {
444 gl_Position = vec4(a_position.xy, 0.0, 1.0);
445 v_texCoord = a_position.xy * 0.5 + vec2(0.5);
446 })";
447 }
448
449 } // namespace vs
450
451 namespace fs
452 {
453
454 // A shader that renders a simple checker pattern of red and green. X axis and y axis separate the
455 // different colors. Needs varying v_position.
Checkered()456 const char *Checkered()
457 {
458 return R"(precision highp float;
459 varying vec4 v_position;
460
461 void main()
462 {
463 bool isLeft = v_position.x < 0.0;
464 bool isTop = v_position.y < 0.0;
465 if (isLeft)
466 {
467 if (isTop)
468 {
469 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
470 }
471 else
472 {
473 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
474 }
475 }
476 else
477 {
478 if (isTop)
479 {
480 gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
481 }
482 else
483 {
484 gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
485 }
486 }
487 })";
488 }
489
490 // A shader that fills with color taken from uniform named "color".
UniformColor()491 const char *UniformColor()
492 {
493 return R"(uniform mediump vec4 u_color;
494 void main(void)
495 {
496 gl_FragColor = u_color;
497 })";
498 }
499
500 // A shader that fills with 100% opaque red.
Red()501 const char *Red()
502 {
503 return R"(precision mediump float;
504
505 void main()
506 {
507 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
508 })";
509 }
510
511 // A shader that fills with 100% opaque green.
Green()512 const char *Green()
513 {
514 return R"(precision mediump float;
515
516 void main()
517 {
518 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
519 })";
520 }
521
522 // A shader that fills with 100% opaque blue.
Blue()523 const char *Blue()
524 {
525 return R"(precision mediump float;
526
527 void main()
528 {
529 gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
530 })";
531 }
532
533 // A shader that samples the texture.
Texture2D()534 const char *Texture2D()
535 {
536 return R"(precision mediump float;
537 uniform sampler2D u_tex2D;
538 varying vec2 v_texCoord;
539
540 void main()
541 {
542 gl_FragColor = texture2D(u_tex2D, v_texCoord);
543 })";
544 }
545
546 } // namespace fs
547 } // namespace essl1_shaders
548
549 namespace essl3_shaders
550 {
551
PositionAttrib()552 const char *PositionAttrib()
553 {
554 return "a_position";
555 }
Texture2DUniform()556 const char *Texture2DUniform()
557 {
558 return "u_tex2D";
559 }
LodUniform()560 const char *LodUniform()
561 {
562 return "u_lod";
563 }
564
565 namespace vs
566 {
567
568 // A shader that sets gl_Position to zero.
Zero()569 const char *Zero()
570 {
571 return R"(#version 300 es
572 void main()
573 {
574 gl_Position = vec4(0);
575 })";
576 }
577
578 // A shader that sets gl_Position to attribute a_position.
Simple()579 const char *Simple()
580 {
581 return R"(#version 300 es
582 in vec4 a_position;
583 void main()
584 {
585 gl_Position = a_position;
586 })";
587 }
588
589 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
590 // v_position.
Passthrough()591 const char *Passthrough()
592 {
593 return R"(#version 300 es
594 in vec4 a_position;
595 out vec4 v_position;
596 void main()
597 {
598 gl_Position = a_position;
599 v_position = a_position;
600 })";
601 }
602
603 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
604 // texcoord.
Texture2DLod()605 const char *Texture2DLod()
606 {
607 return R"(#version 300 es
608 in vec4 a_position;
609 out vec2 v_texCoord;
610
611 void main()
612 {
613 gl_Position = vec4(a_position.xy, 0.0, 1.0);
614 v_texCoord = a_position.xy * 0.5 + vec2(0.5);
615 })";
616 }
617
618 } // namespace vs
619
620 namespace fs
621 {
622
623 // A shader that fills with 100% opaque red.
Red()624 const char *Red()
625 {
626 return R"(#version 300 es
627 precision highp float;
628 out vec4 my_FragColor;
629 void main()
630 {
631 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
632 })";
633 }
634
635 // A shader that fills with 100% opaque green.
Green()636 const char *Green()
637 {
638 return R"(#version 300 es
639 precision highp float;
640 out vec4 my_FragColor;
641 void main()
642 {
643 my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
644 })";
645 }
646
647 // A shader that fills with 100% opaque blue.
Blue()648 const char *Blue()
649 {
650 return R"(#version 300 es
651 precision highp float;
652 out vec4 my_FragColor;
653 void main()
654 {
655 my_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
656 })";
657 }
658
659 // A shader that samples the texture at a given lod.
Texture2DLod()660 const char *Texture2DLod()
661 {
662 return R"(#version 300 es
663 precision mediump float;
664 uniform sampler2D u_tex2D;
665 uniform float u_lod;
666 in vec2 v_texCoord;
667 out vec4 my_FragColor;
668
669 void main()
670 {
671 my_FragColor = textureLod(u_tex2D, v_texCoord, u_lod);
672 })";
673 }
674
675 } // namespace fs
676 } // namespace essl3_shaders
677
678 namespace essl31_shaders
679 {
680
PositionAttrib()681 const char *PositionAttrib()
682 {
683 return "a_position";
684 }
685
686 namespace vs
687 {
688
689 // A shader that sets gl_Position to zero.
Zero()690 const char *Zero()
691 {
692 return R"(#version 310 es
693 void main()
694 {
695 gl_Position = vec4(0);
696 })";
697 }
698
699 // A shader that sets gl_Position to attribute a_position.
Simple()700 const char *Simple()
701 {
702 return R"(#version 310 es
703 in vec4 a_position;
704 void main()
705 {
706 gl_Position = a_position;
707 })";
708 }
709
710 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
711 // v_position.
Passthrough()712 const char *Passthrough()
713 {
714 return R"(#version 310 es
715 in vec4 a_position;
716 out vec4 v_position;
717 void main()
718 {
719 gl_Position = a_position;
720 v_position = a_position;
721 })";
722 }
723
724 } // namespace vs
725
726 namespace fs
727 {
728
729 // A shader that fills with 100% opaque red.
Red()730 const char *Red()
731 {
732 return R"(#version 310 es
733 precision highp float;
734 out vec4 my_FragColor;
735 void main()
736 {
737 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
738 })";
739 }
740
741 // A shader that fills with 100% opaque green.
Green()742 const char *Green()
743 {
744 return R"(#version 310 es
745 precision highp float;
746 out vec4 my_FragColor;
747 void main()
748 {
749 my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
750 })";
751 }
752
753 // A shader that renders a simple gradient of red to green. Needs varying v_position.
RedGreenGradient()754 const char *RedGreenGradient()
755 {
756 return R"(#version 310 es
757 precision highp float;
758 in vec4 v_position;
759 out vec4 my_FragColor;
760
761 void main()
762 {
763 my_FragColor = vec4(v_position.xy * 0.5 + vec2(0.5), 0.0, 1.0);
764 })";
765 }
766
767 } // namespace fs
768 } // namespace essl31_shaders
769 } // namespace angle
770