CHGame 0.3.0
Libraries for the CHGame handheld
Loading...
Searching...
No Matches
Presenting the framebuffer

Sending the framebuffer to the panel: whole or a rectangle, blocking or by DMA in the background, and waiting for it. More...

Functions

void gfx_flush (void)
 Sends the whole framebuffer to the panel and returns when it has gone out.
 
void gfx_flushAsync (void)
 Starts sending the whole framebuffer and returns at once; the DMA interrupt does the rest.
 
bool gfx_busy (void)
 Whether an async flush is still in flight.
 
void gfx_wait (void)
 Waits until the flush in flight, if any, has finished.
 
int gfx_flushRow (void)
 The first framebuffer row the flush in flight may still read.
 
void gfx_waitRow (int y)
 Waits until rows 0..y-1 are free to draw into.
 
void gfx_flushRect (int x, int y, int w, int h)
 Sends one rectangle of the framebuffer to the panel and returns when it has gone out.
 
void gfx_flushRectAsync (int x, int y, int w, int h)
 Starts sending one rectangle of the framebuffer and returns at once, as gfx_flushAsync() does for the whole frame.
 

Detailed Description

Sending the framebuffer to the panel: whole or a rectangle, blocking or by DMA in the background, and waiting for it.

A flush converts the 4 bpp framebuffer through the palette into the panel's format a chunk at a time and DMAs it out. The framebuffer is free to draw into again after gfx_wait() (or, row by row, after gfx_waitRow()). Every flush first waits for the one before it.

What a flush costs the CPU. The wire time is fixed (11.1 ms a full frame at 16 bpp, 8.4 ms at 12 bpp) and an async flush hands it to the DMA. The pixel conversion still runs on the CPU, in the DMA interrupt, one chunk at a time, and the DMA shares the bus: measured on the board, an async full frame costs the main loop 2.5-3.2 ms of CPU (by -O2/-Os and 12/16 bpp). At 60 fps that is a sixth of the CPU, taken from whatever the main loop is doing - an AI search, for example. A partial flush costs in proportion to its area. (Before 1.3, at the board's default -Os, the converters were silently compiled into flash and the same flush cost 5.1 ms.) -DCHGFX_ISR_IN_SRAM trims it by 4-9% for ~330 bytes of SRAM.

The cheapest frame is the one you do not draw: if nothing on screen changed, flush the buffer you already have. A palette animation still moves (the palette is applied during the flush), and the frame costs a flush and no drawing.

Flush progress - "racing the beam". The flush converts the framebuffer top to bottom, and a row it has converted is never read again, so it is free to draw into while the rest of the frame is still going out. gfx_flushRow() is the first row the flush in flight may still read; gfx_waitRow() waits for a band to be free. Useful when the next frame is drawn top to bottom: wait for the top band, draw it, wait for the next. A clip rectangle keeps the drawing honest:

gfx_waitRow(64); gfx_setClip(0, 0, 128, 64); drawTopHalf();
gfx_wait(); gfx_setClip(0, 64, 128, 64); drawBottomHalf();
void gfx_setClip(int x, int y, int w, int h)
Sets the clip rectangle every drawing call stays inside.
void gfx_resetClip(void)
Puts the clip rectangle back to the whole screen.
void gfx_flushAsync(void)
Starts sending the whole framebuffer and returns at once; the DMA interrupt does the rest.
void gfx_wait(void)
Waits until the flush in flight, if any, has finished.
void gfx_waitRow(int y)
Waits until rows 0..y-1 are free to draw into.

Partial updates. The wire is the bottleneck, so sending a quarter of the screen costs a quarter of the time - gfx_flushRect() is the single biggest lever left once DMA is in place.

Function Documentation

◆ gfx_flush()

void gfx_flush ( void  )

Sends the whole framebuffer to the panel and returns when it has gone out.

About 11.1 ms at 16 bpp, 8.4 ms at 12 bpp, at 24 MHz.

See also
gfx_flushAsync(), gfx_flushRect()

◆ gfx_flushAsync()

void gfx_flushAsync ( void  )

Starts sending the whole framebuffer and returns at once; the DMA interrupt does the rest.

Do not draw into the framebuffer until gfx_wait() (or gfx_waitRow()) says the rows are free. The conversion costs the main loop 2.5-3.2 ms of CPU a full frame (see above).

◆ gfx_busy()

bool gfx_busy ( void  )

Whether an async flush is still in flight.

Returns
true until the last byte of the flush has gone out.

◆ gfx_wait()

void gfx_wait ( void  )

Waits until the flush in flight, if any, has finished.

After it the framebuffer and gfx_chunkScratch() are free. Returns at once when nothing is being sent.

◆ gfx_flushRow()

int gfx_flushRow ( void  )

The first framebuffer row the flush in flight may still read.

Returns
A row number; rows above it are free to draw into. GFX_H when no flush is in flight or the last chunk has been converted.

◆ gfx_waitRow()

void gfx_waitRow ( int  y)

Waits until rows 0..y-1 are free to draw into.

Parameters
yOne past the last row needed. gfx_waitRow(GFX_H) is gfx_wait() as far as drawing goes.

◆ gfx_flushRect()

void gfx_flushRect ( int  x,
int  y,
int  w,
int  h 
)

Sends one rectangle of the framebuffer to the panel and returns when it has gone out.

Declare what your frame actually changed and a mostly-static scene runs several times faster: 64x64 goes out in 2.8 ms, 32x32 in 0.75 ms. The rectangle is clipped to the screen (an empty one sends nothing). x/w are rounded OUTWARD to a multiple of 2 (16 bpp) or 8 (12 bpp), because the 4 bpp source has to start and end on a byte. Rounding out is always safe - you just send a few more pixels than strictly needed.

Parameters
xLeft edge, in pixels.
yTop edge, in pixels.
wWidth, in pixels.
hHeight, in pixels.
See also
gfx_flushRectAsync()

◆ gfx_flushRectAsync()

void gfx_flushRectAsync ( int  x,
int  y,
int  w,
int  h 
)

Starts sending one rectangle of the framebuffer and returns at once, as gfx_flushAsync() does for the whole frame.

Clipped and rounded as gfx_flushRect(). gfx_wait() before drawing again.

Parameters
xLeft edge, in pixels.
yTop edge, in pixels.
wWidth, in pixels.
hHeight, in pixels.