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