2026-01-19 07:28:14 +00:00

11 KiB
Raw Blame History

< Back | Summary | Next >

🎨 GFX Peripheral (Graphics System)

1. Overview

The GFX peripheral is responsible for generating images in PROMETEU.

It models simple graphics hardware, inspired by classic consoles (SNES, CPS-2, Neo-Geo), prioritizing:

  • determinism
  • low computational cost
  • didactics
  • portability

The GFX is not a modern GPU.
It is an explicit device, based on:

  • framebuffer
  • tilemaps
  • tile banks
  • priority-based sprites
  • composition by drawing order

2. Resolution and Framebuffer

Base resolution

  • 320 × 180 pixels
  • aspect ratio close to 16:9
  • scalable by the host (nearest-neighbor)

Pixel format

  • RGB565
  • 5 bits Red
  • 6 bits Green
  • 5 bits Blue
  • no alpha channel

Transparency is handled via color key.


3. Double Buffering

The GFX maintains two buffers:

  • Back Buffer — where the frame is built
  • Front Buffer — where the frame is displayed

Per-frame flow:

  1. The system draws to the back buffer
  2. Calls present()
  3. Buffers are swapped
  4. The host displays the front buffer

This guarantees:

  • no tearing
  • clear per-frame synchronization
  • deterministic behavior

4. PROMETEU Graphical Structure

The graphical world is composed of:

  • Up to 16 Tile Banks
  • 4 Tile Layers (scrollable)
  • 1 HUD Layer (fixed, always on top)
  • Sprites with priority between layers

4.1 Tile Banks

  • There are up to 16 banks
  • Each bank has a fixed tile size:
    • 8×8, 16×16, or 32×32
  • A bank is a graphics library:
    • environment
    • characters
    • UI
    • effects

4.2 Layers

  • There are:
    • 4 Tile Layers
    • 1 HUD Layer
  • Each layer points to a single bank
  • Sprites can use any bank
  • HUD:
    • does not scroll
    • maximum priority
    • generally uses 8×8 tiles

5. Internal Model of a Tile Layer

A Tile Layer is not a bitmap of pixels.
It is composed of:

  • A logical Tilemap (tile indices)
  • A Border Cache (window of visible tiles)
  • A Scroll Offset

Structure:

  • bank_id
  • tile_size
  • tilemap (large matrix)
  • scroll_x, scroll_y
  • cache_origin_x, cache_origin_y
  • cache_tiles[w][h]

6. Logical Tilemap

The tilemap represents the world:

Each cell contains:

  • tile_id
  • flip_x
  • flip_y
  • priority (optional)
  • palette_id (optional)

The tilemap can be much larger than the screen.


7. Border Cache (Tile Cache)

The cache is a window of tiles around the camera.

Example:

  • Screen: 320×180
  • 16×16 tiles → 20×12 visible
  • Cache: 22×14 (1-tile margin)

It stores tiles already resolved from the tilemap.


8. Cache Update

Every frame:

  1. Calculate:
  • tile_x = scroll_x / tile_size
  • tile_y = scroll_y / tile_size
  • offset_x = scroll_x % tile_size
  • offset_y = scroll_y % tile_size
  1. If tile_x changed:
  • Advance cache_origin_x
  • Reload only the new column
  1. If tile_y changed:
  • Advance cache_origin_y
  • Reload only the new line

Only one row and/or column is updated per frame.


9. Cache as Ring Buffer

The cache is circular:

  • Does not physically move data
  • Only moves logical indices

Access:

  • real_x = (cache_origin_x + logical_x) % cache_width
  • real_y = (cache_origin_y + logical_y) % cache_height

10. Projection to the Back Buffer

For each frame:

  1. For each Tile Layer, in order:
  • Rasterize visible tiles from the cache
  • Apply scroll, flip, and transparency
  • Write to the back buffer
  1. Draw sprites:
  • With priority between layers
  • Drawing order defines depth
  1. Draw HUD layer last

