1 #ifdef _WIN32
2 #include <stdio.h>
main()3 int main() {
4     printf("[SKIP] OpenGL on Windows is broken.\n");
5     return 0;
6 }
7 #else
8 
9 #include "Halide.h"
10 
11 #include <cstddef>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cstring>
15 
16 #if __APPLE__
17 // TODO: why are these deprecated? Can we update this test?
18 #define GL_SILENCE_DEPRECATION
19 #include <OpenGL/gl3.h>
20 #else
21 #define GL_GLEXT_PROTOTYPES
22 #include <GL/gl.h>
23 #endif
24 
25 // Generates an arbitrary program.
26 class Program {
27 public:
handle()28     static GLuint handle() {
29         const char *vertexShader = " \
30                                     attribute vec4 Position;  \
31                                     attribute vec2 TexCoordIn; \
32                                     varying vec2 TexCoordOut; \
33                                     void main(void) {  \
34                                         gl_Position = Position; \
35                                         TexCoordOut = TexCoordIn; \
36                                     }";
37 
38         const char *fragmentShader = " \
39                                       varying vec2 TexCoordOut; \
40                                       uniform sampler2D Texture; \
41                                       void main(void) { \
42                                           gl_FragColor = texture2D(Texture, TexCoordOut); \
43                                       }";
44 
45         GLuint handle = glCreateProgram();
46         glAttachShader(handle, compileShader("vertex", vertexShader, GL_VERTEX_SHADER));
47         glAttachShader(handle, compileShader("fragment", fragmentShader, GL_FRAGMENT_SHADER));
48         glLinkProgram(handle);
49 
50         GLint linkSuccess;
51         glGetProgramiv(handle, GL_LINK_STATUS, &linkSuccess);
52         if (linkSuccess == GL_FALSE) {
53             GLchar messages[256];
54             glGetProgramInfoLog(handle, sizeof(messages), 0, messages);
55             fprintf(stderr, "Error linking program: %s\n", messages);
56             exit(1);
57         }
58 
59         return handle;
60     }
61 
62 private:
compileShader(const char * label,const char * shaderString,GLenum shaderType)63     static GLuint compileShader(const char *label, const char *shaderString, GLenum shaderType) {
64         const GLuint handle = glCreateShader(shaderType);
65         const int len = strlen(shaderString);
66         glShaderSource(handle, 1, &shaderString, &len);
67         glCompileShader(handle);
68         GLint compileSuccess;
69         glGetShaderiv(handle, GL_COMPILE_STATUS, &compileSuccess);
70         if (compileSuccess == GL_FALSE) {
71             GLchar messages[256];
72             glGetShaderInfoLog(handle, sizeof(messages), 0, messages);
73             fprintf(stderr, "Error compiling %s shader: %s\n", label, messages);
74             exit(1);
75         }
76         return handle;
77     }
78 };
79 
80 // Encapsulates setting OpenGL's state to arbitrary values, and checking
81 // whether the state matches those values.
82 class KnownState {
83 private:
gl_enable(GLenum cap,bool state)84     void gl_enable(GLenum cap, bool state) {
85         (state ? glEnable : glDisable)(cap);
86     }
87 
gl_gen(void (* fn)(GLsizei,GLuint *))88     GLuint gl_gen(void (*fn)(GLsizei, GLuint *)) {
89         GLuint val;
90         (*fn)(1, &val);
91         return val;
92     }
93 
check_value(const char * operation,const char * label,GLenum pname,GLint initial)94     void check_value(const char *operation, const char *label, GLenum pname, GLint initial) {
95         GLint val;
96         glGetIntegerv(pname, &val);
97         if (val != initial) {
98             fprintf(stderr, "%s did not restore %s: initial value was %d (%#x), current value is %d (%#x)\n", operation, label, initial, initial, val, val);
99             errors = true;
100         }
101     }
102 
check_value(const char * operation,const char * label,GLenum pname,GLenum initial)103     void check_value(const char *operation, const char *label, GLenum pname, GLenum initial) {
104         check_value(operation, label, pname, (GLint)initial);
105     }
106 
check_value(const char * operation,const char * label,GLenum pname,GLint initial[],int n=4)107     void check_value(const char *operation, const char *label, GLenum pname, GLint initial[], int n = 4) {
108         GLint val[2048];
109         glGetIntegerv(pname, val);
110         for (int i = 0; i < n; i++) {
111             if (val[i] != initial[i]) {
112                 fprintf(stderr, "%s did not restore %s: initial value was", operation, label);
113                 for (int j = 0; j < n; j++) {
114                     fprintf(stderr, " %d", initial[j]);
115                 }
116                 fprintf(stderr, ", current value is");
117                 for (int j = 0; j < n; j++) {
118                     fprintf(stderr, " %d", val[j]);
119                 }
120                 fprintf(stderr, "\n");
121                 errors = true;
122                 return;
123             }
124         }
125     }
126 
check_value(const char * operation,const char * label,GLenum pname,bool initial)127     void check_value(const char *operation, const char *label, GLenum pname, bool initial) {
128         GLboolean val;
129         glGetBooleanv(pname, &val);
130         if (val != initial) {
131             fprintf(stderr, "%s did not restore boolean %s: initial value was %s, current value is %s\n", operation, label, initial ? "true" : "false", val ? "true" : "false");
132             errors = true;
133         }
134     }
135 
check_error(const char * label)136     void check_error(const char *label) {
137         GLenum err = glGetError();
138         if (err != GL_NO_ERROR) {
139             fprintf(stderr, "Error setting %s: OpenGL error %#x\n", label, err);
140             errors = true;
141         }
142     }
143 
144     // version of OpenGL
145     int gl_major_version;
146     int gl_minor_version;
147 
148     GLenum initial_active_texture;
149     GLint initial_viewport[4];
150     GLuint initial_array_buffer_binding;
151     GLuint initial_element_array_buffer_binding;
152     GLuint initial_current_program;
153     GLuint initial_framebuffer_binding;
154     static const int ntextures = 10;
155     GLuint initial_bound_textures[ntextures];
156     bool initial_cull_face;
157     bool initial_depth_test;
158 
159     static const int nvertex_attribs = 10;
160     bool initial_vertex_attrib_array_enabled[nvertex_attribs];
161 
162     // The next two functions are stolen from opengl.cpp
163     // and are used to parse the major/minor version of OpenGL
164     // to see if vertex array objects are supported
parse_int(const char * str,int * val)165     const char *parse_int(const char *str, int *val) {
166         int v = 0;
167         size_t i = 0;
168         while (str[i] >= '0' && str[i] <= '9') {
169             v = 10 * v + (str[i] - '0');
170             i++;
171         }
172         if (i > 0) {
173             *val = v;
174             return &str[i];
175         }
176         return NULL;
177     }
178 
parse_opengl_version(const char * str,int * major,int * minor)179     const char *parse_opengl_version(const char *str, int *major, int *minor) {
180         str = parse_int(str, major);
181         if (str == NULL || *str != '.') {
182             return NULL;
183         }
184         return parse_int(str + 1, minor);
185     }
186 
187     GLuint initial_vertex_array_binding;
188 
189 public:
190     bool errors{false};
191 
192     // This sets most values to generated or arbitrary values, which the
193     // halide calls would be unlikely to accidentally use.  But for boolean
194     // values, we want to be sure that halide is really restoring the
195     // initial value, not just setting it to true or false.  So we need to
196     // be able to try both.
setup(bool boolval)197     void setup(bool boolval) {
198         // parse the OpenGL version
199         const char *version = (const char *)glGetString(GL_VERSION);
200         parse_opengl_version(version, &gl_major_version, &gl_minor_version);
201 
202         glGenTextures(ntextures, initial_bound_textures);
203         for (int i = 0; i < ntextures; i++) {
204             glActiveTexture(GL_TEXTURE0 + i);
205             glBindTexture(GL_TEXTURE_2D, initial_bound_textures[i]);
206         }
207         glActiveTexture(initial_active_texture = GL_TEXTURE3);
208 
209         for (int i = 0; i < nvertex_attribs; i++) {
210             if ((initial_vertex_attrib_array_enabled[i] = boolval)) {
211                 glEnableVertexAttribArray(i);
212             } else {
213                 glDisableVertexAttribArray(i);
214             }
215             char buf[256];
216             sprintf(buf, "vertex attrib array %d state", i);
217             check_error(buf);
218         }
219 
220         glUseProgram(initial_current_program = Program::handle());
221         glViewport(initial_viewport[0] = 111, initial_viewport[1] = 222, initial_viewport[2] = 333, initial_viewport[3] = 444);
222         gl_enable(GL_CULL_FACE, initial_cull_face = boolval);
223         gl_enable(GL_DEPTH_TEST, initial_depth_test = boolval);
224         glBindBuffer(GL_ARRAY_BUFFER, initial_array_buffer_binding = gl_gen(glGenBuffers));
225         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, initial_element_array_buffer_binding = gl_gen(glGenBuffers));
226         glBindFramebuffer(GL_FRAMEBUFFER, initial_framebuffer_binding = gl_gen(glGenFramebuffers));
227 
228         // Vertex array objects are only used by Halide if the OpenGL version >=3
229         if (gl_major_version >= 3) {
230             glBindVertexArray(initial_vertex_array_binding = gl_gen(glGenVertexArrays));
231         }
232 
233         check_error("known state");
234     }
235 
check(const char * operation)236     void check(const char *operation) {
237         check_value(operation, "ActiveTexture", GL_ACTIVE_TEXTURE, initial_active_texture);
238         check_value(operation, "current program", GL_CURRENT_PROGRAM, initial_current_program);
239         check_value(operation, "framebuffer binding", GL_FRAMEBUFFER_BINDING, initial_framebuffer_binding);
240         check_value(operation, "array buffer binding", GL_ARRAY_BUFFER_BINDING, initial_array_buffer_binding);
241         check_value(operation, "element array buffer binding", GL_ELEMENT_ARRAY_BUFFER_BINDING, initial_element_array_buffer_binding);
242         check_value(operation, "viewport", GL_VIEWPORT, initial_viewport);
243         check_value(operation, "GL_CULL_FACE", GL_CULL_FACE, initial_cull_face);
244         check_value(operation, "GL_DEPTH_TEST", GL_DEPTH_TEST, initial_cull_face);
245 
246         // Vertex array objects are only used by Halide if the OpenGL version >=3
247         if (gl_major_version >= 3) {
248             check_value(operation, "vertex array binding", GL_VERTEX_ARRAY_BINDING, initial_vertex_array_binding);
249         } else {
250             fprintf(stderr, "Skipping vertex array binding tests because OpenGL version is %d.%d (<3.0)\n", gl_major_version, gl_minor_version);
251         }
252 
253         for (int i = 0; i < ntextures; i++) {
254             char buf[100];
255             sprintf(buf, "bound texture (unit %d)", i);
256             glActiveTexture(GL_TEXTURE0 + i);
257             check_value(operation, buf, GL_TEXTURE_BINDING_2D, initial_bound_textures[i]);
258         }
259 
260         for (int i = 0; i < nvertex_attribs; i++) {
261             int initial = initial_vertex_attrib_array_enabled[i];
262             GLint val;
263             glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &val);
264             if (val != initial) {
265                 fprintf(stderr, "%s did not restore boolean VertexAttributeArrayEnabled(%d): initial value was %s, current value is %s\n", operation, i, initial ? "true" : "false", val ? "true" : "false");
266                 errors = true;
267             }
268         }
269     }
270 };
271 
272 using namespace Halide;
273 
main()274 int main() {
275     // This test must be run with an OpenGL target.
276     const Target target = get_jit_target_from_environment().with_feature(Target::OpenGL);
277 
278     KnownState known_state;
279 
280     Buffer<uint8_t> input(255, 10, 3);
281     Buffer<uint8_t> out(UInt(8), 255, 10, 3);
282 
283     Var x, y, c;
284     Func g;
285     g(x, y, c) = input(x, y, c);
286     g.bound(c, 0, 3);
287     g.glsl(x, y, c);
288     g.realize(out, target);  // let Halide initialize OpenGL
289 
290     known_state.setup(true);
291     g.realize(out, target);
292     known_state.check("realize");
293 
294     known_state.setup(true);
295     out.copy_to_host();
296     known_state.check("copy_to_host");
297 
298     known_state.setup(false);
299     g.realize(out, target);
300     known_state.check("realize");
301 
302     known_state.setup(false);
303     out.copy_to_host();
304     known_state.check("copy_to_host");
305 
306     if (known_state.errors) {
307         return 1;
308     }
309 
310     printf("Success!\n");
311     return 0;
312 }
313 
314 #endif
315