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