11. Drawing Order and Priority

  • There is no Z-buffer
  • There is no automatic sorting
  • Whoever draws later is in front

Base order:

  1. Tile Layer 0
  2. Tile Layer 1
  3. Tile Layer 2
  4. Tile Layer 3
  5. Sprites (by priority between layers)
  6. HUD Layer

12. Transparency (Color Key)

  • One RGB565 value is reserved as TRANSPARENT_KEY
  • Pixels with this color are not drawn
if src == TRANSPARENT_KEY:
    skip
else:
    draw

13. Color Math (Discrete Blending)

Inspired by the SNES.

Official modes:

  • BLEND_NONE
  • BLEND_HALF
  • BLEND_HALF_PLUS
  • BLEND_HALF_MINUS
  • BLEND_FULL

No continuous alpha.
No arbitrary blending.

Everything is:

  • integer
  • cheap
  • deterministic

14. Where Blend is Applied

  • Blending occurs during drawing
  • The result goes directly to the back buffer
  • There is no automatic post-composition

15. What the GFX DOES NOT support

By design:

  • Continuous alpha
  • RGBA framebuffer
  • Shaders
  • Modern GPU pipeline
  • HDR
  • Gamma correction

16. Performance Rule

  • Layers:
    • only update the border when crossing a tile
    • never redraw the entire world
  • Rasterization:
    • always per frame, only the visible area
  • Sprites:
    • always redrawn per frame

17. Special PostFX — Fade (Scene and HUD)

PROMETEU supports gradual fade as a special PostFX, with two independent controls:

  • Scene Fade: affects the entire scene (Tile Layers 03 + Sprites)
  • HUD Fade: affects only the HUD Layer (always composed last)

The fade is implemented without continuous per-pixel alpha and without floats. It uses a discrete integer level (0..31), which in practice produces an "almost continuous" visual result in 320×180 pixel art.


17.1 Fade Representation

Each fade is represented by:

  • fade_level: u8 in the range [0..31]
    • 0 → fully replaced by the fade color
    • 31 → fully visible (no fade)
  • fade_color: RGB565
    • color the image will be blended into

Registers:

  • SCENE_FADE_LEVEL (0..31)
  • SCENE_FADE_COLOR (RGB565)
  • HUD_FADE_LEVEL (0..31)
  • HUD_FADE_COLOR (RGB565)

Common cases:

  • Fade-out: fade_color = BLACK
  • Flash/teleport: fade_color = WHITE
  • Special effects: any RGB565 color

17.2 Fade Operation (Blending with Arbitrary Color)

For each RGB565 pixel src and fade color fc, the final pixel dst is calculated per channel.

  1. Extract components:
  • src_r5, src_g6, src_b5
  • fc_r5, fc_g6, fc_b5
  1. Apply integer blending:
src_weight = fade_level // 0..31
fc_weight = 31 - fade_level

r5 = (src_r5 * src_weight + fc_r5 * fc_weight) / 31
g6 = (src_g6 * src_weight + fc_g6 * fc_weight) / 31
b5 = (src_b5 * src_weight + fc_b5 * fc_weight) / 31
  • src_r5, src_g6, src_b5
  • fc_r5, fc_g6, fc_b5
  1. Repack:
dst = pack_rgb565(r5, g6, b5)

Notes:

  • Deterministic operation
  • Integers only
  • Can be optimized via LUT

17.3 Order of Application in the Frame

The frame composition follows this order:

  1. Rasterize Tile Layers 03 → Back Buffer
  2. Rasterize Sprites according to priority
  3. (Optional) Extra pipeline (Emission/Light/Glow etc.)
  4. Apply Scene Fade using:
  • SCENE_FADE_LEVEL
  • SCENE_FADE_COLOR
  1. Rasterize HUD Layer
  2. Apply HUD Fade using:
  • HUD_FADE_LEVEL
  • HUD_FADE_COLOR
  1. present()

Rules:

  • Scene Fade never affects the HUD
  • HUD Fade never affects the scene

