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