OpenSNES
Modern Open-Source SNES Development SDK
Loading...
Searching...
No Matches
vramqueue.h File Reference

A queue of VRAM uploads, filled during the frame and sent in VBlank. More...

#include <snes/types.h>

Go to the source code of this file.

Macros

#define VRAM_QUEUE_COLUMN   0x81
 Step after each word: 32 words on (a column of a 32-wide tilemap).
#define VRAM_QUEUE_MAX   32
 Transfers the queue holds.
#define VRAM_QUEUE_ROW   0x80
 Step after each word: the next VRAM word (a row, tiles).
#define vramQueueFree()
 Entries still free.

Functions

void vramQueueFlush (void)
 Send every queued transfer to VRAM and empty the queue.
u16 vramQueuePush (const u8 *src, u16 addr, u16 size, u16 step)
 Note one transfer for the next vramQueueFlush().
u16 vramQueuePushSprite (const u8 *src, u16 addr, u16 size_px)
 Note one frame of a streamed sprite: all its strips in one call.

Variables

u16 vram_queue_addr [32]
u16 vram_queue_bank [32]
u16 vram_queue_count
u16 vram_queue_size [32]
u16 vram_queue_src [32]
u16 vram_queue_step [32]

Detailed Description

A queue of VRAM uploads, filled during the frame and sent in VBlank.

A game that streams graphics during play — sprite frames, the rows and columns of a scrolling map — makes several small VRAM transfers in each VBlank. One dmaCopyVram() call per transfer costs about as much to set up from C as it does to run for 128 bytes, and the VBlank is short. Here a transfer is noted while the frame is computed, when time is not short, and all of them are sent in VBlank by one assembly routine:

#include <snes/vramqueue.h> // and `vramqueue` in LIB_MODULES
// during the frame, wherever the game decides something must reach VRAM
if (vramQueueFree() >= 4) {
vramQueuePush(frame_tiles, 0x0000, 128, VRAM_QUEUE_ROW);
vramQueuePush(frame_tiles + 512, 0x0100, 128, VRAM_QUEUE_ROW);
// ...
}
vramQueueFlush(); // first thing in VBlank
void WaitForVBlank(void)
Wait for next VBlank period.
A queue of VRAM uploads, filled during the frame and sent in VBlank.
#define VRAM_QUEUE_ROW
Step after each word: the next VRAM word (a row, tiles).
Definition vramqueue.h:50
#define vramQueueFree()
Entries still free.
Definition vramqueue.h:63
u16 vramQueuePush(const u8 *src, u16 addr, u16 size, u16 step)
Note one transfer for the next vramQueueFlush().
void vramQueueFlush(void)
Send every queued transfer to VRAM and empty the queue.

What it buys is VBlank time, not time. Noting an entry is a call too, so in total the queue costs more than the dmaCopyVram() calls it replaces: six 128-byte transfers measure 21,700 master cycles queued against 16,600 called directly. But only 10,700 of the 21,700 fall in the VBlank — 8 per byte plus about 770 per entry — where the six calls put all 16,600 (devtools/libbench, rows vramc and vramq).

The budget is yours to keep. A VBlank is about 49,000 master cycles and the NMI handler takes the first 5,000 to 8,000 of them. A real project measured six 512-byte sprite frames as the most that fit, and queues five. What does not fit lands in active display, where the PPU ignores it: no error, missing tiles.

Request and first user: issue #165.

Macro Definition Documentation

◆ VRAM_QUEUE_COLUMN

#define VRAM_QUEUE_COLUMN   0x81

Step after each word: 32 words on (a column of a 32-wide tilemap).

◆ VRAM_QUEUE_MAX

#define VRAM_QUEUE_MAX   32

Transfers the queue holds.

◆ VRAM_QUEUE_ROW

#define VRAM_QUEUE_ROW   0x80

Step after each word: the next VRAM word (a row, tiles).

◆ vramQueueFree

#define vramQueueFree ( )
Value:
#define VRAM_QUEUE_MAX
Transfers the queue holds.
Definition vramqueue.h:47
u16 vram_queue_count

Entries still free.

Function Documentation

◆ vramQueueFlush()

void vramQueueFlush ( void )

Send every queued transfer to VRAM and empty the queue.

One DMA per entry, on channel 0. Call it in VBlank — right after WaitForVBlank() — or in force blank: it writes VRAM. With an empty queue it returns at once. VMAIN is left at $80, as the rest of the library leaves it.

Warning
Not from an NMI callback while the main code may be in the middle of a vramQueuePush(): the entry would be half written.

◆ vramQueuePush()

u16 vramQueuePush ( const u8 * src,
u16 addr,
u16 size,
u16 step )

Note one transfer for the next vramQueueFlush().

Parameters
srcSource bytes (ROM, plain RAM or FAR RAM) The queue keeps the POINTER: the bytes are read at the flush, not here. They must not change before vramQueueFlush(); a buffer filled again before the flush sends its last contents for every entry that points to it.
addrVRAM word address
sizeBytes (even; an entry of 0 bytes is skipped by the flush)
stepVRAM_QUEUE_ROW or VRAM_QUEUE_COLUMN
Returns
1, or 0 when the queue is full: nothing was noted

◆ vramQueuePushSprite()

u16 vramQueuePushSprite ( const u8 * src,
u16 addr,
u16 size_px )

Note one frame of a streamed sprite: all its strips in one call.

A sprite frame in OBJ VRAM is size_px / 8 rows of tiles; each row is a strip of size_px * 4 bytes (at 4 bpp), and the rows are 16 tiles apart: 512 bytes in the sheet, 256 words in VRAM. That is the layout opensnes-sprite sheet writes (a 128-pixel-wide raster) and the one OBJ VRAM expects. This notes the 1, 2, 4 or 8 strips of one frame.

Parameters
srcFirst byte of the frame in the sheet (its top-left tile). The frame must not straddle a bank boundary. Read at the flush, like vramQueuePush(): the bytes must not change before vramQueueFlush().
addrVRAM word address of the frame's top-left tile
size_pxWidth and height of the sprite: 8, 16, 32 or 64
Returns
1, or 0 when the strips do not all fit in the queue (none is noted) or size_px is under 8

Variable Documentation

◆ vram_queue_addr

u16 vram_queue_addr[32]
extern

◆ vram_queue_bank

u16 vram_queue_bank[32]
extern

◆ vram_queue_count

u16 vram_queue_count
extern

◆ vram_queue_size

u16 vram_queue_size[32]
extern

◆ vram_queue_src

u16 vram_queue_src[32]
extern

◆ vram_queue_step

u16 vram_queue_step[32]
extern