Skip to content

gcu-c has no display HAL contract, so agents invent per-pixel fill_rect text and starve the audio feeder #113

Open

Description

@tig

Summary

The gcu-c plate's HAL is set_led / delay_ms / now_ms. It has no opinion at all about screens — so a product whose face is a screen invents the whole drawing surface, and the first invention an agent reaches for is the worst one.

In tig/xuss-c, src/domain.c renders text like this:

static void draw_char(...) {
  for (int col = 0; col < 5; col++)
    for (int row = 0; row < 7; row++)
      hal->fill_rect(hal, x + col*scale, y + row*scale, scale, scale, color);
}

35 fill_rect calls per character, and the device backend does a malloc + esp_lcd_panel_draw_bitmap + free per call. The Details screen refreshes ~6 rows of ~14 characters every 100 ms → roughly 2,900 allocations and SPI transactions per refresh, ~29k/second.

The product spec §4.4.3 forbids "stutter correlated with wink, banner scroll, Details refresh, or serial service" by name. The Details refresh path is the one the spec calls out, and it is the one carrying 29k transactions/sec next to a DMA audio feeder.

Notably the same file gets it right for the banner: compose the strip into a buffer, one blit. The knowledge to do it well was in the product; nothing pushed it onto the text path.

Landed in the plate PR

Guidance only, in plate AGENTS.md — compose-and-blit, don't render glyphs via per-pixel fill_rect, keep partial paints regional, plus a checklist line in knowledge/esp32-audio.md tying UI paint cost to audio starvation.

Open question

Should the plate ship display primitives at all?

  • For: every screen GCU re-derives fill_rect / blit and the granularity choice is a real performance contract, not a style preference. The plate already encodes a similar hard-won contract for now_ms (int64, with test_time.c seeding past 2³¹) — that pattern worked.
  • Against: gcu-c is a blink demo. Adding unused display hooks to a HAL that no plate code calls is scaffolding for a product that may not have a screen, and risks the plate becoming a framework.

I lean toward a middle option: keep hal.h minimal, but ship an optional include/gcu/draw.h + src/draw.c (portable glyph/strip composition, HAL-agnostic, host-testable) that products opt into. That gives the right idiom without imposing a display on every GCU. Wants a maintainer call.

  • Decide: no primitives / hooks in hal.h / optional portable draw module
  • If a draw module: host tests for composition, since that logic is pure and belongs in L0
  • Consider a silico gate advisory when a device backend allocates inside a HAL primitive (heap churn in a hot paint path is a repeatable trap)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions