mightymandel v16

GPU-based Mandelbrot set explorer

HACKING

Tips for hacking on mightmandel.

Documentation

There are some source code comments formatted for the doxygen tool. To extract the documentation (include the user documentation) to HTML:

make -C doc

A good place to start browsing is then:

doc/mightymandel/index.html

Contributing

Read man gittutorial first if you're not familiar with git.

How to contribute your changes (bug fixes, new features, ...):

git checkout master
git pull
git checkout -b my-new-stuff
# edit files, make changes
git add your-changed-files
git commit
# write a short description, the first line is the most important
git format-patch master
# then email the patches as attachments

Try to split each distinct set of changes into different commits (eg: a bug fix in one file and a new feature in another file should be two commits). On the other hand, changes in multiple files for the same bug-fix or feature should be in one commit. Make sure it compiles before you commit, and preferably make sure it runs and does the right things without breaking other stuff.

Alternatively to git format-patch and emailing, make your repository available online. https://gitorious.org has tools for forking repositories and submitting merge requests, though I've not used them much so can't offer any tips.

Try to keep to the code style of the rest of the code:

  • two space indent
  • no hard tabs
  • no trailing spaces at line endings
  • opening { on the same line as if/while etc
  • log_message usage where output is needed (eg: for debugging)
  • // comments not /* comments */, except for doxygen blocks
  • documentation should fit in 80 columns, code is free to go longer

And unlike the rest of the code (so far), try to add descriptive comments: high-level things, like what the function does, not low level things like "add a to b giving c" - admittedly mpfr can be somewhat asm-like, but better to group a few lines together with a single comment with an expression.

See the BUGS and TODO files for ideas of things that could be worked on. Some things are quite easy, like adding translation support:

https://gitorious.org/maximus/mightymandel/commit/7f11b163b4be64f4f3c24a523b45a142c80e6fb1

Some things are more involved, like adding rotation support (it would require a lot of small changes in a lot of files, all needing to be consistent). And other things are really hard, like making de/no-de switching work at runtime.

Miscellaneous

Use <stdbool.h> for bool instead of int where appropriate.

Add a D; after each line with an OpenGL call. This macro is defined in mightymandel.h and logs any OpenGL errors, with flood control.

Before release, or after large changes, run the test suite and (if releasing) update the TESTING file.

Code Atlas

Program components:

component description
mightymandel main program GUI using glfw3
startup parse command line arguments
parse parse parameter file formats
render higher-level details of rendering
record image output
interact everything to make GUI user input work
tiling handle state for tiled rendering
zoom handle state for zoom sequence rendering

OpenGL rendering:

component description
fp32 plain rendering with single precision floating point
fp64 plain rendering with double precision floating point
fpxx perturbation rendering with double precision floating point
colour take the raw results data and colour it to rgb
fillc fill an image with initial coordinates and copy to VBO
init set up initial in-progress data from initial coordinates
approx series approximation initialization
step perform some iteration calculations on in-progress data
escaped filter in-progress data and emit escapees as raw results
unescaped filter in-progress data and emit non-escapees

Glitch Correction:

component description
atom find periods and atoms (hyperbolic components, minibrots)
blob_set store a collection of blobs (glitched regions)
find_ref detect glitched regions and find new reference points
ref_set store a collection of reference points

Utilities:

component description
complex simple wrappers around mpfr for easier complex arithmetic
logging don't use printf for debugging or output, use this instead
shader compile shaders from GLSL and link them into programs
utility misc stuff

Rendering Overview

The high-level process is slightly confused in the code due to a callback system in render.c (which is a legacy from old versions using gtk but still there to maintain user interface responsiveness). It looks like this for fpxx (fp32 and fp64 are broadly similar with some steps removed):

  • find a central minibrot to use as a reference, or else the center pixel
  • repeat until 'find_ref' (called at end) reports done
    • for each slice
      • 'fillc' creates a texture with coordinates (and the old iterations and error count at that pixel), which is copied to a vertex buffer
      • 'init' processes that into a full structure for iteration calculations (non-erroneous escaped pixels are filtered out to avoid recomputation)
      • 'approx' computes series approximation coefficients and sets the final values as uniforms, the shader updates the iteration calculation data to the skipped iterations
      • repeat until 'completion' reports done
        • 'step' performs a block of calculations on each pixel in the buffer
        • 'escaped' cooks any escaped pixels into a texture for colouring
        • 'unescaped' compacts the unescaped pixels tightly in memory

Most of the computation uses a ping-pong technique between VBOs with transform feedback. The final step (to display the image) uses the 'colour' module, which uses a fragment shader to colour each pixel in the raw iteration texture.

v15 fixed a few bugs in the ping-pong VBO handling (the extra approx step was confusing things and made it tricky to get all of fp32/fp64/fpxx working at the same time), and also a stupid bug where the active count (number of unescaped pixels being iterated) wasn't updated correctly leading to massive corruption.

History

Previously (v14 and earlier) mightymandel had a lot of shared mutable global state. This meant programming required keeping track of which bits were used where, and when it was safe (or even expected) to modify them, together with a lot of undocumented invariants that when messed up would cause weird issues.

This was hell.

After v14 I refactored the code to use much less shared mutable global state. This does mean copying state from one place to another more often, but it's now done in a controlled way at specific points in the control flow, which makes it a lot safe and easier to understand.

Structures are generally visible in headers, but consider them read-only apart from in code within the corresponding implementation file. Visible structures allow other headers to make composite structures, and if used read-only avoids having to write a bunch of getter function boilerplate.

Previously (v14 and earlier) mightymandel control flow was mostly based on a callback architecture. When one task was done it would set an idle function pointer to the next task, and the main loop would just call the idle pointer repeatedly. This lead to obvious headaches working out what was going on, and the GUI needed to set flags to let the idle function tasks know that data it used had changed and the task was no longer needed. Part of this is a legacy from earlier versions implemented using glut and gtk, and the other part is that tasks need to be broken up into small chunks to keep the GUI responsive to user input.

After v14 the main loop is a lot saner. Callbacks from user interface interactions modify a data structure in interact.c. After the main loop polls for events (which makes glfw3 call callbacks), it checks the "updated" flag in the data structure, which determines if rendering should be restarted with new viewing parameters. It also copies the visualisation parameters at this point which don't need rendering to be restarted (things like glitch visualisation and colouring weight).

There are also distinct modes of operation ( –one-shot –tile –zoom etc), in place of the previous mess of different flags in different structures and tests all over the place.