Repository navigation
Why the CPU renderer uses bands
The CPU renderer cuts the image into horizontal bands and draws each on its own thread. A band owns its rows, so no two threads touch one pixel, and the image is byte for byte what one thread produces.
Every triangle is offered to every band. A band draws only the rows it owns. The depth test needs no atomics, and draw order within a band is submission order, which is what keeps blending correct. This is the same argument the GPU kernel makes with one thread per pixel.
The encode from linear light to sRGB, three pow calls per pixel, is split the same way.
A band is the same code with a row range. SIMD would need new arithmetic. The scene benchmark showed that rasterization and the resolve were most of the frame, and both scale with cores now.
make bench-scene renders a mipmapped checkerboard sphere of twelve thousand triangles at 1280 by 720:
| Machine | Workers | Rasterize | Resolve | Whole frame |
|---|---|---|---|---|
| Ryzen 9 5900X, Linux | 1 | 66 ms | 48 ms | 122 ms |
| Ryzen 9 5900X, Linux | 24 | 6 ms | 6 ms | 19 ms |
| Apple M4 Max, macOS | 1 | 83 ms | 9 ms | 97 ms |
| Apple M4 Max, macOS | 16 | 9 ms | 1 ms | 15 ms |
prepare is single threaded. It takes about 7 ms on the Ryzen and about 2 ms on the M4 Max, and it is now the largest stage.
The coverage tool reconstructs MC/DC vectors from the order its probe records arrive in. Two threads reporting one decision at once would interleave them. The examples and the benchmark ask for every core with available_workers().
A task cannot raise. A band writes its error into a slot, and rasterize_all raises it after every band has finished. Malformed triangle state never reaches a band: rasterize_all refuses it first, so the answer does not depend on which band a triangle falls in.
Mojo destroys a value after its last visible use. A coroutine that borrows an argument outlives the call, so rasterize_all takes its inputs as borrowed parameters and hands the tasks pointers to them. Everything the tasks read stays alive until TaskGroup.wait returns.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang