scene/damage.go

Functions Structs

Functions

func ComputeDamage

ComputeDamage compares current frame's objects with previous frame's,

returning the union of all changed/added/removed bounding boxes.

 

Returns image.Rectangle{} (zero rect) if nothing changed.

Returns full scene bounds on first frame (must render everything).

 

The algorithm is O(N) where N = max(current objects, previous objects).

func (dt *DamageTracker) ComputeDamage(objects []TaggedBounds) image.Rectangle {
	if dt.firstFrame {
		dt.firstFrame = false
		dt.storeBounds(objects)
		// First frame: caller should render everything.
		// Return zero rect — caller detects first frame via separate flag or renders full.
		return image.Rectangle{}
	}

	var damage image.Rectangle
	seen := make(map[uint64]struct{}, len(objects))

	for _, obj := range objects {
		seen[obj.ID] = struct{}{}
		if prev, exists := dt.prevBounds[obj.ID]; exists {
			if prev != obj.Rect {
				damage = damage.Union(prev).Union(obj.Rect)
			}
		} else {
			damage = damage.Union(obj.Rect)
		}
	}

	for id, prev := range dt.prevBounds {
		if _, exists := seen[id]; !exists {
			damage = damage.Union(prev)
		}
	}

	dt.storeBounds(objects)
	return damage
}

func IsFirstRender

IsFirstRender returns true if RenderWithDamage hasn't rendered a frame yet.

func (dt *DamageTracker) IsFirstRender() bool {
	return !dt.rendered
}

func MarkRendered

MarkRendered records that the first full render has completed.

func (dt *DamageTracker) MarkRendered() {
	dt.rendered = true
}

func NewDamageTracker

NewDamageTracker creates a new damage tracker.

func NewDamageTracker() *DamageTracker {
	return &DamageTracker{
		prevBounds:	make(map[uint64]image.Rectangle),
		firstFrame:	true,
	}
}

func Reset

Reset clears all tracked state. Next ComputeDamage will be treated as first frame.

func (dt *DamageTracker) Reset() {
	dt.prevBounds = make(map[uint64]image.Rectangle)
	dt.firstFrame = true
	dt.rendered = false
}

Structs

type DamageTracker struct

DamageTracker computes the minimal dirty region between frames by comparing

per-object bounding boxes. This is Level 1 of the four-level damage pipeline

(ADR-021): Object Diff → Tile Dirty → GPU Scissor → OS Present.

 

Usage:

 

tracker := NewDamageTracker()

// Each frame:

damage := tracker.ComputeDamage(scene.TaggedBounds())

if !damage.Empty() {

renderer.dirty.MarkRect(damage)

renderer.RenderDirty(target, scene)

}

type DamageTracker struct {
	prevBounds	map[uint64]image.Rectangle
	firstFrame	bool
	rendered	bool
}

type TaggedBounds struct

TaggedBounds associates a stable object ID with its bounding rectangle.

Objects with the same ID across frames are compared for movement/resize.

type TaggedBounds struct {
	ID	uint64
	Rect	image.Rectangle
}