1 module gfm.opengl.shader;
2 
3 import std.string,
4        std.conv,
5        std.experimental.logger;
6 
7 import bindbc.opengl;
8 
9 import gfm.opengl.opengl;
10 
11 
12 /// OpenGL Shader wrapper.
13 final class GLShader
14 {
15     public
16     {
17         /// Creates a shader devoid of source code.
18         /// Throws: $(D OpenGLException) on error.
19         this(GLenum shaderType)
20         {
21             _shader = glCreateShader(shaderType);
22             if (_shader == 0)
23                 throw new OpenGLException("glCreateShader failed");
24             _initialized = true;
25         }
26 
27         /// Creates a shader with source code and compiles it.
28         /// Throws: $(D OpenGLException) on error.
29         this(GLenum shaderType, string[] lines)
30         {
31             this(shaderType);
32             try
33             {
34                 load(lines);
35                 compile();
36             }
37             catch(Exception e)
38             {
39                 glDeleteShader(_shader);
40                 _initialized = false;
41                 throw e;
42             }
43         }
44 
45         /// Releases the OpenGL shader resource.
46         ~this()
47         {
48             if (_initialized)
49             {
50                 debug ensureNotInGC("GLShader");
51                 glDeleteShader(_shader);
52                 _initialized = false;
53             }
54         }
55 
56         /// Load source code for this shader.
57         /// Throws: $(D OpenGLException) on error.
58         void load(string[] lines)
59         {
60             size_t lineCount = lines.length;
61 
62             auto lengths = new GLint[lineCount];
63             auto addresses = new immutable(GLchar)*[lineCount];
64             auto localLines = new string[lineCount];
65 
66             for (size_t i = 0; i < lineCount; ++i)
67             {
68                 localLines[i] = lines[i] ~ "\n";
69                 lengths[i] = cast(GLint)(localLines[i].length);
70                 addresses[i] = localLines[i].ptr;
71             }
72 
73             glShaderSource(_shader,
74                            cast(GLint)lineCount,
75                            cast(const(char)**)addresses.ptr,
76                            cast(const(int)*)(lengths.ptr));
77             runtimeCheck();
78         }
79 
80         /// Compile this OpenGL shader.
81         /// Throws: $(D OpenGLException) on compilation error.
82         void compile()
83         {
84             glCompileShader(_shader);
85             runtimeCheck();
86 
87             // print info log
88             const(char)[] infoLog = getInfoLog();
89             if (infoLog != null && _logger)
90                 _logger.info(infoLog);
91 
92             GLint compiled;
93             glGetShaderiv(_shader, GL_COMPILE_STATUS, &compiled);
94 
95             if (compiled != GL_TRUE)
96                 throw new OpenGLException("shader did not compile");
97         }
98 
99         /// Gets the compiling report.
100         /// Returns: Log output of the GLSL compiler. Can return null!
101         /// Throws: $(D OpenGLException) on error.
102         const(char)[] getInfoLog()
103         {
104             GLint logLength;
105             glGetShaderiv(_shader, GL_INFO_LOG_LENGTH, &logLength);
106             if (logLength <= 0) // "If shader has no information log, a value of 0 is returned."
107                 return null;
108 
109             char[] log = new char[logLength];
110             GLint dummy;
111             glGetShaderInfoLog(_shader, logLength, &dummy, log.ptr);
112             runtimeCheck();
113             return fromStringz(log.ptr);
114         }
115 
116         /// Sets a logger for the program. That allows additional output
117         /// besides error reporting.
118         void logger(Logger l) pure nothrow { _logger = l; }
119     }
120 
121     package
122     {
123         GLuint _shader;
124     }
125 
126     private
127     {
128         Logger _logger;
129         bool _initialized;
130     }
131 }
132 
133