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 "util/test_utils.h"
15
16 namespace
17 {
ReadEntireFile(const std::string & filePath,std::string * contentsOut)18 bool ReadEntireFile(const std::string &filePath, std::string *contentsOut)
19 {
20 constexpr uint32_t kMaxBufferSize = 2000;
21 char buffer[kMaxBufferSize] = {};
22 if (!angle::ReadEntireFileToString(filePath.c_str(), buffer, kMaxBufferSize) ||
23 strlen(buffer) == 0)
24 return false;
25 *contentsOut = buffer;
26 return true;
27 }
28
CompileProgramInternal(const char * vsSource,const char * gsSource,const char * fsSource,const std::function<void (GLuint)> & preLinkCallback)29 GLuint CompileProgramInternal(const char *vsSource,
30 const char *gsSource,
31 const char *fsSource,
32 const std::function<void(GLuint)> &preLinkCallback)
33 {
34 GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
35 GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
36
37 if (vs == 0 || fs == 0)
38 {
39 glDeleteShader(fs);
40 glDeleteShader(vs);
41 return 0;
42 }
43
44 GLuint program = glCreateProgram();
45
46 glAttachShader(program, vs);
47 glDeleteShader(vs);
48
49 glAttachShader(program, fs);
50 glDeleteShader(fs);
51
52 GLuint gs = 0;
53
54 if (strlen(gsSource) > 0)
55 {
56 gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
57 if (gs == 0)
58 {
59 glDeleteShader(vs);
60 glDeleteShader(fs);
61 glDeleteProgram(program);
62 return 0;
63 }
64
65 glAttachShader(program, gs);
66 glDeleteShader(gs);
67 }
68
69 if (preLinkCallback)
70 {
71 preLinkCallback(program);
72 }
73
74 glLinkProgram(program);
75
76 return CheckLinkStatusAndReturnProgram(program, true);
77 }
78 } // namespace
79
CompileShader(GLenum type,const char * source)80 GLuint CompileShader(GLenum type, const char *source)
81 {
82 GLuint shader = glCreateShader(type);
83
84 const char *sourceArray[1] = {source};
85 glShaderSource(shader, 1, sourceArray, nullptr);
86 glCompileShader(shader);
87
88 GLint compileResult;
89 glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
90
91 if (compileResult == 0)
92 {
93 GLint infoLogLength;
94 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
95
96 // Info log length includes the null terminator, so 1 means that the info log is an empty
97 // string.
98 if (infoLogLength > 1)
99 {
100 std::vector<GLchar> infoLog(infoLogLength);
101 glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
102 std::cerr << "shader compilation failed: " << &infoLog[0];
103 }
104 else
105 {
106 std::cerr << "shader compilation failed. <Empty log message>";
107 }
108
109 std::cerr << std::endl;
110
111 glDeleteShader(shader);
112 shader = 0;
113 }
114
115 return shader;
116 }
117
CompileShaderFromFile(GLenum type,const std::string & sourcePath)118 GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
119 {
120 std::string source;
121 if (!ReadEntireFile(sourcePath, &source))
122 {
123 std::cerr << "Error reading shader file: " << sourcePath << "\n";
124 return 0;
125 }
126
127 return CompileShader(type, source.c_str());
128 }
129
CheckLinkStatusAndReturnProgram(GLuint program,bool outputErrorMessages)130 GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
131 {
132 if (glGetError() != GL_NO_ERROR)
133 return 0;
134
135 GLint linkStatus;
136 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
137 if (linkStatus == 0)
138 {
139 if (outputErrorMessages)
140 {
141 GLint infoLogLength;
142 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
143
144 // Info log length includes the null terminator, so 1 means that the info log is an
145 // empty string.
146 if (infoLogLength > 1)
147 {
148 std::vector<GLchar> infoLog(infoLogLength);
149 glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
150 &infoLog[0]);
151
152 std::cerr << "program link failed: " << &infoLog[0];
153 }
154 else
155 {
156 std::cerr << "program link failed. <Empty log message>";
157 }
158 }
159
160 glDeleteProgram(program);
161 return 0;
162 }
163
164 return program;
165 }
166
GetProgramShader(GLuint program,GLint requestedType)167 GLuint GetProgramShader(GLuint program, GLint requestedType)
168 {
169 static constexpr GLsizei kMaxShaderCount = 16;
170 GLuint attachedShaders[kMaxShaderCount] = {0u};
171 GLsizei count = 0;
172 glGetAttachedShaders(program, kMaxShaderCount, &count, attachedShaders);
173 for (int i = 0; i < count; ++i)
174 {
175 GLint type = 0;
176 glGetShaderiv(attachedShaders[i], GL_SHADER_TYPE, &type);
177 if (type == requestedType)
178 {
179 return attachedShaders[i];
180 }
181 }
182
183 return 0;
184 }
185
CompileProgramWithTransformFeedback(const char * vsSource,const char * fsSource,const std::vector<std::string> & transformFeedbackVaryings,GLenum bufferMode)186 GLuint CompileProgramWithTransformFeedback(
187 const char *vsSource,
188 const char *fsSource,
189 const std::vector<std::string> &transformFeedbackVaryings,
190 GLenum bufferMode)
191 {
192 auto preLink = [&](GLuint program) {
193 if (transformFeedbackVaryings.size() > 0)
194 {
195 std::vector<const char *> constCharTFVaryings;
196
197 for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
198 {
199 constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
200 }
201
202 glTransformFeedbackVaryings(program,
203 static_cast<GLsizei>(transformFeedbackVaryings.size()),
204 &constCharTFVaryings[0], bufferMode);
205 }
206 };
207
208 return CompileProgramInternal(vsSource, "", fsSource, preLink);
209 }
210
CompileProgram(const char * vsSource,const char * fsSource)211 GLuint CompileProgram(const char *vsSource, const char *fsSource)
212 {
213 return CompileProgramInternal(vsSource, "", fsSource, nullptr);
214 }
215
CompileProgram(const char * vsSource,const char * fsSource,const std::function<void (GLuint)> & preLinkCallback)216 GLuint CompileProgram(const char *vsSource,
217 const char *fsSource,
218 const std::function<void(GLuint)> &preLinkCallback)
219 {
220 return CompileProgramInternal(vsSource, "", fsSource, preLinkCallback);
221 }
222
CompileProgramWithGS(const char * vsSource,const char * gsSource,const char * fsSource)223 GLuint CompileProgramWithGS(const char *vsSource, const char *gsSource, const char *fsSource)
224 {
225 return CompileProgramInternal(vsSource, gsSource, fsSource, nullptr);
226 }
227
CompileProgramFromFiles(const std::string & vsPath,const std::string & fsPath)228 GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)
229 {
230 std::string vsSource;
231 if (!ReadEntireFile(vsPath, &vsSource))
232 {
233 std::cerr << "Error reading shader: " << vsPath << "\n";
234 return 0;
235 }
236
237 std::string fsSource;
238 if (!ReadEntireFile(fsPath, &fsSource))
239 {
240 std::cerr << "Error reading shader: " << fsPath << "\n";
241 return 0;
242 }
243
244 return CompileProgram(vsSource.c_str(), fsSource.c_str());
245 }
246
CompileComputeProgram(const char * csSource,bool outputErrorMessages)247 GLuint CompileComputeProgram(const char *csSource, bool outputErrorMessages)
248 {
249 GLuint program = glCreateProgram();
250
251 GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
252 if (cs == 0)
253 {
254 glDeleteProgram(program);
255 return 0;
256 }
257
258 glAttachShader(program, cs);
259
260 glLinkProgram(program);
261
262 return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
263 }
264
LoadBinaryProgramOES(const std::vector<uint8_t> & binary,GLenum binaryFormat)265 GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
266 {
267 GLuint program = glCreateProgram();
268 glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
269 return CheckLinkStatusAndReturnProgram(program, true);
270 }
271
LoadBinaryProgramES3(const std::vector<uint8_t> & binary,GLenum binaryFormat)272 GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
273 {
274 GLuint program = glCreateProgram();
275 glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
276 return CheckLinkStatusAndReturnProgram(program, true);
277 }
278
LinkAttachedProgram(GLuint program)279 bool LinkAttachedProgram(GLuint program)
280 {
281 glLinkProgram(program);
282 return (CheckLinkStatusAndReturnProgram(program, true) != 0);
283 }
284
285 namespace angle
286 {
287
288 namespace essl1_shaders
289 {
290
PositionAttrib()291 const char *PositionAttrib()
292 {
293 return "a_position";
294 }
ColorUniform()295 const char *ColorUniform()
296 {
297 return "u_color";
298 }
299
Texture2DUniform()300 const char *Texture2DUniform()
301 {
302 return "u_tex2D";
303 }
304
305 namespace vs
306 {
307
308 // A shader that sets gl_Position to zero.
Zero()309 const char *Zero()
310 {
311 return R"(void main()
312 {
313 gl_Position = vec4(0);
314 })";
315 }
316
317 // A shader that sets gl_Position to attribute a_position.
Simple()318 const char *Simple()
319 {
320 return R"(precision highp float;
321 attribute vec4 a_position;
322
323 void main()
324 {
325 gl_Position = a_position;
326 })";
327 }
328
329 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
330 // v_position.
Passthrough()331 const char *Passthrough()
332 {
333 return R"(precision highp float;
334 attribute vec4 a_position;
335 varying vec4 v_position;
336
337 void main()
338 {
339 gl_Position = a_position;
340 v_position = a_position;
341 })";
342 }
343
344 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
345 // texcoord.
Texture2D()346 const char *Texture2D()
347 {
348 return R"(precision highp float;
349 attribute vec4 a_position;
350 varying vec2 v_texCoord;
351
352 void main()
353 {
354 gl_Position = vec4(a_position.xy, 0.0, 1.0);
355 v_texCoord = a_position.xy * 0.5 + vec2(0.5);
356 })";
357 }
358
359 } // namespace vs
360
361 namespace fs
362 {
363
364 // A shader that renders a simple checker pattern of red and green. X axis and y axis separate the
365 // different colors. Needs varying v_position.
Checkered()366 const char *Checkered()
367 {
368 return R"(precision highp float;
369 varying vec4 v_position;
370
371 void main()
372 {
373 if (v_position.x * v_position.y > 0.0)
374 {
375 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
376 }
377 else
378 {
379 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
380 }
381 })";
382 }
383
384 // A shader that fills with color taken from uniform named "color".
UniformColor()385 const char *UniformColor()
386 {
387 return R"(uniform mediump vec4 u_color;
388 void main(void)
389 {
390 gl_FragColor = u_color;
391 })";
392 }
393
394 // A shader that fills with 100% opaque red.
Red()395 const char *Red()
396 {
397 return R"(precision mediump float;
398
399 void main()
400 {
401 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
402 })";
403 }
404
405 // A shader that fills with 100% opaque green.
Green()406 const char *Green()
407 {
408 return R"(precision mediump float;
409
410 void main()
411 {
412 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
413 })";
414 }
415
416 // A shader that fills with 100% opaque blue.
Blue()417 const char *Blue()
418 {
419 return R"(precision mediump float;
420
421 void main()
422 {
423 gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
424 })";
425 }
426
427 // A shader that samples the texture.
Texture2D()428 const char *Texture2D()
429 {
430 return R"(precision mediump float;
431 uniform sampler2D u_tex2D;
432 varying vec2 v_texCoord;
433
434 void main()
435 {
436 gl_FragColor = texture2D(u_tex2D, v_texCoord);
437 })";
438 }
439
440 } // namespace fs
441 } // namespace essl1_shaders
442
443 namespace essl3_shaders
444 {
445
PositionAttrib()446 const char *PositionAttrib()
447 {
448 return "a_position";
449 }
450
451 namespace vs
452 {
453
454 // A shader that sets gl_Position to zero.
Zero()455 const char *Zero()
456 {
457 return R"(#version 300 es
458 void main()
459 {
460 gl_Position = vec4(0);
461 })";
462 }
463
464 // A shader that sets gl_Position to attribute a_position.
Simple()465 const char *Simple()
466 {
467 return R"(#version 300 es
468 in vec4 a_position;
469 void main()
470 {
471 gl_Position = a_position;
472 })";
473 }
474
475 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
476 // v_position.
Passthrough()477 const char *Passthrough()
478 {
479 return R"(#version 300 es
480 in vec4 a_position;
481 out vec4 v_position;
482 void main()
483 {
484 gl_Position = a_position;
485 v_position = a_position;
486 })";
487 }
488
489 } // namespace vs
490
491 namespace fs
492 {
493
494 // A shader that fills with 100% opaque red.
Red()495 const char *Red()
496 {
497 return R"(#version 300 es
498 precision highp float;
499 out vec4 my_FragColor;
500 void main()
501 {
502 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
503 })";
504 }
505
506 // A shader that fills with 100% opaque green.
Green()507 const char *Green()
508 {
509 return R"(#version 300 es
510 precision highp float;
511 out vec4 my_FragColor;
512 void main()
513 {
514 my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
515 })";
516 }
517
518 // A shader that fills with 100% opaque blue.
Blue()519 const char *Blue()
520 {
521 return R"(#version 300 es
522 precision highp float;
523 out vec4 my_FragColor;
524 void main()
525 {
526 my_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
527 })";
528 }
529
530 } // namespace fs
531 } // namespace essl3_shaders
532
533 namespace essl31_shaders
534 {
535
PositionAttrib()536 const char *PositionAttrib()
537 {
538 return "a_position";
539 }
540
541 namespace vs
542 {
543
544 // A shader that sets gl_Position to zero.
Zero()545 const char *Zero()
546 {
547 return R"(#version 310 es
548 void main()
549 {
550 gl_Position = vec4(0);
551 })";
552 }
553
554 // A shader that sets gl_Position to attribute a_position.
Simple()555 const char *Simple()
556 {
557 return R"(#version 310 es
558 in vec4 a_position;
559 void main()
560 {
561 gl_Position = a_position;
562 })";
563 }
564
565 // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
566 // v_position.
Passthrough()567 const char *Passthrough()
568 {
569 return R"(#version 310 es
570 in vec4 a_position;
571 out vec4 v_position;
572 void main()
573 {
574 gl_Position = a_position;
575 v_position = a_position;
576 })";
577 }
578
579 } // namespace vs
580
581 namespace fs
582 {
583
584 // A shader that fills with 100% opaque red.
Red()585 const char *Red()
586 {
587 return R"(#version 310 es
588 precision highp float;
589 out vec4 my_FragColor;
590 void main()
591 {
592 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
593 })";
594 }
595
596 // A shader that fills with 100% opaque green.
Green()597 const char *Green()
598 {
599 return R"(#version 310 es
600 precision highp float;
601 out vec4 my_FragColor;
602 void main()
603 {
604 my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
605 })";
606 }
607
608 } // namespace fs
609 } // namespace essl31_shaders
610 } // namespace angle
611