666ede9b5fd074c2dfe089c86b9a7321c3b438bb
[gltest.git] / glutil_gl2.cpp
1 /* gltest - small OpenGL tearing test program
2  * Copyright (C) 2012-2013 Ralf Jung <post@ralfj.de>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string>
22 #include <sstream>
23
24 #include "glutil.h"
25
26 // extension functions we use
27 typedef GLuint (*GLCREATESHADERPROC) (GLenum type);
28 typedef void (*GLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
29 typedef void (*GLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
30 typedef void (*GLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const *string, const GLint *length);
31 typedef void (*GLCOMPILESHADERPROC) (GLuint shader);
32 typedef GLuint (*GLCREATEPROGRAMPROC) (void);
33 typedef void (*GLATTACHSHADERPROC) (GLuint program, GLuint shader);
34 typedef void (*GLLINKPROGRAMPROC) (GLuint program);
35 typedef GLint (*GLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
36 typedef void (*GLUSEPROGRAMPROC) (GLuint program);
37
38 typedef void (*GLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
39 typedef void (*GLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
40 typedef void (*GLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
41 typedef void (*GLBINDBUFFERPROC) (GLenum target, GLuint buffer);
42 typedef void (*GLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
43 typedef void (*GLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
44
45 #ifdef CON_GL3
46 // VAOs are only supported in GL3+, and not needed in earler contexts
47 typedef void (*GLBINDVERTEXARRAYPROC) (GLuint array);
48 typedef void (*GLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
49 typedef void (*GLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
50 #endif
51
52 GLGETSHADERIVPROC p_glGetShaderiv = NULL;
53 GLGETSHADERINFOLOGPROC p_glGetShaderInfoLog = NULL;
54 GLCREATESHADERPROC p_glCreateShader = NULL;
55 GLSHADERSOURCEPROC p_glShaderSource = NULL;
56 GLCOMPILESHADERPROC p_glCompileShader = NULL;
57 GLCREATEPROGRAMPROC p_glCreateProgram = NULL;
58 GLATTACHSHADERPROC p_glAttachShader = NULL;
59 GLLINKPROGRAMPROC p_glLinkProgram = NULL;
60 GLGETATTRIBLOCATIONPROC p_glGetAttribLocation = NULL;
61 GLUSEPROGRAMPROC p_glUseProgram = NULL;
62
63 GLVERTEXATTRIBPOINTERPROC p_glVertexAttribPointer = NULL;
64 GLENABLEVERTEXATTRIBARRAYPROC p_glEnableVertexAttribArray = NULL;
65 GLGENBUFFERSPROC p_glGenBuffers = NULL;
66 GLBINDBUFFERPROC p_glBindBuffer = NULL;
67 GLBUFFERDATAPROC p_glBufferData = NULL;
68 GLDELETEBUFFERSPROC p_glDeleteBuffers = NULL;
69
70 #ifdef CON_GL3
71 GLBINDVERTEXARRAYPROC p_glBindVertexArray = NULL;
72 GLDELETEVERTEXARRAYSPROC p_glDeleteVertexArrays = NULL;
73 GLGENVERTEXARRAYSPROC p_glGenVertexArrays = NULL;
74 #endif
75
76 // resolve function pointers
77 static T_proc resolveFunctionPointer(T_glGetProcAddress p_glGetProcAddress, const char *name)
78 {
79         T_proc proc = p_glGetProcAddress(name);
80         if (proc == NULL) {
81                 fprintf(stderr, "Error resolvung function %s\n", name);
82                 exit(1);
83         }
84         return proc;
85 }
86
87 void resolveFunctionPointers(T_glGetProcAddress p_glGetProcAddress)
88 {
89 #ifdef CON_GL1
90         // we need to check if we have GL 2.0 or greater
91         std::string version = (char*)glGetString(GL_VERSION);
92         std::stringstream ssv(version);
93         std::string major;
94         std::getline(ssv, major, '.');
95         std::stringstream ssm(major);
96         int majorVersion;
97         ssm >> majorVersion;
98         if (majorVersion < 2) {
99                 fprintf(stderr, "Need at least GL 2.0 to function properly, but detected version %d\n", majorVersion);
100                 exit(1);
101         }
102 #endif
103         
104         // now get the function pointers
105         p_glGetShaderiv = (GLGETSHADERIVPROC)resolveFunctionPointer(p_glGetProcAddress, "glGetShaderiv");
106         p_glGetShaderInfoLog = (GLGETSHADERINFOLOGPROC)resolveFunctionPointer(p_glGetProcAddress, "glGetShaderInfoLog");
107         p_glCreateShader = (GLCREATESHADERPROC)resolveFunctionPointer(p_glGetProcAddress, "glCreateShader");
108         p_glShaderSource = (GLSHADERSOURCEPROC)resolveFunctionPointer(p_glGetProcAddress, "glShaderSource");
109         p_glCompileShader = (GLCOMPILESHADERPROC)resolveFunctionPointer(p_glGetProcAddress, "glCompileShader");
110         p_glCreateProgram = (GLCREATEPROGRAMPROC)resolveFunctionPointer(p_glGetProcAddress, "glCreateProgram");
111         p_glAttachShader = (GLATTACHSHADERPROC)resolveFunctionPointer(p_glGetProcAddress, "glAttachShader");
112         p_glLinkProgram = (GLLINKPROGRAMPROC)resolveFunctionPointer(p_glGetProcAddress, "glLinkProgram");
113         p_glGetAttribLocation = (GLGETATTRIBLOCATIONPROC)resolveFunctionPointer(p_glGetProcAddress, "glGetAttribLocation");
114
115 #ifdef CON_GL3
116         p_glBindVertexArray = (GLBINDVERTEXARRAYPROC)resolveFunctionPointer(p_glGetProcAddress, "glBindVertexArray");
117         p_glDeleteVertexArrays = (GLDELETEVERTEXARRAYSPROC)resolveFunctionPointer(p_glGetProcAddress, "glDeleteVertexArrays");
118         p_glGenVertexArrays = (GLGENVERTEXARRAYSPROC)resolveFunctionPointer(p_glGetProcAddress, "glGenVertexArrays");
119 #endif
120
121         p_glUseProgram = (GLUSEPROGRAMPROC)resolveFunctionPointer(p_glGetProcAddress, "glUseProgram");
122         p_glVertexAttribPointer = (GLVERTEXATTRIBPOINTERPROC)resolveFunctionPointer(p_glGetProcAddress, "glVertexAttribPointer");
123         p_glEnableVertexAttribArray = (GLENABLEVERTEXATTRIBARRAYPROC)resolveFunctionPointer(p_glGetProcAddress, "glEnableVertexAttribArray");
124         p_glGenBuffers = (GLGENBUFFERSPROC)resolveFunctionPointer(p_glGetProcAddress, "glGenBuffers");
125         p_glBindBuffer = (GLBINDBUFFERPROC)resolveFunctionPointer(p_glGetProcAddress, "glBindBuffer");
126         p_glBufferData = (GLBUFFERDATAPROC)resolveFunctionPointer(p_glGetProcAddress, "glBufferData");
127         p_glDeleteBuffers = (GLDELETEBUFFERSPROC)resolveFunctionPointer(p_glGetProcAddress, "glDeleteBuffers");
128 }
129
130 static const char *glErrorToString(GLenum e)
131 {
132 #define CASE(name) case name: return #name
133         switch (e) {
134                 CASE(GL_NO_ERROR);
135                 CASE(GL_INVALID_ENUM);
136                 CASE(GL_INVALID_VALUE);
137                 CASE(GL_INVALID_OPERATION);
138 #ifndef CON_GLES2
139                 CASE(GL_STACK_OVERFLOW);
140                 CASE(GL_STACK_UNDERFLOW);
141 #endif
142                 CASE(GL_OUT_OF_MEMORY);
143                 default: return "<unknown>";
144         }
145 #undef CASE
146 }
147
148 static void checkGlError(const char *what)
149 {
150         GLenum e = glGetError();
151         if (e == GL_NO_ERROR) return;
152         fprintf(stderr, "GL error %d (%s): %s\n", e, glErrorToString(e), what);
153         exit(1);
154 }
155
156 // shaders
157 static const char *vertex_shader_source =
158 "#version 100 \n\
159 attribute vec2 position; \n\
160 attribute vec3 color; \n\
161 varying vec3 frag_color; \n\
162 void main() \n\
163 { \n\
164         frag_color = color; \n\
165         gl_Position = vec4(position * vec2(2) - vec2(1), 0.0, 1.0); \n\
166 }";
167 static const char *fragment_shader_source =
168 "#version 100 \n\
169 varying vec3 frag_color; \n\
170 void main(void) \n\
171 { \n\
172         gl_FragColor = vec4(frag_color, 1.0); \n\
173 }";
174
175
176 // OpenGL IDs (yeah, this is a bad hack... do we care?)
177 static GLuint program;
178 static struct {
179         GLint position;
180         GLint color;
181 } attributes;
182
183 // some local hlper functions
184 static void show_info_log(GLuint object)
185 {
186         GLint log_length;
187         p_glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
188         char *log = new char[log_length];
189         p_glGetShaderInfoLog(object, log_length, NULL, log);
190         fprintf(stderr, "%s", log);
191         delete[] log;
192 }
193
194 static GLuint compile_shader(GLenum shader_type, const char *source)
195 {
196         GLuint shader = p_glCreateShader(shader_type);
197         p_glShaderSource(shader, 1, &source, NULL);
198         p_glCompileShader(shader);
199         GLint shader_ok;
200         p_glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
201         if (!shader_ok) {
202                 fprintf(stderr, "Failed to compile shader\n");
203                 show_info_log(shader);
204                 exit(1);
205         }
206         checkGlError("Compiling shader");
207         return shader;
208 }
209
210 static GLuint createArrayBuffer(GLsizeiptr size, GLfloat *data)
211 {
212         GLuint buffer;
213         p_glGenBuffers(1, &buffer);
214         p_glBindBuffer(GL_ARRAY_BUFFER, buffer);
215         p_glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
216         checkGlError("Creating array buffer");
217         return buffer;
218 }
219
220 // initialisation
221 void initialise2dProjection()
222 {
223         GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_source);
224         GLuint fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_source);
225         program = p_glCreateProgram();
226         p_glAttachShader(program, vertex_shader);
227         p_glAttachShader(program, fragment_shader);
228         p_glLinkProgram(program);
229         attributes.position = p_glGetAttribLocation(program, "position");
230         attributes.color = p_glGetAttribLocation(program, "color");
231 }
232
233 // draw a quad
234 void drawQuad(GLfloat red, GLfloat green, GLfloat blue, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
235 {
236         p_glUseProgram(program);
237         checkGlError("Enabling the shaders");
238
239 #ifdef CON_GL3
240         // create a vertex array obect and use it
241         GLuint vao;
242         p_glGenVertexArrays(1, &vao);
243         p_glBindVertexArray(vao);
244         checkGlError("Creating a VAO");
245 #endif
246         
247         // send vertex data to card with given attribute
248         GLfloat vertex_buffer_data[] = {
249                 x1, y1,
250                 x2, y1,
251                 x2, y2,
252                 x1, y2,
253         };
254         GLuint vertex_buffer = createArrayBuffer(sizeof(vertex_buffer_data), vertex_buffer_data);
255         p_glVertexAttribPointer(
256                 attributes.position,              /* attribute */
257                 2,                                /* size */
258                 GL_FLOAT,                         /* type */
259                 GL_FALSE,                         /* normalized? */
260                 0,                                /* stride */
261                 (void*)0                          /* array buffer offset */
262         );
263         checkGlError("Preparing vertex data");
264         p_glEnableVertexAttribArray(attributes.position);
265         checkGlError("Sending vertex data");
266         
267         // same with color data
268         GLfloat color_buffer_data[] = {
269                 red, green, blue,
270                 red, green, blue,
271                 red, green, blue,
272                 red, green, blue,
273         };
274         GLuint color_buffer = createArrayBuffer(sizeof(color_buffer_data), color_buffer_data);
275         p_glVertexAttribPointer(
276                 attributes.color,              /* attribute */
277                 3,                             /* size */
278                 GL_FLOAT,                      /* type */
279                 GL_FALSE,                      /* normalized? */
280                 0,                             /* stride */
281                 (void*)0                       /* array buffer offset */
282         );
283         checkGlError("Preparing color data");
284         p_glEnableVertexAttribArray(attributes.color);
285         checkGlError("Sending color data");
286         
287         // draw
288         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
289         checkGlError("Drawing");
290         
291         // cleanup
292         p_glDeleteBuffers(1, &vertex_buffer);
293         p_glDeleteBuffers(1, &color_buffer);
294 #ifdef CON_GL3
295         p_glDeleteVertexArrays(1, &vao);
296 #endif
297         checkGlError("Doing cleanup");
298 }