Skip to main content

Overview

Fractal rendering is computationally intensive, requiring millions of floating-point operations per frame. This guide explores the optimization strategies implemented in Fract’ol and techniques for balancing visual quality with performance.

Key Performance Parameters

Three critical parameters defined in fractol_init.c:21-28 control the quality-performance tradeoff:
void	data_init(t_fractol *fractol)
{
	fractol->escape_value = 4;
	fractol->iterations_definition = 30;
	fractol->shift_x = 0.0;
	fractol->shift_y = 0.0;
	fractol->zoom = 1;
}

Iteration Limit (iterations_definition)

Default Value: 30 The iteration limit is the most impactful parameter for both quality and performance.
1
How It Affects Performance
2
Each pixel requires up to iterations_definition iterations:
3
while (i < fractol->iterations_definition)
{
	z = sum_complex(sqare_complex(z), c);
	if ((z.x * z.x) + (z.y * z.y) > fractol->escape_value)
		return;  // Early exit
	i++;
}
4
Computation cost:
5
  • Single frame: 2000 × 1500 × iterations_definition operations
  • At 30 iterations: 90 million operations
  • At 100 iterations: 300 million operations
  • 6
    How It Affects Quality
    7
    Low iterations (10-30):
    8
  • Fast rendering
  • Lost detail near set boundaries
  • Blocky, less smooth gradients
  • Adequate for initial exploration
  • 9
    Medium iterations (50-100):
    10
  • Balanced quality/performance
  • Good detail in most regions
  • Smooth color transitions
  • Recommended for detailed viewing
  • 11
    High iterations (200+):
    12
  • Slow rendering
  • Maximum detail at deep zoom levels
  • Required for extreme magnifications
  • Necessary for publication-quality images
  • The optimal iteration count scales with zoom level. At 10x zoom, you may need 3x more iterations to maintain the same detail level.

    Runtime Adjustment

    Users can dynamically adjust iterations using keyboard controls (events.c:38-41):
    else if (keysym == XK_plus)
    	fractol->iterations_definition += 5;
    else if (keysym == XK_minus)
    	fractol->iterations_definition -= 5;
    
    Each adjustment triggers a complete re-render via fractol_render(fractol).

    Escape Value Optimization

    Default Value: 4 The escape value determines when we consider a point to have “escaped” to infinity:
    if ((z.x * z.x) + (z.y * z.y) > fractol->escape_value)
    

    Why 4 is Optimal

    For the quadratic map z = z² + c, if |z| > 2, the sequence is guaranteed to diverge to infinity. Since we compare the squared magnitude to avoid sqrt(), we use |z|² > 4.Using exactly 4 is optimal because:
    • Too low (< 4): False positives - some bounded points incorrectly marked as escaped
    • Too high (> 4): Wasted iterations checking points already proven to diverge
    Using squared magnitude instead of actual magnitude:Without optimization:
    if (sqrt(z.x * z.x + z.y * z.y) > 2.0)  // Expensive sqrt()
    
    Optimized version:
    if ((z.x * z.x) + (z.y * z.y) > 4)  // No sqrt() needed
    
    Performance gain: Approximately 30-40% faster rendering by eliminating 90 million sqrt() calls per frame.

    Zoom and Precision Considerations

    Zoom is controlled via mouse wheel events (events.c:46-60):
    int	mouse_handler(int button, int x, int y, t_fractol *fractol)
    {
    	if (button == Button4)
    		fractol->zoom *= 0.95;  // Zoom in (5% closer)
    	else if (button == Button5)
    		fractol->zoom *= 1.05;  // Zoom out (5% farther)
    	fractol_render(fractol);
    	return (0);
    }
    

    Coordinate Calculation with Zoom

    From fractol_render.c:45-46:
    z.x = (map(x, -2, +2, WIDTH) * fractol->zoom) + fractol->shift_x;
    z.y = (map(y, +2, -2, HEIGHT) * fractol->zoom) + fractol->shift_y;
    
    As zoom decreases (zooming in):
    • Smaller coordinate range examined
    • Higher effective resolution
    • More iterations needed for same visual detail

    Floating-Point Precision Limits

    C’s double type provides approximately 15-17 decimal digits of precision. At extreme zoom levels (zoom < 1e-13), you’ll encounter precision artifacts where the fractal appears pixelated or incorrect.
    Zoom thresholds:
    • zoom = 1.0: Default view, no precision issues
    • zoom = 0.01: 100x magnification, still precise
    • zoom = 1e-10: Near precision limits, may see artifacts
    • zoom < 1e-13: Significant precision degradation
    If you need deeper zoom capabilities, consider using arbitrary-precision libraries or specialized fractal software that implements perturbation theory.

    Rendering Pipeline Efficiency

    Sequential Pixel Processing

    The renderer processes pixels sequentially (fractol_render.c:63-81):
    void	fractol_render(t_fractol *fractol)
    {
    	int	x;
    	int	y;
    
    	y = 0;
    	while (y < HEIGHT)
    	{
    		x = 0;
    		while (x < WIDTH)
    		{
    			handle_pixel(x, y, fractol);
    			x++;
    		}
    		y++;
    	}
    	mlx_put_image_to_window(fractol->mlx_connection, fractol->mlx_window,
    		fractol->img.img_ptr, 0, 0);
    }
    

    Memory Access Optimization

    Pixel data is written directly to the image buffer using pointer arithmetic (fractol_render.c:15-21):
    static void	my_pixel_put(int x, int y, t_img *img, int color)
    {
    	int	offset;
    
    	offset = (y * img->line_length) + (x * (img->bpp / 8));
    	*(unsigned int *)(img->pixel_ptr + offset) = color;
    }
    
    Why this is efficient:
    • Direct memory write, no function call overhead
    • Single calculation per pixel
    • Cache-friendly sequential access pattern
    mlx_put_image_to_window() is called once after all pixels are computed, minimizing expensive window operations.

    Performance Optimization Strategies

    1
    Strategy 1: Adaptive Iteration Count
    2
    Start with low iterations for exploration, increase for detailed viewing:
    3
    // Quick preview
    fractol->iterations_definition = 20;
    
    // Detailed view
    fractol->iterations_definition = 100;
    
    // Deep zoom
    fractol->iterations_definition = 300;
    
    4
    Strategy 2: Early Escape Detection
    5
    The algorithm already implements this optimally:
    6
    if ((z.x * z.x) + (z.y * z.y) > fractol->escape_value)
    	return;  // Immediately exits loop
    
    7
    Points that escape quickly (most of the image) skip remaining iterations.
    8
    Strategy 3: Reduced Resolution for Real-Time Interaction
    9
    Modify fractol.h:24-25 for faster interaction:
    10
    // Default (high quality)
    # define WIDTH   2000
    # define HEIGHT  1500
    
    // Fast preview
    # define WIDTH   800
    # define HEIGHT  600
    
    11
    Impact: 800×600 is 6.25× faster than 2000×1500
    12
    Strategy 4: Cache-Aware Color Computation
    13
    Color mapping is performed only for escaped points:
    14
    color = map(i, PSYCHEDELIC_LIME, PSYCHEDELIC_MINT,
                fractol->iterations_definition);
    
    15
    Points in the set (white) skip this calculation entirely.

    Benchmarking Different Settings

    Typical Rendering Times

    These are approximate values on a modern CPU (Intel i5/i7 or equivalent). Actual times vary based on fractal region and hardware.
    Resolution: 2000×1500
    IterationsAvg TimeUse Case
    30~100msInitial exploration
    50~150msGeneral viewing
    100~300msDetailed examination
    200~600msDeep zoom
    500~1500msMaximum quality
    Resolution: 800×600
    IterationsAvg TimeUse Case
    30~15msSmooth real-time panning
    100~50msInteractive exploration

    Computational Complexity

    Per-pixel complexity:
    O(iterations_definition × operations_per_iteration)
    
    Per-frame complexity:
    O(WIDTH × HEIGHT × iterations_definition)
    
    Operations per iteration:
    • 1× complex square: 3 multiplications
    • 1× complex add: 2 additions
    • 1× magnitude check: 3 operations (2 multiplies, 1 add, 1 compare)
    • Total: ~8 floating-point operations
    At 2000×1500 with 30 iterations: 720 million floating-point operations per frame!

    Advanced Optimization Techniques

    The rendering loop is embarrassingly parallel - each pixel is independent. Implementing multi-threading could provide near-linear speedup based on CPU core count.Approach:
    • Divide screen into horizontal strips
    • Assign each strip to a separate thread
    • 8 cores could theoretically achieve 8× speedup
    Trade-off: Increased code complexity, thread synchronization overhead
    Single Instruction Multiple Data (SIMD) can process multiple pixels simultaneously using SSE/AVX instructions.Benefit: Process 4-8 pixels per CPU cycle Trade-off: Platform-specific code, complex implementation
    GPUs excel at parallel fractal rendering, capable of computing thousands of pixels simultaneously.Benefit: 100-1000× speedup possible Trade-off: Requires OpenCL/CUDA, different graphics framework (not MiniLibX)
    For deep zooms, perturbation theory allows calculating only the difference from a reference orbit, enabling arbitrary precision at standard double speed.Benefit: Deep zoom beyond float precision limits Trade-off: Complex algorithm, significant implementation effort

    Quality vs Performance Guidelines

    For smooth interaction:
    iterations_definition = 20-30
    WIDTH = 800, HEIGHT = 600
    
    For balanced quality:
    iterations_definition = 50-100
    WIDTH = 1920, HEIGHT = 1080
    
    For maximum quality:
    iterations_definition = 200-500
    WIDTH = 3840, HEIGHT = 2160  // 4K
    
    At maximum quality settings, rendering a single frame may take several seconds. This is normal for fractal rendering and reflects the computational complexity of the mathematics.

    Build docs developers (and LLMs) love