mightymandel v16

GPU-based Mandelbrot set explorer

render.c
Go to the documentation of this file.
1 // mightymandel -- GPU-based Mandelbrot Set explorer
2 // Copyright (C) 2012,2013,2014,2015 Claude Heiland-Allen
3 // License GPL3+ http://www.gnu.org/licenses/gpl.html
4 
10 #include <math.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15 
16 #include "render.h"
17 #include "utility.h"
18 #include "find_ref.h"
19 #include "atom.h"
20 #include "logging.h"
21 #include "mightymandel.h"
22 #include "image.h"
23 #include "stopwatch.h"
24 #include "poll.h"
25 #include "texture.h"
26 
27 const char *render_method_name[3] = {
28  "fp32",
29  "fp64",
30  "fpxx"
31 };
32 
34  memset(o, 0, sizeof(struct render_options));
35  mpfr_init2(o->centerx, 53);
36  mpfr_init2(o->centery, 53);
37  mpfr_init2(o->radius, 53);
38 }
39 
41  mpfr_clear(o->centerx);
42  mpfr_clear(o->centery);
43  mpfr_clear(o->radius);
44 }
45 
46 void render_options_set_location(struct render_options *o, const mpfr_t cx, const mpfr_t cy, const mpfr_t r) {
47  int bits = pxbits(r, o->height);
48  if (bits < 24) {
50  } else if (bits < 53) {
52  } else {
54  }
55  if (! FP64) {
57  }
58  mpfr_set_prec(o->centerx, mpfr_get_prec(cx));
59  mpfr_set_prec(o->centery, mpfr_get_prec(cy));
60  mpfr_set_prec(o->radius, mpfr_get_prec(r));
61  mpfr_set(o->centerx, cx, MPFR_RNDN);
62  mpfr_set(o->centery, cy, MPFR_RNDN);
63  mpfr_set(o->radius, r, MPFR_RNDN);
64 }
65 
66 void render_options_copy(struct render_options *o, const struct render_options *p) {
67  o->method = p->method;
68  o->calculate_de = p->calculate_de;
69  o->weight = p->weight;
70  o->max_glitch = p->max_glitch;
71  o->max_blob = p->max_blob;
72  o->sharpness = p->sharpness;
73  o->show_de = p->show_de;
76  o->width = p->width;
77  o->height = p->height;
78  o->win_width = p->win_width;
79  o->win_height = p->win_height;
81  o->slice = p->slice;
82  mpfr_set_prec(o->centerx, mpfr_get_prec(p->centerx));
83  mpfr_set_prec(o->centery, mpfr_get_prec(p->centery));
84  mpfr_set_prec(o->radius, mpfr_get_prec(p->radius));
85  mpfr_set(o->centerx, p->centerx, MPFR_RNDN);
86  mpfr_set(o->centery, p->centery, MPFR_RNDN);
87  mpfr_set(o->radius, p->radius, MPFR_RNDN);
88 }
89 
90 void render_begin(struct render *s) {
91  if (! s->begun) {
92  memset(s, 0, sizeof(struct render));
95 
96  s->refs = 0;
97  s->blobs = 0;
98 
99  s->rgb_tex = 0;
100  s->tex = 0;
101  s->tex2 = 0;
102  s->fbo = 0;
103  s->query = 0;
104  s->vbo[0] = 0;
105  s->vbo[1] = 0;
106  s->active_count = 0;
107  s->escaperadius2 = 65536.0 * 8.0 * log(2.0);
108 
109  glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE);glGetError();D;
110  glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);D;
111  glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);glGetError();D;
112  glGenTextures(1, &s->rgb_tex);D;
113  glActiveTexture(GL_TEXTURE0 + TEX_RGB);
114  glBindTexture(GL_TEXTURE_2D, s->rgb_tex);D;
115  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);D;
116  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);D;
117  glGenTextures(1, &s->tex);D;
118  glActiveTexture(GL_TEXTURE0 + TEX_RAW);
119  glBindTexture(GL_TEXTURE_2D, s->tex);D;
120  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);D;
121  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);D;
122  glGenTextures(1, &s->tex2);D;
123  glActiveTexture(GL_TEXTURE0 + TEX_FILLC);
124  glBindTexture(GL_TEXTURE_2D, s->tex2);D;
125  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);D;
126  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);D;
127  glGenFramebuffers(1, &s->fbo);D;
128  glGenQueries(1, &s->query);D;
129  glGenBuffers(2, s->vbo);D;
130 
133 
138 
139  if (FP64) {
144 
150  }
151 
153 
154  s->idle.f = 0;
155 
156  s->begun = 1;
157  }
158 }
159 
160 void render_end(struct render *s) {
163 
164  glDeleteTextures(1, &s->rgb_tex);D;
165  glDeleteTextures(1, &s->tex);D;
166  glDeleteTextures(1, &s->tex2);D;
167  glDeleteFramebuffers(1, &s->fbo);D;
168  glDeleteQueries(1, &s->query);D;
169  glDeleteBuffers(2, s->vbo);D;
170 
173 
178 
179  if (FP64) {
184 
190  }
191 
193 }
194 
195 static void fpX_start(struct render *s);
196 bool render_reshape(struct render *s, int new_width, int new_height, int new_slice, GLsizei *bytes_allocated) {
197  bool reshape = (s->options.width != new_width) || (s->options.height != new_height) || (s->options.slice != new_slice);
198  if (reshape) {
199 #define CHECK assert(vbo_bytes > 0 && "--size is too big for my tiny GLsizei, try increasing --slice")
200  assert(sizeof(double) == 8 && "what kind of obscure system are you running on?");
201  GLsizei vbo_bytes = (new_width >> new_slice) * (new_height >> new_slice); CHECK;
202  vbo_bytes *= (DE ? 9 : 7); CHECK;
203  vbo_bytes <<= 1; CHECK;
204  vbo_bytes <<= 1; CHECK;
205  vbo_bytes <<= 1; CHECK;
206  for (int i = 0; i < 2; ++i) {
207  glBindBuffer(GL_ARRAY_BUFFER, s->vbo[i]);D;
208  glBufferData(GL_ARRAY_BUFFER, vbo_bytes, 0, GL_DYNAMIC_COPY);D;
209  glBindBuffer(GL_ARRAY_BUFFER, 0);D;
210  }
211  GLsizei tex_bytes = 0;
212  glActiveTexture(GL_TEXTURE0 + TEX_RGB);D;
213  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, new_width, new_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);D;
214  tex_bytes += new_width * new_height * 4;
215  glActiveTexture(GL_TEXTURE0 + TEX_RAW);D;
216  glTexImage2D(GL_TEXTURE_2D, 0, DE ? GL_RGB32F : GL_RG32F, new_width, new_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);D;
217  tex_bytes += new_width * new_height * 4 * (DE ? 3 : 2);
218  glActiveTexture(GL_TEXTURE0 + TEX_FILLC);D;
219  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, new_width >> new_slice, new_height >> new_slice, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);D;
220  tex_bytes += (new_width >> new_slice) * (new_height >> new_slice) * 4 * 4;
221  if (bytes_allocated) {
222  *bytes_allocated = 2 * vbo_bytes + tex_bytes;
223  }
224  }
225  return reshape;
226 }
227 
228 static bool render_update_options(struct render *s, const struct render_options *o) {
229  bool need_to_restart_rendering = false;
230  if (s->options.method != o->method) { need_to_restart_rendering = true; }
231  if (s->options.calculate_de > o->calculate_de) { need_to_restart_rendering = true; }
232  if (s->options.width != o->width) { need_to_restart_rendering = true; }
233  if (s->options.height != o->height) { need_to_restart_rendering = true; }
234  if (! mpfr_equal_p(s->options.centerx, o->centerx)) { need_to_restart_rendering = true; }
235  if (! mpfr_equal_p(s->options.centery, o->centery)) { need_to_restart_rendering = true; }
236  if (! mpfr_equal_p(s->options.radius, o->radius)) { need_to_restart_rendering = true; }
237  if (render_reshape(s, o->width, o->height, o->slice, 0)) { need_to_restart_rendering = true; }
239  return need_to_restart_rendering;
240 }
241 
242 void render_start(struct render *s, const struct render_options *o) {
244  s->pass = 0;
245  s->slice_n = 0;
246  s->timeout = false;
247  s->slice_done = false;
248  s->done = false;
249  s->all_done = false;
251  s->idle.f = fpX_start;
252  debug_message("render_start()\n");
253  log_message(LOG_INFO, "pass: %d\n", s->pass + 1);
254 }
255 
256 void render_cont(struct render *s) {
257  if (s->options.method == render_method_fpxx && s->all_done) {
258  s->idle.f = 0;
259  return;
260  }
261  s->pass++;
262  s->slice_n = 0;
263  s->idle.f = fpX_start;
264  debug_message("render_cont()\n");
265  log_message(LOG_INFO, "pass: %d\n", s->pass + 1);
266 }
267 
268 void render_next_slice(struct render *s) {
269  s->idle.f = fpX_start;
270  s->slice_n += 1;
271  if (s->slice_n == (1 << (s->options.slice << 1))) {
272  s->done = true;
273  s->idle.f = 0;
274  }
275 }
276 
277 #define PINGPONG(fp,FP) \
278 void fp##_pingpong(struct render *s) { \
279  if (! (s->active_count == 0)) { \
280  fp##_step_do(&s->fp##_step, &s->active_count, s->vbo[1], s->query); \
281  GLuint unescaped = 0; \
282  fp##_unescaped_do(&s->fp##_unescaped, &unescaped, s->active_count, s->vbo[0], s->query); \
283  int escaped = s->active_count - unescaped; \
284  if (escaped) { \
285  fp##_escaped_do(&s->fp##_escaped, s->active_count); \
286  } \
287  if (completion_update(&s->completion, unescaped, escaped, 1, s->options.sharpness)) { \
288  if (completion_done(&s->completion)) { \
289  render_next_slice(s); \
290  } \
291  } else { \
292  s->slice_done = false; \
293  s->idle.f = fp##_pingpong; \
294  } \
295  s->active_count = unescaped; \
296  } else { \
297  render_next_slice(s); \
298  } \
299 }
300 PINGPONG(fp32,FP32)
301 PINGPONG(fp64,FP64)
302 PINGPONG(fpxx,FPXX)
303 #undef PINGPONG
304 
305 void fp32_start(struct render *s) {
306  s->slice_done = false;
307  int slice_x = 0;
308  int slice_y = 0;
309  slice_table_coords(s->slice_table, s->options.slice, s->slice_n, &slice_x, &slice_y);
310  fp32_fillc_do(&s->fp32_fillc, s->vbo[1], s->fbo, s->tex, s->tex2, s->options.width, s->options.height, s->pass, s->options.slice, slice_x, slice_y);
313  fp32_step_start(&s->fp32_step, s->vbo[0], s->escaperadius2);
316  s->idle.f = fp32_pingpong;
317 }
318 
319 void fp64_start(struct render *s) {
320  s->slice_done = false;
321  int slice_x = 0;
322  int slice_y = 0;
323  slice_table_coords(s->slice_table, s->options.slice, s->slice_n, &slice_x, &slice_y);
324  fp32_fillc_do(&s->fp32_fillc, s->vbo[1], s->fbo, s->tex, s->tex2, s->options.width, s->options.height, s->pass, s->options.slice, slice_x, slice_y);
327  fp64_step_start(&s->fp64_step, s->vbo[0], s->escaperadius2);
330  s->idle.f = fp64_pingpong;
331 }
332 
333 bool fpxx_start_atom_abort(void *userdata) {
334  struct render *s = userdata;
335  switch (poll_ui(s->poll, false)) {
336  case poll_abort: s->aborted = true; return true;
337  case poll_timeout: s->aborted = true; s->timeout = true; return true;
338  case poll_display: return false;
339  case poll_continue: return false;
340  }
341  // poll returned some invalid enum...
342  s->aborted = true;
343  return true;
344 }
345 
346 void fpxx_start(struct render *s) {
347  s->idle.f = 0;
348  s->slice_done = false;
349  int slice_x = 0;
350  int slice_y = 0;
351  slice_table_coords(s->slice_table, s->options.slice, s->slice_n, &slice_x, &slice_y);
352  if (! s->refs) {
353  s->refs = ref_set_new();
354  if (s->blobs) { blob_set_delete(s->blobs); }
355  s->blobs = blob_set_new();
356  mpfr_t x, y, r;
357  mpfr_inits2(53, x, y, r, (mpfr_ptr) 0);
358  pixel_coordinate(x, y, s->options.width, s->options.height, s->options.centerx, s->options.centery, s->options.radius, s->options.width / 2.0 + 0.5, s->options.height / 2.0 + 0.5);
359  mpfr_set(r, s->options.radius, MPFR_RNDN);
360  unsigned int period = boxperiod(x, y, r, 100000, s, fpxx_start_atom_abort); // FIXME hardcoded maxperiod
361  if (s->aborted) {
362  mpfr_clears(x, y, r, (mpfr_ptr) 0);
363  return;
364  }
365  if (period > 0) {
366  muatom(period, x, y, r, s, fpxx_start_atom_abort);
367  if (s->aborted) {
368  mpfr_clears(x, y, r, (mpfr_ptr) 0);
369  return;
370  }
371  // may fail, but if it does we just use existing refx,refy anyway
372  // but it might return NaN or inf if it fails really badly
373  if (! (mpfr_number_p(x) && mpfr_number_p(y))) {
374  pixel_coordinate(x, y, s->options.width, s->options.height, s->options.centerx, s->options.centery, s->options.radius, s->options.width / 2.0 + 0.5, s->options.height / 2.0 + 0.5);
375  }
376  }
377  if (! ref_set_contains(s->refs, x, y)) {
378  ref_set_insert(s->refs, x, y);
379  }
380  mpfr_clears(x, y, r, (mpfr_ptr) 0);
381  }
382  fp32_fillc_do(&s->fp32_fillc, s->vbo[0], s->fbo, s->tex, s->tex2, s->options.width, s->options.height, s->pass, s->options.slice, slice_x, slice_y);
383  glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);D;
385  glBindFramebuffer(GL_FRAMEBUFFER, 0);D;
386  if (s->pass == 0) {
388  if (s->slice_n == 0) {
392  }
393  } else {
395  if (s->slice_n == (1 << (s->options.slice << 1))) {
397  if (completion_done(&s->completion2)) {
398  s->all_done = true;
399  return;
400  }
401  }
403  }
404  }
405  mpfr_t zx, zy, dzx, dzy;
406  mpfr_inits2(53, zx, zy, dzx, dzy, (mpfr_ptr) 0);
407  fpxx_approx_do(&s->fpxx_approx, &s->active_count, s->vbo, s->query, zx, zy, dzx, dzy, s->pass, s->options.radius, s->refs->set->x, s->refs->set->y, s->options.series_approx, s->slice_n == 0, s, fpxx_start_atom_abort);
408  if (s->aborted) {
409  mpfr_clears(zx, zy, dzx, dzy, (mpfr_ptr) 0);
410  return;
411  }
412  fpxx_step_start(&s->fpxx_step, s->vbo[0], s->escaperadius2, zx, zy, dzx, dzy, s->refs->set->x, s->refs->set->y, s->slice_n == 0);
413  mpfr_clears(zx, zy, dzx, dzy, (mpfr_ptr) 0);
416  s->idle.f = fpxx_pingpong;
417 }
418 
419 void fpX_start(struct render *s) {
420  log_message(LOG_DEBUG, "slice: %d\n", s->slice_n);
421  switch (s->options.method) {
422  case render_method_fp32: fp32_start(s); break;
423  case render_method_fp64: fp64_start(s); break;
424  case render_method_fpxx: fpxx_start(s); break;
425  }
426  glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);D;
427 }
428 
429 bool render_calculate(struct render *s, const struct render_options *o, struct poll *poll) {
430  s->poll = poll;
431  if (render_update_options(s, o)) {
433  debug_message("render_calculate() reset time %f\n", stopwatch_elapsed(s->render_time));
434  if (s->refs) {
435  ref_set_delete(s->refs);
436  s->refs = 0;
437  }
438  render_start(s, o);
439  }
440  bool more = s->idle.f && ! s->aborted;
441  while (more) {
442  s->idle.f(s);
443  more = s->idle.f && ! s->aborted;
444  enum poll_result result = poll_ui(poll, false);
445  switch (result) {
446  case poll_continue: break;
447  case poll_abort: s->aborted = true; return false;
448  case poll_display: more |= render_display(s, o, poll); break;
449  case poll_timeout: s->timeout = true; render_display(s, o, poll); return false;
450  }
451  }
452  return more;
453 }
454 
455 bool render_display(struct render *s, const struct render_options *o, struct poll *poll) {
456  double iterations, exterior, interior, glitch;
457  enum result_t result;
458  debug_message("render_display() %p\n", s->idle.f);
459 #define COLOUR do{ \
460  fp32_colour_do(&s->fp32_colour, o->win_width, o->win_height, o->width, o->height, s->timeout || s->all_done, s->pass > 0 || s->all_done, o->show_glitches, pow(0.5, o->weight), o->slice, s->slice_n); \
461  glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);D; \
462  poll_swap_buffers(poll); \
463 }while(0)
464  COLOUR;
465  if (s->timeout) {
466  log_message(LOG_INFO, "timeout\n");
467  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
468  result = image_result(o->width, o->height, &iterations, &exterior, &interior, &glitch);
469  log_result(o->filename, result_timeout, o->method, iterations, exterior, interior, glitch, o->method == render_method_fpxx ? s->pass + 1 : 0);
470  s->slice_done = true;
471  s->done = true;
472  s->all_done = true;
473  s->aborted = true;
474  s->idle.f = 0;
475  return false;
476  }
477  if (! s->idle.f) {
478  debug_message("render method: %d\n", o->method);
479  if (o->method == render_method_fpxx && s->done && ! s->all_done) {
480  if (s->pass < 1000) { // FIXME hardcoded max passes
481  switch (find_ref(s->refs, s->blobs, o->width, o->height, o->centerx, o->centery, o->radius, o->max_glitch, o->max_blob)) {
482  case find_ref_failure:
483  log_message(LOG_INFO, "failed to find reference, after passes: %d\n", s->pass + 1);
484  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
485  image_result(o->width, o->height, &iterations, &exterior, &interior, &glitch);
486  log_result(o->filename, result_glitch, o->method, iterations, exterior, interior, glitch, s->pass + 1);
487  s->done = true;
488  s->all_done = true;
489  COLOUR;
490  return false;
491  case find_ref_complete:
492  image_result(o->width, o->height, &iterations, &exterior, &interior, &glitch);
493  if (s->fpxx_approx.too_deep) {
494  log_message(LOG_INFO, "possibly errors in image, took passes: %d\n", s->pass + 1);
495  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
496  result = result_deep;
497  } else {
498  log_message(LOG_INFO, "no more errors in image, took passes: %d\n", s->pass + 1);
499  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
500  result = result_ok;
501  }
502  log_result(o->filename, result, o->method, iterations, exterior, interior, glitch, s->pass + 1);
503  s->done = true;
504  s->all_done = true;
505  COLOUR;
506  return false;
507  case find_ref_success:
508  debug_message("found another reference\n");
509  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
510  render_cont(s);
511  s->slice_done = false;
512  s->done = false;
513  return true;
514  }
515  } else {
516  log_message(LOG_INFO, "too many passes: %d\n", s->pass);
517  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
518  result = image_result(o->width, o->height, &iterations, &exterior, &interior, &glitch);
519  log_result(o->filename, result, o->method, iterations, exterior, interior, glitch, s->pass);
520  s->done = true;
521  s->all_done = true;
522  COLOUR;
523  return false;
524  }
525  } else {
526  if (! s->all_done) {
527  log_message(LOG_INFO, "elapsed time: %f\n", stopwatch_elapsed(s->render_time));
528  result = image_result(o->width, o->height, &iterations, &exterior, &interior, &glitch);
529  log_result(o->filename, result, o->method, iterations, exterior, interior, glitch, 0);
530  s->done = true;
531  s->all_done = true;
532  }
533  return false;
534  }
535  }
536  return s->idle.f;
537 }
538 
540  render->aborted = false;
541  do {
542  while (render_calculate(render, render_options, poll)) {
543  // spin
544  }
545  if (render->aborted) {
546  break;
547  }
548  } while (render_display(render, render_options, poll));
549  if (render->aborted) {
550  if (render->timeout) {
551  return render_complete;
552  }
553  return render_aborted;
554  } else {
555  return render_complete;
556  }
557 }