Documentation
¶
Overview ¶
Package parallel provides tile-based parallel rendering infrastructure for gogpu/gg.
This package implements a tile-based rendering system where the canvas is divided into 64x64 pixel tiles that can be rendered independently in parallel. Key features:
- 64x64 tiles optimized for L1 cache (16KB per tile in RGBA)
- Tile pooling for memory reuse via sync.Pool
- Dirty region tracking for incremental rendering
- Lock-free operations where possible
Thread safety: TileGrid operations are NOT thread-safe by default. Use external synchronization or the provided WorkerPool for parallel access.
Index ¶
- Constants
- func PutTile(tile *Tile)
- type DirtyRegion
- func (d *DirtyRegion) Clear()
- func (d *DirtyRegion) Count() int
- func (d *DirtyRegion) ForEachDirty(fn func(tx, ty int))
- func (d *DirtyRegion) GetAndClear() [][2]int
- func (d *DirtyRegion) IsDirty(tx, ty int) bool
- func (d *DirtyRegion) IsEmpty() bool
- func (d *DirtyRegion) Mark(tx, ty int)
- func (d *DirtyRegion) MarkAll()
- func (d *DirtyRegion) MarkRect(x, y, w, h int)
- func (d *DirtyRegion) Resize(tilesX, tilesY int) *DirtyRegion
- func (d *DirtyRegion) TilesX() int
- func (d *DirtyRegion) TilesY() int
- func (d *DirtyRegion) TotalTiles() int
- type ParallelRasterizer
- func (pr *ParallelRasterizer) Clear(c color.Color)
- func (pr *ParallelRasterizer) ClearDirty()
- func (pr *ParallelRasterizer) Close()
- func (pr *ParallelRasterizer) Composite(dst []byte, stride int)
- func (pr *ParallelRasterizer) CompositeDirty(dst []byte, stride int)
- func (pr *ParallelRasterizer) DirtyTileCount() int
- func (pr *ParallelRasterizer) FillRect(x, y, w, h int, c color.Color)
- func (pr *ParallelRasterizer) FillTiles(tiles []*Tile, fn func(t *Tile))
- func (pr *ParallelRasterizer) Grid() *TileGrid
- func (pr *ParallelRasterizer) Height() int
- func (pr *ParallelRasterizer) MarkAllDirty()
- func (pr *ParallelRasterizer) MarkRectDirty(x, y, w, h int)
- func (pr *ParallelRasterizer) Resize(width, height int)
- func (pr *ParallelRasterizer) TileCount() int
- func (pr *ParallelRasterizer) Width() int
- type Tile
- type TileGrid
- func (g *TileGrid) AllTiles() []*Tile
- func (g *TileGrid) ClearDirty()
- func (g *TileGrid) Close()
- func (g *TileGrid) DirtyTiles() []*Tile
- func (g *TileGrid) ForEach(fn func(tile *Tile))
- func (g *TileGrid) ForEachDirty(fn func(tile *Tile))
- func (g *TileGrid) Height() int
- func (g *TileGrid) MarkAllDirty()
- func (g *TileGrid) MarkDirty(tx, ty int)
- func (g *TileGrid) MarkRectDirty(x, y, w, h int)
- func (g *TileGrid) Resize(width, height int)
- func (g *TileGrid) TileAt(tx, ty int) *Tile
- func (g *TileGrid) TileAtPixel(px, py int) *Tile
- func (g *TileGrid) TileCount() int
- func (g *TileGrid) TilesInRect(x, y, w, h int) []*Tile
- func (g *TileGrid) TilesX() int
- func (g *TileGrid) TilesY() int
- func (g *TileGrid) Width() int
- type TilePool
- type WorkerPool
Constants ¶
const ( // TileWidth is the width of a tile in pixels. // 64 pixels is optimal for work distribution (matches vello/tiny-skia). TileWidth = 64 // TileHeight is the height of a tile in pixels. // 64 pixels ensures 16KB per tile (fits L1 cache). TileHeight = 64 // TilePixels is the total number of pixels in a full tile. TilePixels = TileWidth * TileHeight // TileBytes is the size of a full tile in bytes (RGBA = 4 bytes per pixel). TileBytes = TilePixels * 4 )
Tile size constants optimized for cache efficiency and work distribution.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type DirtyRegion ¶
type DirtyRegion struct {
// contains filtered or unexported fields
}
DirtyRegion tracks which tiles need redrawing using an atomic bitmap. It provides lock-free, thread-safe operations for concurrent access.
The bitmap uses one bit per tile, packed into uint64 words (64 tiles per word). All methods are safe for concurrent use without external synchronization.
This is designed for incremental rendering where only changed regions need to be redrawn, significantly improving performance for static content.
func NewDirtyRegion ¶
func NewDirtyRegion(tilesX, tilesY int) *DirtyRegion
NewDirtyRegion creates a new dirty region tracker for the given tile grid dimensions. All tiles start as clean (not dirty). Returns nil if dimensions are invalid (zero or negative).
func (*DirtyRegion) Clear ¶
func (d *DirtyRegion) Clear()
Clear clears all dirty flags (marks all tiles as clean).
func (*DirtyRegion) Count ¶
func (d *DirtyRegion) Count() int
Count returns the number of tiles marked as dirty.
func (*DirtyRegion) ForEachDirty ¶
func (d *DirtyRegion) ForEachDirty(fn func(tx, ty int))
ForEachDirty calls fn for each dirty tile without clearing the dirty flags. Tiles are visited in row-major order (left-to-right, top-to-bottom).
func (*DirtyRegion) GetAndClear ¶
func (d *DirtyRegion) GetAndClear() [][2]int
GetAndClear atomically retrieves all dirty tile coordinates and clears them. Returns a slice of [2]int{tx, ty} for each dirty tile. This is the primary method for processing dirty tiles during rendering.
func (*DirtyRegion) IsDirty ¶
func (d *DirtyRegion) IsDirty(tx, ty int) bool
IsDirty returns true if the tile at (tx, ty) is marked as dirty. Returns false for out-of-bounds coordinates.
func (*DirtyRegion) IsEmpty ¶
func (d *DirtyRegion) IsEmpty() bool
IsEmpty returns true if no tiles are marked as dirty.
func (*DirtyRegion) Mark ¶
func (d *DirtyRegion) Mark(tx, ty int)
Mark marks a single tile as dirty (needs redrawing). This is a lock-free O(1) operation using atomic OR. Does nothing if coordinates are out of bounds.
func (*DirtyRegion) MarkAll ¶
func (d *DirtyRegion) MarkAll()
MarkAll marks all tiles as dirty. This is useful when the entire content needs to be redrawn.
func (*DirtyRegion) MarkRect ¶
func (d *DirtyRegion) MarkRect(x, y, w, h int)
MarkRect marks all tiles that intersect the given pixel rectangle as dirty. Coordinates are in pixel space, not tile space. Does nothing if the rectangle is invalid or completely outside the grid.
func (*DirtyRegion) Resize ¶
func (d *DirtyRegion) Resize(tilesX, tilesY int) *DirtyRegion
Resize creates a new dirty region with the specified dimensions. All tiles in the new region are marked as dirty. Returns nil if dimensions are invalid.
Note: This is not an in-place resize; it returns a new DirtyRegion. For thread-safe usage, the caller should atomically swap the pointer.
func (*DirtyRegion) TilesX ¶
func (d *DirtyRegion) TilesX() int
TilesX returns the number of tiles horizontally.
func (*DirtyRegion) TilesY ¶
func (d *DirtyRegion) TilesY() int
TilesY returns the number of tiles vertically.
func (*DirtyRegion) TotalTiles ¶
func (d *DirtyRegion) TotalTiles() int
TotalTiles returns the total number of tiles in the region.
type ParallelRasterizer ¶
type ParallelRasterizer struct {
// contains filtered or unexported fields
}
ParallelRasterizer renders to tiles in parallel using WorkerPool.
It divides the canvas into 64x64 pixel tiles that can be rendered independently and in parallel. This is the primary entry point for parallel rendering operations.
Thread safety: ParallelRasterizer methods are safe for concurrent use after initialization. The internal WorkerPool handles synchronization.
func NewParallelRasterizer ¶
func NewParallelRasterizer(width, height int) *ParallelRasterizer
NewParallelRasterizer creates a new parallel rasterizer for the given dimensions. It initializes a TileGrid and WorkerPool with GOMAXPROCS workers. Returns nil if width or height is <= 0.
func NewParallelRasterizerWithWorkers ¶
func NewParallelRasterizerWithWorkers(width, height, workers int) *ParallelRasterizer
NewParallelRasterizerWithWorkers creates a parallel rasterizer with a specific worker count. If workers <= 0, GOMAXPROCS is used. Returns nil if width or height is <= 0.
func (*ParallelRasterizer) Clear ¶
func (pr *ParallelRasterizer) Clear(c color.Color)
Clear fills all tiles with the specified color in parallel.
func (*ParallelRasterizer) ClearDirty ¶
func (pr *ParallelRasterizer) ClearDirty()
ClearDirty resets the dirty flag on all tiles.
func (*ParallelRasterizer) Close ¶
func (pr *ParallelRasterizer) Close()
Close releases all resources. The rasterizer should not be used after Close is called.
func (*ParallelRasterizer) Composite ¶
func (pr *ParallelRasterizer) Composite(dst []byte, stride int)
Composite copies all tile data to a destination buffer in row-major RGBA order. The dst buffer must be at least width * height * 4 bytes. The stride is the number of bytes per row in dst (typically width * 4). This operation is performed in parallel across tiles.
func (*ParallelRasterizer) CompositeDirty ¶
func (pr *ParallelRasterizer) CompositeDirty(dst []byte, stride int)
CompositeDirty copies only dirty tiles to a destination buffer. This is useful for incremental rendering where only changed regions need updating.
func (*ParallelRasterizer) DirtyTileCount ¶
func (pr *ParallelRasterizer) DirtyTileCount() int
DirtyTileCount returns the number of tiles marked as dirty.
func (*ParallelRasterizer) FillRect ¶
func (pr *ParallelRasterizer) FillRect(x, y, w, h int, c color.Color)
FillRect fills a rectangle with the specified color across affected tiles in parallel. Coordinates are in canvas pixel space. The rectangle is clipped to canvas bounds.
func (*ParallelRasterizer) FillTiles ¶
func (pr *ParallelRasterizer) FillTiles(tiles []*Tile, fn func(t *Tile))
FillTiles executes a function on each tile in parallel. The function receives each tile and should only modify that tile's Data. This is the generic parallel fill mechanism for custom operations.
func (*ParallelRasterizer) Grid ¶
func (pr *ParallelRasterizer) Grid() *TileGrid
Grid returns the underlying TileGrid for advanced usage. Use with caution as direct grid manipulation bypasses parallel safety.
func (*ParallelRasterizer) Height ¶
func (pr *ParallelRasterizer) Height() int
Height returns the canvas height in pixels.
func (*ParallelRasterizer) MarkAllDirty ¶
func (pr *ParallelRasterizer) MarkAllDirty()
MarkAllDirty marks all tiles for redraw.
func (*ParallelRasterizer) MarkRectDirty ¶
func (pr *ParallelRasterizer) MarkRectDirty(x, y, w, h int)
MarkRectDirty marks all tiles intersecting the rectangle as dirty.
func (*ParallelRasterizer) Resize ¶
func (pr *ParallelRasterizer) Resize(width, height int)
Resize changes the canvas dimensions, reallocating tiles as needed. All tiles will be marked dirty after resize. If dimensions are unchanged, this is a no-op.
func (*ParallelRasterizer) TileCount ¶
func (pr *ParallelRasterizer) TileCount() int
TileCount returns the total number of tiles.
func (*ParallelRasterizer) Width ¶
func (pr *ParallelRasterizer) Width() int
Width returns the canvas width in pixels.
type Tile ¶
type Tile struct {
// X is the tile column index (0-based).
X int
// Y is the tile row index (0-based).
Y int
// Width is the actual width in pixels (may be < TileWidth for edge tiles).
Width int
// Height is the actual height in pixels (may be < TileHeight for edge tiles).
Height int
// Dirty indicates whether this tile needs redrawing.
Dirty bool
// Data contains the RGBA pixel data owned by this tile.
// Length is Width * Height * 4 bytes.
Data []byte
}
Tile represents a rectangular region for parallel processing.
Each tile contains its own pixel buffer that can be rendered independently. Edge tiles may have smaller actual dimensions when the canvas is not evenly divisible by the tile size.
func (*Tile) Bounds ¶
Bounds returns the pixel bounds of this tile in canvas space. Returns (x, y, width, height) where x,y is the top-left corner.
func (*Tile) CanvasPixelOffset ¶
CanvasPixelOffset returns the byte offset for a canvas-space pixel. Coordinates cx, cy are in canvas space. Returns -1 if the pixel is not within this tile.
func (*Tile) Contains ¶
Contains returns true if the canvas-space pixel (cx, cy) is within this tile.
func (*Tile) PixelOffset ¶
PixelOffset returns the byte offset into Data for the given pixel. Coordinates px, py are relative to the tile (0,0 is top-left of tile). Returns -1 if coordinates are out of bounds.
type TileGrid ¶
type TileGrid struct {
// contains filtered or unexported fields
}
TileGrid manages a grid of tiles for parallel rendering.
The grid divides a canvas into 64x64 pixel tiles. Edge tiles may have smaller dimensions when the canvas is not evenly divisible by the tile size. Tiles are stored in a flat slice for cache efficiency, accessed via index calculation: index = ty * tilesX + tx.
Thread safety: TileGrid is NOT thread-safe. Use external synchronization for concurrent access, or use the provided WorkerPool.
func NewTileGrid ¶
NewTileGrid creates a new tile grid for the given canvas dimensions. The grid will contain enough tiles to cover the entire canvas. Edge tiles will have reduced dimensions if the canvas is not evenly divisible by the tile size (64x64).
func (*TileGrid) AllTiles ¶
AllTiles returns all tiles in the grid. The returned slice should not be modified.
func (*TileGrid) ClearDirty ¶
func (g *TileGrid) ClearDirty()
ClearDirty resets the dirty flag on all tiles.
func (*TileGrid) Close ¶
func (g *TileGrid) Close()
Close releases all tiles back to the pool. The grid should not be used after calling Close.
func (*TileGrid) DirtyTiles ¶
DirtyTiles returns all tiles that are marked as dirty. The returned slice is newly allocated and can be safely modified.
func (*TileGrid) ForEach ¶
ForEach calls fn for each tile in the grid. Tiles are visited in row-major order (left-to-right, top-to-bottom).
func (*TileGrid) ForEachDirty ¶
ForEachDirty calls fn for each dirty tile in the grid.
func (*TileGrid) MarkAllDirty ¶
func (g *TileGrid) MarkAllDirty()
MarkAllDirty marks all tiles as dirty.
func (*TileGrid) MarkDirty ¶
MarkDirty marks the tile at tile coordinates (tx, ty) as dirty. Does nothing if coordinates are out of bounds.
func (*TileGrid) MarkRectDirty ¶
MarkRectDirty marks all tiles intersecting the pixel rectangle as dirty. Coordinates are in canvas pixel space.
func (*TileGrid) Resize ¶
Resize changes the grid dimensions, reallocating tiles as needed. If dimensions haven't changed, this is a no-op. All tiles will be marked dirty after resize.
func (*TileGrid) TileAt ¶
TileAt returns the tile at tile coordinates (tx, ty). Returns nil if coordinates are out of bounds.
func (*TileGrid) TileAtPixel ¶
TileAtPixel returns the tile containing the pixel at canvas coordinates (px, py). Returns nil if coordinates are out of bounds.
func (*TileGrid) TilesInRect ¶
TilesInRect returns all tiles that intersect the given rectangle. Coordinates are in canvas pixel space. Returns nil if the rectangle is completely outside the canvas.
type TilePool ¶
type TilePool struct {
// contains filtered or unexported fields
}
TilePool provides efficient reuse of Tile instances via sync.Pool.
The pool reduces GC pressure by reusing tile memory. When a tile is returned to the pool, its data is cleared and ready for reuse.
Thread safety: TilePool is safe for concurrent use.
type WorkerPool ¶
type WorkerPool struct {
// contains filtered or unexported fields
}
WorkerPool is a pool of goroutines for parallel rendering.
The pool distributes work items across multiple workers, each with their own queue. Workers can steal work from other workers when their own queue is empty. This helps balance load when some tasks are slower than others.
Thread safety: WorkerPool is safe for concurrent use.
func NewWorkerPool ¶
func NewWorkerPool(workers int) *WorkerPool
NewWorkerPool creates a new worker pool with the specified number of workers. If workers is 0 or negative, GOMAXPROCS is used. The pool starts immediately and workers begin waiting for work.
func (*WorkerPool) Close ¶
func (p *WorkerPool) Close()
Close gracefully shuts down the pool. It stops accepting new work, waits for all queued work to complete, and then stops all workers. Close is safe to call multiple times.
func (*WorkerPool) ExecuteAll ¶
func (p *WorkerPool) ExecuteAll(work []func())
ExecuteAll distributes work across workers and waits for all to complete. This is the primary method for parallel processing. If the pool is closed, this is a no-op.
func (*WorkerPool) ExecuteAsync ¶
func (p *WorkerPool) ExecuteAsync(work []func())
ExecuteAsync distributes work across workers without waiting for completion. Useful for fire-and-forget scenarios. If the pool is closed, this is a no-op.
func (*WorkerPool) IsRunning ¶
func (p *WorkerPool) IsRunning() bool
IsRunning returns true if the pool is still accepting work.
func (*WorkerPool) QueuedWork ¶
func (p *WorkerPool) QueuedWork() int
QueuedWork returns the total number of work items currently queued. This is an approximation as queues can change while iterating.
func (*WorkerPool) Submit ¶
func (p *WorkerPool) Submit(fn func())
Submit sends a single work item to the pool. The work is distributed to the worker with the shortest queue. If the pool is closed, this is a no-op.
func (*WorkerPool) Workers ¶
func (p *WorkerPool) Workers() int
Workers returns the number of workers in the pool.