Skip to main content

CPU/GPU timing

The table below outlines the timing specifications for the Chombit central processing unit (CPU) and Palix graphics processing unit (GPU):

TimingValue
CPU speed6,000,000 cycles/second
6,000 cycles/ms
Video rendering speed30 frames/second
CPU cycles per video frame200,000 cycles/frame
Milliseconds per video frame33 13\frac{1}{3} ms/frame
GPU "paint" interval
(CPU is stalled during this time)
24,000 cycles
4 ms
12% of one frame

The number of CPU instructions per second can be approximated as follows:

ValueApproximation
Average cycles per instruction2.5 cycles/op
Instructions per second2,400,000 ops/second
Instructions per video frame80,000 ops/frame

The GPU paint interval time is always 4 milliseconds, regardless of the complexity of your scene. It is proportional to the maximum number of bytes (pixels for example) that might be read from memory during rendering. The calculation is shown below:

W=320H=224RowInitCycles=96×HTilemapCycles=3×W×HSpriteCycles=64×48×48PaintCycles=RowInitCycles+TilemapCycles+SpriteCycles16=24,000\begin{align*} W &= 320 \\ H &= 224 \\ \text{RowInitCycles} &= 96 \times H \\ \text{TilemapCycles} &= 3 \times W \times H \\ \text{SpriteCycles} &= 64 \times 48 \times 48 \\ \text{PaintCycles} &= \frac{\text{RowInitCycles} + \text{TilemapCycles} + \text{SpriteCycles}}{16} = 24{,}000 \end{align*}

GPU paint timing

The Palix video display is a matrix of pixels with 320 columns and 224 rows. It is updated by a GPU paint operation which is scheduled at specific points in time, thirty times per second. During the GPU paint operation, the Chombit CPU is stalled and will not execute any machine instructions for 4 milliseconds. If the paint operation is skipped, then a frame buffer will retain the most recently painted image and continue to display it; the CPU is not stalled in this case.

The GPU paint operation is controlled by the IO::PAINT_MODE MMIO location:

MODULE IO
. . .
# INCREMENTS FOR EACH VIDEO FRAME, EVEN IF PAINTING IS SKIPPED.
# THE REFRESH RATE IS 30 FRAMES/SEC = 33.333 MS/FRAME
VAR FRAME_COUNTER: PAIR LOCATED AT $D0_0302

# 0 = UNBUFFERED
# 1 = BUFFERED
# 3 = BUFFERED, PAINT REQUESTED; AFTERWARDS THE VALUE REVERTS TO 1
VAR PAINT_MODE: BYTE LOCATED AT $D0_0304
. . .
END MODULE

At the scheduled time for a GPU paint operation:

  • If IO::PAINT_MODE is 0, then painting is always performed. This is called unbuffered mode.
  • If IO::PAINT_MODE is 1, then painting is skipped. The buffered image will be displayed again.
  • If IO::PAINT_MODE is 3, then a new image gets painted; afterwards the GPU will set to IO::PAINT_MODE to 1 again. In other words, a value of 3 requests for the GPU to perform one paint operation.

This diagram illustrates the CPU/GPU timing synchronization for buffered rendering:

Chombit (CPU)Palix (GPU)GPU paintCPU is stalledPalix sets PAINT_MODE=1(skipped render)program sets PAINT_MODE=3timesafesafe to update graphics awaitingawaiting paintprogram sets PAINT_MODE=3

Why buffered mode?

Buffered rendering ensures that Palix does not paint until your program has finished updating the scene. For example, suppose that a video game monster's head and body are represented by two different sprites. If your program has moved the first sprite, but not yet moved the second sprite, then painting at this time would display a monster whose head is misaligned with its body. (These undesirable effects are called "rendering artifacts.") To avoid that, your program sets IO::PAINT_MODE = 3 to tell the GPU when it's ready for the scene to be painted, and the GPU sets IO::PAINT_MODE = 1 to tell your program that it's okay to move sprites around.

The Hybrix framework enables buffered rendering by default.

Why unbuffered mode?

Unbuffered rendering is a simpler mode, suitable for programs that don't require precise video animation. It is particularly useful for debugging, because sprites and tilemaps will update immediately after you update their MMIO locations. When stepping through your program using the Hybrix debugger, Palix will repaint whenever your program is paused.

Code sample: unbuffered mode

If you step through this program in the debugger, you will see the screen colors change from red, to green, then to blue:

MODULE MAIN
VAR BACKGROUND_COLOR: BYTE LOCATED AT $D0_0301

FUNC START()
16 -> MAIN::BACKGROUND_COLOR
19 -> MAIN::BACKGROUND_COLOR
21 -> MAIN::BACKGROUND_COLOR
END FUNC
END MODULE

Code sample: buffered mode

Here is a simple loop that requests a buffered paint, then waits in a LOOP for it to complete:

MODULE MAIN
. . .
# ---------------------------------------------------------------------------
FUNC WAIT_FOR_PAINT()
3 -> IO::PAINT_MODE

# WAIT FOR THE NEXT VIDEO FRAME TO GET PAINTED.
# THIS ALSO SYNCHRONIZES THE MAIN LOOP TO AT MOST 30 FPS.
LOOP
IF IO::PAINT_MODE <> 3
DO DROP
END LOOP
END FUNC
. . .
END MODULE

The framework's [ENGINE] file provides an equivalent implementation in ENGINE::WAIT_FOR_PAINT(), but using a more sophisticated approach that saves battery power by temporarily putting the CPU to sleep:

MODULE ENGINE
. . .
# ---------------------------------------------------------------------------
# WAIT FOR THE NEXT VIDEO FRAME TO GET PAINTED.
# THIS ALSO SYNCHRONIZES THE MAIN LOOP TO AT MOST 30 FPS.
FUNC WAIT_FOR_PAINT()
1 -> IO::IRQ_PENDING # "WRITE 1 TO CLEAR" IRQ_PENDING
3 -> IO::PAINT_MODE # REQUEST PAINT
1 -> IO::IRQ_WAKE_MASK # SLEEP UNTIL THE PAINT FINISHES
KERNEL::SLEEP()
END FUNC
. . .
END MODULE