littlewood (fp32)
[maximus:littlewood.git] / littlewood.cc
1 /*
2 littlewood -- GPU accelerated Littlewood fractal renderer
3 (C) 2011 Claude Heiland-Allen <claudiusmaximus@goto10.org>
4 */
5
6 #ifndef FP
7 #define FP 32
8 #endif
9
10 #include <GL/glew.h>
11 #include <GL/freeglut.h>
12
13 #include <stdio.h>
14
15 #include <glm/glm.hpp>
16 #include <glm/gtc/matrix_transform.hpp>
17
18 #if (FP == 32)
19
20 #include "fp32_init0.vert.c"
21 const char *init0_vert = fp32_init0_vert;
22 #include "fp32_init0.frag.c"
23 const char *init0_frag = fp32_init0_frag;
24 #include "fp32_init1.vert.c"
25 const char *init1_vert = fp32_init1_vert;
26 #include "fp32_init1.geom.c"
27 const char *init1_geom = fp32_init1_geom;
28 #include "fp32_step.vert.c"
29 const char *step_vert = fp32_step_vert;
30 #include "fp32_step.geom.c"
31 const char *step_geom = fp32_step_geom;
32 #include "fp32_draw.vert.c"
33 const char *draw_vert = fp32_draw_vert;
34 #include "fp32_draw.frag.c"
35 const char *draw_frag = fp32_draw_frag;
36
37 const int major = 3;
38 const int minor = 3;
39 typedef glm::vec4 vec4;
40 typedef glm::mat4 mat4;
41 const mat4 IDENTITY = mat4(1.0f);
42 typedef float flt;
43 #define glVertexAttribPointer_(a,b,c,d,e) glVertexAttribPointer(a,b,GL_FLOAT,c,d,e)
44 #define glUniformMatrix4fv_ glUniformMatrix4fv
45 #define glUniform1f_ glUniform1f
46 #define glUniform2f_ glUniform2f
47 #define glVertex2f_ glVertex2f
48 #else
49 #if (FP == 64)
50 #define SHADER_DIR "fp64/"
51 const int major = 4;
52 const int minor = 0;
53 typedef glm::dvec4 vec4;
54 typedef glm::dmat4 mat4;
55 const mat4 IDENTITY = mat4(1.0);
56 typedef double flt;
57 #define glVertexAttribPointer_(a,b,c,d,e) glVertexAttribLPointer(a,b,GL_DOUBLE,d,e)
58 #define glUniformMatrix4fv_ glUniformMatrix4dv
59 #define glUniform1f_ glUniform1d
60 #define glUniform2f_ glUniform2d
61 #define glVertex2f_ glVertex2d
62 #else
63 #error unknown FP
64 #endif
65 #endif
66
67 #define pi 3.141592653589793
68 #define BUFFER_OFFSET(i) (((unsigned char *)0)+i)
69 #define POSITION 0
70 #define COLOR 3
71 struct point { vec4 pos, col; };
72
73 #define D do{ int e = glGetError(); if (e != 0) { fprintf(stderr, "ERROR %s:%d %d\n", __FUNCTION__, __LINE__, e); } }while(0)
74
75 int win_width = 512;
76 int win_height = 512;
77
78   GLsizei const VertexCount(1 << 25);
79
80   flt cx = 0;
81   flt cy = 0;
82   flt r  = 1;
83   mat4 MVP;
84
85   int which = 0;
86   GLuint PingPongArrayBufferName[2];
87   GLuint PingPongVertexArrayName[2];
88   GLuint InitVertexArrayName(0);
89
90   GLuint Init0ProgramName(0);
91   GLint  Init0UniformMVP(0);
92
93   GLuint Init1ProgramName(0);
94
95   GLuint StepProgramName(0);
96   GLint  StepUniformPlusMinus(0);
97   GLint  StepUniformHueShift(0);
98   GLint  StepUniformPixelSpacing(0);
99
100   GLuint DrawProgramName(0);
101   GLint  DrawUniformMVP(0);
102   GLint  DrawUniformPixelSpacing(0);
103
104   GLuint Texture2DName(0);
105   GLuint FramebufferName(0);
106
107   GLuint Query(0);
108
109 GLuint createShader(GLenum Type, const GLchar *Source) {
110   GLuint Name = glCreateShader(Type);
111   glShaderSource(Name, 1, &Source, NULL);
112   glCompileShader(Name);
113   return Name;
114 }
115
116 bool checkProgram(GLuint ProgramName) {
117                 if(!ProgramName)
118                         return false;
119
120                 GLint Result = GL_FALSE;
121                 glGetProgramiv(ProgramName, GL_LINK_STATUS, &Result);
122                 int InfoLogLength;
123                 glGetProgramiv(ProgramName, GL_INFO_LOG_LENGTH, &InfoLogLength);
124                 char *Buffer = (char *) malloc(InfoLogLength + 1);
125                 glGetProgramInfoLog(ProgramName, InfoLogLength, NULL, &Buffer[0]);
126                 Buffer[InfoLogLength] = 0;
127                 if (Buffer[0]) {
128                   fprintf(stderr, "%s\n", &Buffer[0]);
129                 }
130                 free(Buffer);
131                 return Result == GL_TRUE;
132 }
133
134
135 bool initProgram()
136 {
137         bool Validated = true;
138
139         // Create program 'init0'
140         {
141                 GLuint VertexShaderName = createShader(GL_VERTEX_SHADER, init0_vert);D;
142                 GLuint FragmentShaderName = createShader(GL_FRAGMENT_SHADER, init0_frag);D;
143
144                 Init0ProgramName = glCreateProgram();D;
145                 glAttachShader(Init0ProgramName, VertexShaderName);D;
146                 glAttachShader(Init0ProgramName, FragmentShaderName);D;
147                 glDeleteShader(VertexShaderName);D;
148                 glDeleteShader(FragmentShaderName);D;
149
150                 glLinkProgram(Init0ProgramName);D;
151
152                 Validated = Validated && checkProgram(Init0ProgramName);
153         }
154         // Get variables locations
155         {
156                 Init0UniformMVP = glGetUniformLocation(Init0ProgramName, "MVP");D;
157                 Validated = Validated && (Init0UniformMVP >= 0);
158         }
159         
160         // Create program 'init1'
161         {
162                 GLuint VertexShaderName = createShader(GL_VERTEX_SHADER, init1_vert);D;
163                 GLuint GeometryShaderName = createShader(GL_GEOMETRY_SHADER, init1_geom);D;
164
165                 Init1ProgramName = glCreateProgram();D;
166                 glAttachShader(Init1ProgramName, VertexShaderName);D;
167                 glAttachShader(Init1ProgramName, GeometryShaderName);D;
168                 glDeleteShader(VertexShaderName);D;
169                 glDeleteShader(GeometryShaderName);D;
170
171                 GLchar const * Strings[] = {"block.Position", "block.Color"}; 
172                 glTransformFeedbackVaryings(Init1ProgramName, 2, Strings, GL_INTERLEAVED_ATTRIBS);D;
173                 glLinkProgram(Init1ProgramName);D;
174
175                 Validated = Validated && checkProgram(Init1ProgramName);
176         }
177
178         // Create program 'step'
179         {
180                 GLuint VertexShaderName = createShader(GL_VERTEX_SHADER, step_vert);D;
181                 GLuint GeometryShaderName = createShader(GL_GEOMETRY_SHADER, step_geom);D;
182
183                 StepProgramName = glCreateProgram();D;
184                 glAttachShader(StepProgramName, VertexShaderName);D;
185                 glAttachShader(StepProgramName, GeometryShaderName);D;
186                 glDeleteShader(VertexShaderName);D;
187                 glDeleteShader(GeometryShaderName);D;
188
189                 GLchar const * Strings[] = {"block.Position", "block.Color"}; 
190                 glTransformFeedbackVaryings(StepProgramName, 2, Strings, GL_INTERLEAVED_ATTRIBS);D;
191                 glLinkProgram(StepProgramName);D;
192
193                 Validated = Validated && checkProgram(StepProgramName);
194
195         }
196         // Get variables locations
197         {
198                 StepUniformPlusMinus = glGetUniformLocation(StepProgramName, "plusMinus");D;
199                 StepUniformHueShift = glGetUniformLocation(StepProgramName, "hueShift");D;
200                 StepUniformPixelSpacing = glGetUniformLocation(StepProgramName, "pixelSpacing");D;
201                 Validated = Validated && (StepUniformPlusMinus >= 0) && (StepUniformHueShift >= 0) && (StepUniformPixelSpacing >= 0);
202         }
203
204         // Create program 'draw'
205         {
206                 GLuint VertexShaderName = createShader(GL_VERTEX_SHADER, draw_vert);D;
207                 GLuint FragmentShaderName = createShader(GL_FRAGMENT_SHADER, draw_frag);D;
208
209                 DrawProgramName = glCreateProgram();D;
210                 glAttachShader(DrawProgramName, VertexShaderName);D;
211                 glAttachShader(DrawProgramName, FragmentShaderName);D;
212                 glDeleteShader(VertexShaderName);D;
213                 glDeleteShader(FragmentShaderName);D;
214
215                 glLinkProgram(DrawProgramName);D;
216                 Validated = Validated && checkProgram(DrawProgramName);
217         }
218         // Get variables locations
219         {
220                 DrawUniformMVP = glGetUniformLocation(DrawProgramName, "MVP");D;
221                 DrawUniformPixelSpacing = glGetUniformLocation(DrawProgramName, "pixelSpacing");D;
222                 Validated = Validated && (DrawUniformMVP >= 0) && (DrawUniformPixelSpacing >= 0);
223         }
224
225         return Validated;
226 }
227
228 void initVertexArray1(GLuint *name, GLuint arr) {
229   glGenVertexArrays(1, name);D;
230   glBindVertexArray(*name);D; {
231     glBindBuffer(GL_ARRAY_BUFFER, arr);D; {
232       glVertexAttribPointer_(POSITION, 4, GL_FALSE, sizeof(point), 0);D;
233     } glBindBuffer(GL_ARRAY_BUFFER, 0);D;
234     glBindBuffer(GL_ARRAY_BUFFER, arr);D; {
235       glVertexAttribPointer_(COLOR, 4, GL_FALSE, sizeof(point), BUFFER_OFFSET(sizeof(vec4)));D;
236     } glBindBuffer(GL_ARRAY_BUFFER, 0);D;
237     glEnableVertexAttribArray(POSITION);D;
238     glEnableVertexAttribArray(COLOR);D;
239   } glBindVertexArray(0);D;
240 }
241
242 void initVertexArray() {
243   initVertexArray1(&PingPongVertexArrayName[0], PingPongArrayBufferName[0]);D;
244   initVertexArray1(&PingPongVertexArrayName[1], PingPongArrayBufferName[1]);D;
245   glGenVertexArrays(1, &InitVertexArrayName);D;
246   glBindVertexArray(InitVertexArrayName);D; {
247     glBindBuffer(GL_ARRAY_BUFFER, PingPongArrayBufferName[1]);D; {
248       glVertexAttribPointer_(POSITION, 4, GL_FALSE, sizeof(vec4), 0);D;
249     } glBindBuffer(GL_ARRAY_BUFFER, 0);D;
250     glEnableVertexAttribArray(POSITION);D;
251   } glBindVertexArray(0);D;
252 }
253
254 void initArrayBuffer1(GLuint *name) {
255   glGenBuffers(1, name);D;
256   glBindBuffer(GL_ARRAY_BUFFER, *name);D; {
257     glBufferData(GL_ARRAY_BUFFER, sizeof(point) * VertexCount, NULL, GL_DYNAMIC_COPY);D;
258   } glBindBuffer(GL_ARRAY_BUFFER, 0);D;
259 }  
260
261 GLuint resetView() {
262   which = 0;
263   fprintf(stderr, "%+f + %+f i @ %f\t", cx, cy, r);
264   GLuint active2;
265   GLuint active = win_width * win_height;
266   if (active > 0) {
267     // Set the display viewport
268     flt a = flt(win_width) / flt(win_height);
269     MVP = glm::ortho(cx - r * a, cx + a * r, cy - r, cy + r);
270     glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);D; {
271       glViewport(0, 0, win_width, win_height);D;
272       glUseProgram(Init0ProgramName);D; {
273         glUniformMatrix4fv_(Init0UniformMVP, 1, GL_FALSE, &MVP[0][0]);D;
274         glBegin(GL_QUADS); {
275           glVertex2f_(cx - r * a, cy - r);
276           glVertex2f_(cx + r * a, cy - r);
277           glVertex2f_(cx + r * a, cy + r);
278           glVertex2f_(cx - r * a, cy + r);
279         } glEnd();D;
280       } glUseProgram(0);D;
281       // copy to Pong
282       glBindBuffer(GL_PIXEL_PACK_BUFFER, PingPongArrayBufferName[1]);D; {
283         glReadPixels(0, 0, win_width, win_height, GL_RGBA, GL_FLOAT, 0);D;
284       } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);D;
285     } glBindFramebuffer(GL_FRAMEBUFFER, 0);D;
286     // transform to Ping
287     glEnable(GL_RASTERIZER_DISCARD);D; {
288       glUseProgram(Init1ProgramName);D; {
289         glBindVertexArray(InitVertexArrayName);D; {
290           glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, PingPongArrayBufferName[0]);D; {
291             glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, Query);D; {
292               glBeginTransformFeedback(GL_POINTS);D; {
293                 glDrawArrays(GL_POINTS, 0, active);D;
294               } glEndTransformFeedback();D;
295             } glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);D;
296             glGetQueryObjectuiv(Query, GL_QUERY_RESULT, &active2);D;
297           } glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);D;
298         } glBindVertexArray(0);D;
299       } glUseProgram(0);D;
300     } glDisable(GL_RASTERIZER_DISCARD);D;
301   }
302   return active;
303 }
304
305 void initArrayBuffer()
306 {
307   initArrayBuffer1(&PingPongArrayBufferName[0]);D;
308   initArrayBuffer1(&PingPongArrayBufferName[1]);D;
309 }
310
311 int calculate1(GLuint active, int pass) {
312   if (active <= 0) return 0;
313   GLuint kept1 = 0;
314   GLuint kept2 = 0;
315   glEnable(GL_RASTERIZER_DISCARD);D; {
316     glUseProgram(StepProgramName);D; {
317       glUniform1f_(StepUniformPixelSpacing, r * sqrt(2.0) / win_height);D;
318       glBindVertexArray(PingPongVertexArrayName[which]);D; {
319         // pass 1
320         glUniform1f_(StepUniformPlusMinus,  1);D;
321         glUniform1f_(StepUniformHueShift,   pi * pow(0.5, pass));D;
322         glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, PingPongArrayBufferName[1 - which]);D; {
323           glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, Query);D; {
324             glBeginTransformFeedback(GL_POINTS);D; {
325               glDrawArrays(GL_POINTS, 0, active);D;
326             } glEndTransformFeedback();D;
327                 } glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);D;
328                 glGetQueryObjectuiv(Query, GL_QUERY_RESULT, &kept1);D;
329         } glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);D;
330         // pass 2
331         glUniform1f_(StepUniformPlusMinus, -1);D;
332         glUniform1f_(StepUniformHueShift,   0);D;
333         glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, PingPongArrayBufferName[1 - which], kept1 * sizeof(point), (VertexCount - kept1) * sizeof(point));D; {
334           glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, Query);D; {
335             glBeginTransformFeedback(GL_POINTS);D; {
336               glDrawArrays(GL_POINTS, 0, active);D;
337             } glEndTransformFeedback();D;
338           } glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);D;
339           glGetQueryObjectuiv(Query, GL_QUERY_RESULT, &kept2);D;
340         } glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);D;
341       } glBindVertexArray(0);D;
342     } glUseProgram(0);
343     which = 1 - which;
344   } glDisable(GL_RASTERIZER_DISCARD);D;
345   return kept1 + kept2;
346 }
347
348 void draw(GLuint active) {
349   glViewport(0, 0, win_width, win_height);D;
350   glEnable(GL_BLEND); {
351     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
352     glUseProgram(DrawProgramName);D; {
353       glUniformMatrix4fv_(DrawUniformMVP, 1, GL_FALSE, &MVP[0][0]);D;
354       glUniform1f_(DrawUniformPixelSpacing, r * sqrt(2.0) / win_height);D;
355       glBindVertexArray(PingPongVertexArrayName[which]);D; {
356         glDrawArrays(GL_POINTS, 0, active);D;
357       } glBindVertexArray(0);D;
358     } glUseProgram(0);D;
359   } glDisable(GL_BLEND);
360   glutSwapBuffers();
361 }
362
363 GLuint calculate(GLuint active) {
364   int pass = 0;
365   while (2 * active <= GLuint(VertexCount) && pass < 256) {
366     active = calculate1(active, pass);
367     pass += 1;
368   }
369   fprintf(stderr, "%9d\t%d\n", active, pass);
370   return active;
371 }
372
373 void render() {
374   GLuint active = 0;
375   active = resetView();
376   active = calculate(active);
377   draw(active);
378 }
379
380 bool begincb() {
381   bool Validated = true;
382 /*
383         Validated = Validated && glf::checkGLVersion(major, minor);
384         Validated = Validated && glf::checkExtension("GL_ARB_viewport_array");
385         Validated = Validated && glf::checkExtension("GL_ARB_separate_shader_objects");
386 */
387
388   glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE);D;
389   glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);D;
390   glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);D;
391
392   glGenQueries(1, &Query);D;
393   initProgram();
394   initArrayBuffer();
395   initVertexArray();
396
397   glGenTextures(1, &Texture2DName);D;
398   glBindTexture(GL_TEXTURE_2D, Texture2DName);D;
399   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);D;
400   glBindTexture(GL_TEXTURE_2D, 0);D;
401   glGenFramebuffers(1, &FramebufferName);D;
402   glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);D;
403   glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, Texture2DName, 0);D;
404   GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
405   glDrawBuffers(1, DrawBuffers);D;
406   if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
407     fprintf(stderr, "frame buffer %d\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
408   }
409   glBindFramebuffer(GL_FRAMEBUFFER, 0);D;
410   return Validated;
411 }
412
413 void endcb()
414 {
415 /* // FIXME
416         glDeleteVertexArrays(1, &TransformVertexArrayName);
417         glDeleteBuffers(1, &TransformArrayBufferName);
418         glDeleteProgram(TransformProgramName);
419         
420         glDeleteVertexArrays(1, &FeedbackVertexArrayName);
421         glDeleteBuffers(1, &FeedbackArrayBufferPositionName);
422         glDeleteBuffers(1, &FeedbackArrayBufferColorName);
423         glDeleteProgram(FeedbackProgramName);
424 */
425         glDeleteQueries(1, &Query);D;
426 }
427
428 void displaycb(void) {
429   glClear(GL_COLOR_BUFFER_BIT);
430   render();
431   glutSwapBuffers();
432 }
433
434 void reshapecb(int w, int h) {
435   win_width = w;
436   win_height = h;
437   glutPostRedisplay();
438 }
439
440 void mousecb(int button, int state, int x, int y) {
441   if (state == GLUT_DOWN) {
442     if (button == GLUT_LEFT_BUTTON) {
443       cx += 2 * r * (x -  win_width / 2.0) / win_height;
444       cy += 2 * r * (win_height / 2.0 - y) / win_height;
445       r  *= 0.5;
446       glutPostRedisplay();
447     } else if (button == GLUT_MIDDLE_BUTTON) {
448       cx += 2 * r * (x -  win_width / 2.0) / win_height;
449       cy += 2 * r * (win_height / 2.0 - y) / win_height;
450       r  *= 1.0;
451       glutPostRedisplay();
452     } else if (button == GLUT_RIGHT_BUTTON) {
453       cx += 2 * r * (x -  win_width / 2.0) / win_height;
454       cy += 2 * r * (win_height / 2.0 - y) / win_height;
455       r  *= 2.0;
456       glutPostRedisplay();
457     }
458   }
459 }
460 void keyboardcb(unsigned char key, int x, int y) { if (key == 27) { exit(0); } }
461 void closecb() { exit(0); }
462 int main(int argc, char* argv[]) {
463   glutInitWindowSize(win_width, win_height);
464   glutInit(&argc, argv);
465   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
466
467   int WindowHandle = glutCreateWindow(argv[0]);
468   glewInit();
469   glutDestroyWindow(WindowHandle);
470
471   glutInitContextVersion(major, minor);
472   if (100 * major + 10 * minor >= 320) {
473     glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
474     glutInitContextFlags(GLUT_FORWARD_COMPATIBLE | GLUT_DEBUG);
475   }
476   glutCreateWindow(argv[0]);
477
478   if (! begincb()) { return 1; }
479
480   glutDisplayFunc(displaycb); 
481   glutReshapeFunc(reshapecb);
482   glutMouseFunc(mousecb);
483   glutKeyboardFunc(keyboardcb);
484   glutCloseFunc(closecb);
485   atexit(endcb);
486   glutMainLoop();
487   return 0;
488 }