17.4 Use Cases

  • HUD Switch:

    • decrease HUD_FADE_LEVEL to 0
    • switch HUD/tilemap
    • increase HUD_FADE_LEVEL to 31
  • Area Switch:

    • decrease SCENE_FADE_LEVEL to 0
    • switch scenery
    • increase SCENE_FADE_LEVEL to 31
  • Flash / damage / teleport:

    • use fade_color = WHITE or another thematic color

18. Palette System

18.1. Overview

PROMETEU uses exclusively palette-indexed graphics.

There is no direct RGB-per-pixel mode.
Every graphical pixel is an index pointing to a real color in a palette.

Objectives:

  • reduce RAM and storage usage
  • allow color swapping without shaders
  • maintain retro identity
  • facilitate effects like variation, damage, day/night

18.2. Pixel Format

Each pixel of a tile or sprite is:

  • 4 bits per pixel (4bpp)
  • values: 0..15

Fixed rule:

  • Index 0 = TRANSPARENT
  • Indices 1..15 = valid palette colors

18.3. Palette Structure

Each Tile Bank contains:

  • Up to 256 palettes
  • Each palette has:
    • 16 colors
    • each color in RGB565 (u16)

Size:

  • 1 palette = 16 × 2 bytes = 32 bytes
  • 256 palettes = 8 KB per bank
  • 16 banks = 128 KB maximum palettes

18.4. Palette Association

Fundamental Rule

  • Each tile uses a single palette
  • Each sprite uses a single palette
  • The palette must be provided explicitly in every draw

There is no palette swap within the same tile or sprite.


18.5. Where the Palette is Defined

Tilemap

Each tilemap cell contains:

  • tile_id
  • palette_id (u8)
  • flip_x
  • flip_y

Sprite

Each sprite draw contains:

  • bank_id
  • tile_id
  • palette_id (u8)
  • x, y
  • flip_x, flip_y
  • priority

18.6. Color Resolution

The pipeline works like this:

  1. Read indexed pixel from tile (value 0..15)
  2. If index == 0 → transparent pixel
  3. Otherwise:
  • real_color = palette[palette_id][index]
  1. Apply:
  • flip
  • discrete blend
  • writing to back buffer

In other words:

pixel_index = tile_pixel(x,y)
if pixel_index == 0:
    skip
else:
    color = bank.palettes[palette_id][pixel_index]
    draw(color)

18.7. Organization of Tile Banks

Tile Banks are "strong assets":

  • Tiles and palettes live together
  • Export/import always carries:
    • tiles + palettes
  • The hardware does not impose semantic organization:
    • grouping is the creator's decision
  • Tooling and scripts can create conventions:
    • e.g.: palettes 0..15 = enemies
    • 16..31 = scenery
    • etc.

18.8. Possible Effects with Palettes

Without shaders, it is possible to:

  • Palette swap:
    • enemies with color variation
  • States:
    • damage, ice, poison, power-up
  • Day / night:
    • swap palettes globally
  • Biomes:
    • same art, different climate
  • UI themes

All this without changing tiles.


18.9. Artistic Limitations

  • Each tile/sprite:

    • maximum of 16 colors
  • Smooth gradients require:

    • dithering
    • discrete blend
    • glow/emission

This limitation is intentional and part of the PROMETEU identity.


18.10. Metrics for Certification (CAP)

The system can measure:

  • palettes_loaded_total
  • palettes_referenced_this_frame
  • tiles_drawn_by_palette_id
  • sprites_drawn_by_palette_id

This allows:

  • analyzing artistic cost
  • teaching the impact of excessive variety
  • suggesting best practices for visual cohesion

19. Summary

PROMETEU's GFX is simple by choice, not by limitation.

  • RGB565 Framebuffer with double buffer
  • Color key for transparency
  • SNES-style discrete blending
  • Up to 16 tile banks
  • 4 Tile Layers + 1 HUD
  • Layer = tilemap + cache + scroll
  • Rasterized projection per frame
  • Depth defined by drawing order

< Back | Summary | Next >