# Implementation Plan: Full Review — Animation Pipeline Performance

**Track ID:** review-perf_20260318
**Spec:** [spec.md](./spec.md)
**Created:** 2026-03-18
**Status:** [x] Complete

## Overview

Optimize the animation hot path in three phases: benchmark first to measure baseline, then optimize the two highest-ROI bottlenecks (FrameBuffer LUT and update_colors flattening), then add the optional protocol-direct frame generation path and guard the debug logging overhead.

---

## Phase 1: Benchmark Infrastructure

Establish performance baselines before optimizing so improvements are measurable.

### Tasks

- [x] Task 1.1: Create `tests/benchmarks/` directory with `conftest.py` and `pytest.ini` marker registration (`benchmark`)
- [x] Task 1.2: Write `tests/benchmarks/test_framebuffer_perf.py` — benchmark `_apply_canvas` for 1-tile, 5-tile, and 10-tile configurations; assert <5ms per call for 5-tile (PERF-H1, T-H4)
- [x] Task 1.3: Write `tests/benchmarks/test_packets_perf.py` — benchmark `update_colors` and `MatrixPacketGenerator.generate_packet` for 64px and 128px devices; record baseline (PERF-H2, T-H4)
- [x] Task 1.4: Write `tests/benchmarks/test_effect_frame_perf.py` — benchmark `AuroraEffect.generate_frame()` for 128-pixel device; record baseline (PERF-C1, T-H4)

- [x] Task 1.5: Run benchmarks and save as `review-perf_20260318-Phase1`: `uv run pytest tests/benchmarks/ -m benchmark --no-cov --benchmark-save=review-perf_20260318-Phase1`

### Verification

- [x] `uv run pytest tests/benchmarks/ -v -m benchmark` — all 10 benchmark tests pass
- [x] Baseline persisted via pytest-benchmark `--benchmark-save=review-perf_20260318-Phase1` to `.benchmarks/`

---

## Phase 2: FrameBuffer LUT and update_colors Optimization

Eliminate the two highest-cost per-frame bottlenecks.

### Tasks

- [x] Task 2.1: In `FrameBuffer.__init__`, compute `self._lut: list[int]` — flat list of canvas pixel indices in device output order — replacing the per-frame nested loop logic (PERF-H1)
- [x] Task 2.2: Replace `_apply_canvas` nested loop body with `return [canvas[i] for i in self._lut]` single comprehension (PERF-H1)
- [x] Task 2.3: Update `apply()` method to use `_lut`; ensure orientation remapping is pre-baked into the LUT indices (not a separate per-frame step) (PERF-H1)
- [x] Task 2.4: In `MatrixPacketGenerator.update_colors()`, replace `flat.extend(...)` per-pixel loop with direct `struct.pack_into` writes into `self._buf` at correct offsets (PERF-H2)
- [x] Task 2.5: Apply same optimization to `MultiZonePacketGenerator.update_colors()` if applicable (PERF-H2)
- [x] Task 2.6: Guard `dataclasses.asdict()` call in `Packet.unpack()` behind `_LOGGER.isEnabledFor(logging.DEBUG)` check (`base.py:114`) (PERF-L3)
- [x] Task 2.7: Run benchmarks and save as `review-perf_20260318-Phase2`: `uv run pytest tests/benchmarks/ -m benchmark --no-cov --benchmark-save=review-perf_20260318-Phase2`

### Verification

- [x] `uv run pytest tests/animation/ -v` — all existing animation tests pass (29 framebuffer + 53 packets)
- [x] `uv run pytest tests/benchmarks/ -m benchmark --benchmark-compare` — _apply_canvas 3.7× faster, update_colors 1.3× faster vs baseline
- [x] `uv run pyright` — 0 errors, 0 warnings, 0 information

---

## Phase 3: Protocol-Direct Frame Generation Path

Add optional high-performance frame generation that bypasses HSBK object construction.

### Tasks

- [x] Task 3.1: Add `generate_protocol_frame()` to `FrameEffect` with default implementation that calls `generate_frame()` and converts (PERF-C1)
- [x] Task 3.2: Update `FrameEffect.async_play()` to call `generate_protocol_frame()` instead of `generate_frame()` + conversion (PERF-C1, AR-M3)
- [x] Task 3.3: Implement a direct `generate_protocol_frame()` override in `EffectAurora` as a proof-of-concept (bypasses HSBK, returns raw uint16 tuples directly) (PERF-C1)
- [x] Task 3.4: Write benchmark test verifying `EffectAurora.generate_protocol_frame()` is faster than `generate_frame()` + conversion for 128-pixel device
- [x] Task 3.5: Run benchmarks and save as `review-perf_20260318-Phase3`: `uv run pytest tests/benchmarks/ -m benchmark --no-cov --benchmark-save=review-perf_20260318-Phase3`

### Verification

- [x] `uv run pytest tests/effects/ -v` — all existing effects tests pass (33 aurora + 21 frame_effect)
- [x] `uv run pytest tests/benchmarks/ -m benchmark` — protocol-direct 128px: 70.4µs vs 92.5µs (1.31× faster)
- [x] `uv run --frozen pytest` — 2447 tests pass
- [x] `uv run ruff format . && uv run ruff check .` — clean
- [x] `uv run pyright` — 0 errors, 0 warnings, 0 information

---

## Final Verification

- [x] All acceptance criteria in spec.md met
- [x] Benchmark results show `_apply_canvas` <5ms for 5-tile (320px) configuration (2.77µs = 0.003ms)
- [x] `uv run --frozen pytest` — 2447 tests pass including new benchmarks
- [x] `uv run pyright` — clean
- [x] `uv run ruff format . && uv run ruff check .` — clean
- [x] Ready for review

---

_Generated by Conductor. Tasks will be marked [~] in progress and [x] complete._
