glDrawArraysInstanced not available on my laptop
[ruff:ruff-render-gl.git] / src / ruff-render-gl.cc
1 /*
2 ruff-render-gl -- GPU accelerated tile renderer for ruff
3 (C) 2011 Claude Heiland-Allen <claudiusmaximus@goto10.org>
4
5 based on:
6
7 // **********************************
8 // OpenGL Transform Feedback Separated
9 // 06/04/2010 - 22/06/2011
10 // **********************************
11 // Christophe Riccio
12 // ogl-samples@g-truc.net
13 // **********************************
14 // G-Truc Creation
15 // www.g-truc.net
16 // **********************************
17
18 */
19
20 #include <glf/glf.hpp>
21
22 namespace
23 {
24   std::string const SAMPLE_NAME = "OpenGL Mandelbrot Set";
25   std::string const VERT_SHADER_SOURCE_ITERATE("iterate.vert");
26   std::string const VERT_SHADER_SOURCE_ESCAPE ("escape.vert");
27   std::string const GEOM_SHADER_SOURCE_ESCAPE ("escape.geom");
28   std::string const VERT_SHADER_SOURCE_COOK   ("cook.vert");
29   std::string const VERT_SHADER_SOURCE_COLOUR ("colour.vert");
30   std::string const FRAG_SHADER_SOURCE_COLOUR ("colour.frag");
31
32         int const SAMPLE_SIZE_WIDTH(256);
33         int const SAMPLE_SIZE_HEIGHT(256);
34         int const SAMPLE_MAJOR_VERSION(3);
35         int const SAMPLE_MINOR_VERSION(3);
36
37         glf::window Window(glm::ivec2(SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT));
38
39         GLsizei const VertexCount(SAMPLE_SIZE_WIDTH * SAMPLE_SIZE_HEIGHT);
40   GLuint iteratees = VertexCount;
41   bool firstDisplay = true;
42   int iterationcount = 0;
43   float cx = 0;
44   float cy = 0;
45   float r = 2;
46   float pixelspacing = 4.0f / SAMPLE_SIZE_WIDTH;
47
48   GLuint IterateProgramName(0);
49         GLuint IterateArrayBufferPositionName(0);
50         GLuint IterateArrayBufferColorName(0);
51   GLuint IterateVertexArrayName(0);
52
53   GLuint EscapeProgramName(0);
54         GLuint EscapeArrayBufferPositionName(0);
55         GLuint EscapeArrayBufferColorName(0);
56   GLuint EscapeVertexArrayName(0);
57   GLint  EscapeUniformEscape(0);
58
59   GLuint CookProgramName(0);
60         GLuint CookArrayBufferPositionName(0);
61         GLuint CookArrayBufferColorName(0);
62   GLuint CookVertexArrayName(0);
63   GLint  CookUniformIters(0);
64   GLint  CookUniformScale(0);
65
66   GLuint ColourProgramName(0);
67         GLuint ColourArrayBufferPositionName(0);
68         GLuint ColourArrayBufferColorName(0);
69   GLuint ColourVertexArrayName(0);
70   GLint  ColourUniformMVP(0);
71   GLint  ColourUniformInterior(0);
72   GLint  ColourUniformBorder(0);
73   GLint  ColourUniformExterior(0);
74
75         GLuint Query(0);
76
77 }//namespace
78
79 bool initProgram()
80 {
81         bool Validated = true;
82         
83         // Create program 'iterate'
84         //if(Validated)
85         {
86                 GLuint VertexShaderName = glf::createShader(GL_VERTEX_SHADER, VERT_SHADER_SOURCE_ITERATE);
87
88                 IterateProgramName = glCreateProgram();
89                 glAttachShader(IterateProgramName, VertexShaderName);
90                 glDeleteShader(VertexShaderName);
91
92                 GLchar const * Strings[] = {"block.Position", "block.Color"}; 
93                 glTransformFeedbackVaryings(IterateProgramName, 2, Strings, GL_SEPARATE_ATTRIBS); 
94                 glLinkProgram(IterateProgramName);
95
96                 Validated = Validated && glf::checkProgram(IterateProgramName);
97
98 /*
99                 // BUG AMD 10.12
100                 char Name[64];
101                 memset(Name, 0, 64);
102                 GLsizei Length(0);
103                 GLsizei Size(0);
104                 GLenum Type(0);
105
106                 glGetTransformFeedbackVarying(
107                         IterateProgramName,
108                         0,
109                         64,
110                         &Length,
111                         &Size,
112                         &Type,
113                         Name);
114
115                 Validated = Validated && (Size == 1) && (Type == GL_FLOAT_VEC4);
116 */
117         }
118
119
120         // Create program 'escape'
121         //if(Validated)
122         {
123                 GLuint VertexShaderName = glf::createShader(GL_VERTEX_SHADER, VERT_SHADER_SOURCE_ESCAPE);
124                 GLuint GeometryShaderName = glf::createShader(GL_GEOMETRY_SHADER, GEOM_SHADER_SOURCE_ESCAPE);
125
126                 EscapeProgramName = glCreateProgram();
127                 glAttachShader(EscapeProgramName, VertexShaderName);
128                 glAttachShader(EscapeProgramName, GeometryShaderName);
129                 glDeleteShader(VertexShaderName);
130                 glDeleteShader(GeometryShaderName);
131
132                 GLchar const * Strings[] = {"block.Position", "block.Color"}; 
133                 glTransformFeedbackVaryings(EscapeProgramName, 2, Strings, GL_SEPARATE_ATTRIBS); 
134                 glLinkProgram(EscapeProgramName);
135
136                 Validated = Validated && glf::checkProgram(EscapeProgramName);
137 /*
138                 // BUG AMD 10.12
139                 char Name[64];
140                 memset(Name, 0, 64);
141                 GLsizei Length(0);
142                 GLsizei Size(0);
143                 GLenum Type(0);
144
145                 glGetTransformFeedbackVarying(
146                         EscapeProgramName,
147                         0,
148                         64,
149                         &Length,
150                         &Size,
151                         &Type,
152                         Name);
153
154                 Validated = Validated && (Size == 1) && (Type == GL_FLOAT_VEC4);
155 */
156         }
157         // Get variables locations
158         //if(Validated)
159         {
160                 EscapeUniformEscape = glGetUniformLocation(EscapeProgramName, "escape");
161                 Validated = Validated && (EscapeUniformEscape >= 0);
162         }
163
164
165         // Create program 'cook'
166         //if(Validated)
167         {
168                 GLuint VertexShaderName = glf::createShader(GL_VERTEX_SHADER, VERT_SHADER_SOURCE_COOK);
169
170                 CookProgramName = glCreateProgram();
171                 glAttachShader(CookProgramName, VertexShaderName);
172                 glDeleteShader(VertexShaderName);
173
174                 GLchar const * Strings[] = {"block.Position", "block.Color"}; 
175                 glTransformFeedbackVaryings(CookProgramName, 2, Strings, GL_SEPARATE_ATTRIBS); 
176                 glLinkProgram(CookProgramName);
177
178                 Validated = Validated && glf::checkProgram(CookProgramName);
179 /*
180                 // BUG AMD 10.12
181                 char Name[64];
182                 memset(Name, 0, 64);
183                 GLsizei Length(0);
184                 GLsizei Size(0);
185                 GLenum Type(0);
186
187                 glGetTransformFeedbackVarying(
188                         CookProgramName,
189                         0,
190                         64,
191                         &Length,
192                         &Size,
193                         &Type,
194                         Name);
195
196                 Validated = Validated && (Size == 1) && (Type == GL_FLOAT_VEC4);
197 */
198         }
199         // Get variables locations
200         //if(Validated)
201         {
202                 CookUniformIters = glGetUniformLocation(CookProgramName, "iters");
203                 CookUniformScale = glGetUniformLocation(CookProgramName, "scale");
204                 Validated = Validated && (CookUniformIters >= 0) && (CookUniformScale >= 0);
205         }
206
207
208   // Create program 'colour'
209         //if(Validated)
210         {
211                 GLuint VertexShaderName = glf::createShader(GL_VERTEX_SHADER, VERT_SHADER_SOURCE_COLOUR);
212                 GLuint FragmentShaderName = glf::createShader(GL_FRAGMENT_SHADER, FRAG_SHADER_SOURCE_COLOUR);
213
214                 ColourProgramName = glCreateProgram();
215                 glAttachShader(ColourProgramName, VertexShaderName);
216                 glAttachShader(ColourProgramName, FragmentShaderName);
217                 glDeleteShader(VertexShaderName);
218                 glDeleteShader(FragmentShaderName);
219
220                 glLinkProgram(ColourProgramName);
221                 Validated = Validated && glf::checkProgram(ColourProgramName);
222         }
223         // Get variables locations
224         if(Validated)
225         {
226                 ColourUniformMVP = glGetUniformLocation(ColourProgramName, "MVP");
227                 ColourUniformInterior = glGetUniformLocation(ColourProgramName, "interior");
228                 ColourUniformBorder = glGetUniformLocation(ColourProgramName, "border");
229                 ColourUniformExterior = glGetUniformLocation(ColourProgramName, "exterior");
230
231                 Validated = Validated && (ColourUniformMVP >= 0) && (ColourUniformInterior >= 0) && (ColourUniformBorder >= 0) && (ColourUniformExterior >= 0);
232         }
233
234         return Validated && glf::checkError("initProgram");
235 }
236
237 void initVertexArray1(GLuint *name, GLuint position, GLuint color, const char *err) {
238         glGenVertexArrays(1, name);
239     glBindVertexArray(*name);
240                 glBindBuffer(GL_ARRAY_BUFFER, position);
241                 glVertexAttribPointer(glf::semantic::attr::POSITION, 4, GL_FLOAT, GL_FALSE, 0, 0);
242                 glBindBuffer(GL_ARRAY_BUFFER, color);
243                 glVertexAttribPointer(glf::semantic::attr::COLOR, 4, GL_FLOAT, GL_FALSE, 0, 0);
244                 glBindBuffer(GL_ARRAY_BUFFER, 0);
245                 glEnableVertexAttribArray(glf::semantic::attr::POSITION);
246                 glEnableVertexAttribArray(glf::semantic::attr::COLOR);
247         glBindVertexArray(0);
248         glf::checkError(err);
249 }
250
251 bool initVertexArray() {
252   glf::checkError("initVertexArray 0");
253   initVertexArray1(&IterateVertexArrayName, IterateArrayBufferPositionName, IterateArrayBufferColorName, "iva iterate");
254   initVertexArray1(&EscapeVertexArrayName , EscapeArrayBufferPositionName , EscapeArrayBufferColorName , "iva escape");
255   initVertexArray1(&CookVertexArrayName   , CookArrayBufferPositionName   , CookArrayBufferColorName   , "iva cook");
256   initVertexArray1(&ColourVertexArrayName , ColourArrayBufferPositionName , ColourArrayBufferColorName , "iva colour");
257         return glf::checkError("initVertexArray");
258 }
259
260 void initArrayBuffer1(GLuint *name, const char *err, void *data) {
261         glGenBuffers(1, name);
262   glBindBuffer(GL_ARRAY_BUFFER, *name);
263     glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * VertexCount, data, GL_DYNAMIC_COPY);
264         glBindBuffer(GL_ARRAY_BUFFER, 0);
265   glf::checkError(err);
266 }  
267
268 void resetView() {
269   glm::vec4 *positions = (glm::vec4 *) calloc(1, sizeof(glm::vec4) * VertexCount);
270   glBindBuffer(GL_ARRAY_BUFFER, IterateArrayBufferColorName);
271     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec4) * VertexCount, positions);
272   glBindBuffer(GL_ARRAY_BUFFER, 0);
273   glm::vec4 *p = positions;
274   for (int i = 0; i < SAMPLE_SIZE_WIDTH; ++i) {
275     float x = cx + (i - SAMPLE_SIZE_WIDTH/2) * r / (SAMPLE_SIZE_WIDTH/2);
276     for (int j = 0; j < SAMPLE_SIZE_HEIGHT; ++j) {
277       float y = cy + (j - SAMPLE_SIZE_HEIGHT/2) * r / (SAMPLE_SIZE_HEIGHT/2);
278       *p++ = glm::vec4(x, y, 0.0f, 1.0f);
279     }
280   }
281   glBindBuffer(GL_ARRAY_BUFFER, IterateArrayBufferPositionName);
282     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec4) * VertexCount, positions);
283   glBindBuffer(GL_ARRAY_BUFFER, 0);
284   free(positions);
285   iterationcount = 0;
286   iteratees = VertexCount;
287 }
288
289 bool initArrayBuffer()
290 {
291   glf::checkError("initArrayBuffer 0");
292   void *zero = calloc(1, sizeof(glm::vec4) * VertexCount);
293         initArrayBuffer1(&IterateArrayBufferPositionName, "iab iterate position", zero);
294         initArrayBuffer1(&IterateArrayBufferColorName, "iab iterate color", zero);
295   free(zero);
296         initArrayBuffer1(&EscapeArrayBufferPositionName, "iab escape position", NULL);
297         initArrayBuffer1(&EscapeArrayBufferColorName, "iab escape color", NULL);
298         initArrayBuffer1(&CookArrayBufferPositionName, "iab cook position", NULL);
299         initArrayBuffer1(&CookArrayBufferColorName, "iab cook color", NULL);
300         initArrayBuffer1(&ColourArrayBufferPositionName, "iab colour position", NULL);
301         initArrayBuffer1(&ColourArrayBufferColorName, "iab colour color", NULL);
302         return glf::checkError("initArrayBuffer");
303 }
304
305 void click(int button, int state, int x, int y) {
306   if (!state) {
307     cx += (x - SAMPLE_SIZE_WIDTH /2) * r / (SAMPLE_SIZE_WIDTH /2);
308     cy += (SAMPLE_SIZE_HEIGHT/2 - y) * r / (SAMPLE_SIZE_HEIGHT/2);
309     if (!button) {
310       r  /= 2;
311       pixelspacing /= 2;
312     } else {
313       r  *= 2;
314       pixelspacing *= 2;
315     }
316     resetView();
317   } 
318 }
319
320 bool begin()
321 {
322         bool Validated = true;
323         Validated = Validated && glf::checkGLVersion(SAMPLE_MAJOR_VERSION, SAMPLE_MINOR_VERSION);
324         Validated = Validated && glf::checkExtension("GL_ARB_viewport_array");
325         Validated = Validated && glf::checkExtension("GL_ARB_separate_shader_objects");
326
327   glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE);
328   glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);
329   glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);
330
331         glGenQueries(1, &Query);
332
333         if(Validated)
334                 Validated = initProgram();
335         if(Validated)
336                 Validated = initArrayBuffer();
337         if(Validated)
338                 Validated = initVertexArray();
339
340   resetView();
341   
342         return Validated && glf::checkError("begin");
343 }
344
345 bool end()
346 {
347 /* // FIXME
348         glDeleteVertexArrays(1, &TransformVertexArrayName);
349         glDeleteBuffers(1, &TransformArrayBufferName);
350         glDeleteProgram(TransformProgramName);
351         
352         glDeleteVertexArrays(1, &FeedbackVertexArrayName);
353         glDeleteBuffers(1, &FeedbackArrayBufferPositionName);
354         glDeleteBuffers(1, &FeedbackArrayBufferColorName);
355         glDeleteProgram(FeedbackProgramName);
356 */
357         glDeleteQueries(1, &Query);
358
359         return glf::checkError("end");
360 }
361
362 void timer1(int v) {
363   glutPostRedisplay();
364 }
365
366 void display()
367 {
368         // Compute the MVP (Model View Projection matrix)
369         glm::mat4 Projection = glm::ortho(cx - r, cx + r, cy - r, cy + r);
370         glm::mat4 View = glm::mat4(1.0f);
371         glm::mat4 Model = glm::mat4(1.0f);
372         glm::mat4 MVP = Projection * View * Model;
373
374         // Set the display viewport
375         glViewport(0, 0, Window.Size.x, Window.Size.y);
376
377   if (iterationcount == 0) {
378           // Clear color buffer with red
379         glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
380         glClear(GL_COLOR_BUFFER_BIT);
381         glf::checkError("firstDisplay");
382   }
383
384   // Disable rasterisation, vertices processing only!
385         glEnable(GL_RASTERIZER_DISCARD);
386
387         // iterate
388         {
389     glUseProgram(IterateProgramName);
390     // dst
391                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, EscapeArrayBufferPositionName);
392                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, EscapeArrayBufferColorName);
393     // src
394                 glBindVertexArray(IterateVertexArrayName);
395     // do it
396                 glBeginTransformFeedback(GL_POINTS);
397                 glDrawArrays(GL_POINTS, 0, iteratees);
398                 glEndTransformFeedback();
399                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
400                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, 0);
401                 glBindVertexArray(0);
402         glf::checkError("iterate");
403   }
404   // keep unescaped
405   GLuint unescaped = 0;
406         {
407     glUseProgram(EscapeProgramName);
408         glf::checkError("unescaped 1");
409     glUniform1i(EscapeUniformEscape, 0);
410         glf::checkError("unescaped 2");
411     // dst
412                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, IterateArrayBufferPositionName);
413         glf::checkError("unescaped 3");
414                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, IterateArrayBufferColorName);
415         glf::checkError("unescaped 4");
416     // src
417                 glBindVertexArray(EscapeVertexArrayName);
418         glf::checkError("unescaped 5");
419     // do it
420                 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, Query); 
421         glf::checkError("unescaped 6");
422                 glBeginTransformFeedback(GL_POINTS);
423         glf::checkError("unescaped 7");
424                         glDrawArrays(GL_POINTS, 0, iteratees);
425         glf::checkError("unescaped 8");
426                 glEndTransformFeedback();
427         glf::checkError("unescaped 9");
428                 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); 
429         glf::checkError("unescaped A");
430                 glGetQueryObjectuiv(Query, GL_QUERY_RESULT, &unescaped);
431         glf::checkError("unescaped");
432   }
433   // pass through escaped
434   GLuint escaped = 0;
435         {
436     glUseProgram(EscapeProgramName);
437     glUniform1i(EscapeUniformEscape, 1);
438     // dst
439                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, CookArrayBufferPositionName);
440                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, CookArrayBufferColorName);
441     // src
442                 glBindVertexArray(EscapeVertexArrayName);
443     // do it
444                 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, Query); 
445                 glBeginTransformFeedback(GL_POINTS);
446                         glDrawArrays(GL_POINTS, 0, iteratees);
447                 glEndTransformFeedback();
448                 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); 
449                 glGetQueryObjectuiv(Query, GL_QUERY_RESULT, &escaped);
450         glf::checkError("escaped");
451   }
452   // cook the escapees
453         {
454     glUseProgram(CookProgramName);
455     glUniform1f(CookUniformIters, ++iterationcount);
456     glUniform1f(CookUniformScale, pixelspacing);
457     // dst
458                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, ColourArrayBufferPositionName);
459                 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, ColourArrayBufferColorName);
460     // src
461                 glBindVertexArray(CookVertexArrayName);
462     // do it
463                 glBeginTransformFeedback(GL_POINTS);
464                         glDrawArrays(GL_POINTS, 0, escaped);
465                 glEndTransformFeedback();
466         glf::checkError("cook");
467   }
468
469   // re-enable drawing
470   glDisable(GL_RASTERIZER_DISCARD);
471
472   // colourize the cookeds
473         {
474                 glUseProgram(ColourProgramName);
475                 glUniformMatrix4fv(ColourUniformMVP, 1, GL_FALSE, &MVP[0][0]);
476                 glUniform3f(ColourUniformInterior, 1.0, 0.0, 0.0);
477                 glUniform3f(ColourUniformBorder  , 0.0, 0.0, 0.0);
478                 glUniform3f(ColourUniformExterior, 1.0, 1.0, 1.0);
479                 glBindVertexArray(ColourVertexArrayName);
480                 glDrawArrays(GL_POINTS, 0, escaped);
481         glf::checkError("colour");
482         }
483   if ((iterationcount % 1000) == 0) {
484     printf("%d\t%d\t%d\t%d\n", iterationcount, iteratees, escaped, unescaped);
485   }
486   iteratees = unescaped;
487
488         glf::checkError("display");
489   if ((iterationcount % 100) == 0) {
490         glf::swapBuffers();
491   }
492   glutTimerFunc(1, timer1, 1);
493   glutIdleFunc(0);
494   glutMouseFunc(click);
495 }
496
497 int main(int argc, char* argv[])
498 {
499         return glf::run(
500                 argc, argv,
501                 glm::ivec2(::SAMPLE_SIZE_WIDTH, ::SAMPLE_SIZE_HEIGHT), 
502                 ::SAMPLE_MAJOR_VERSION, 
503                 ::SAMPLE_MINOR_VERSION);
504 }