Skip to main content
The GCM (Graphics Command Manager) system is the low-level interface for managing the RSX command buffer. It provides functions for controlling how commands are queued and executed by the graphics processor.

Command Buffer Architecture

The RSX uses a circular command buffer stored in main memory that the CPU writes to and the RSX reads from:
┌─────────────────────────────────────────┐
│         Command Buffer (Main RAM)      │
│  ┌──────────────────────────────────┐  │
│  │                                  │  │
│  │  begin                    end    │  │
│  │    ▼                        ▲    │  │
│  │  ┌───┬───┬───┬───┬───┬───┬─┴─┐  │  │
│  │  │CMD│CMD│CMD│   │   │   │   │  │  │
│  │  └───┴───┴───┴───┴───┴───┴───┘  │  │
│  │    ▲           ▲                 │  │
│  │    │           │                 │  │
│  │  current      get                │  │
│  │  (CPU write) (RSX read)          │  │
│  └──────────────────────────────────┘  │
└─────────────────────────────────────────┘

Context Structure

The gcmContextData structure tracks the command buffer state:
// From rsx/gcm_sys.h:722
typedef struct _gcmCtxData {
    u32 *begin;      // Start address of command buffer
    u32 *end;        // End address of command buffer  
    u32 *current;    // Current write position (CPU)
    gcmContextCallback callback; // Called when buffer is full
} gcmContextData;

Control Registers

The gcmControlRegister structure provides access to hardware registers:
// From rsx/gcm_sys.h:734
typedef struct _gcmCtrlRegister {
    vu32 put; // PUT register: CPU write position
    vu32 get; // GET register: RSX read position  
    vu32 ref; // REF register: reference value
} gcmControlRegister;

Command Buffer Modes

The GCM system supports three FIFO modes for managing the command buffer:
GCM_DEFAULT_FIFO_MODE_TRADITIONAL (0)From rsx/gcm_sys.h:19Checks the GET pointer in the traditional way for openings in the next segment. May sleep-wait if the GET pointer is at the next segment.
// Initialize with traditional FIFO mode
gcmInitDefaultFifoMode(GCM_DEFAULT_FIFO_MODE_TRADITIONAL);
Use when: You need simple, predictable behavior and don’t require optimal performance.

Initialization

1

Allocate Memory

Allocate aligned memory for the command buffer:
#define COMMAND_BUFFER_SIZE 0x80000  // 512KB
#define IO_BUFFER_SIZE (128*1024*1024) // 128MB
#define ALIGNMENT (1024*1024) // 1MB

void *host_addr = memalign(ALIGNMENT, IO_BUFFER_SIZE);
if (!host_addr) {
    printf("Failed to allocate I/O buffer\n");
    return -1;
}
The I/O buffer must be:
  • 1MB aligned
  • Size multiple of 1MB
  • Located in main memory
2

Initialize Context

Create the GCM context:
gcmContextData *context;
s32 ret = gcmInitBody(&context, COMMAND_BUFFER_SIZE, 
                      IO_BUFFER_SIZE, host_addr);
if (ret != 0) {
    printf("gcmInitBody failed: %d\n", ret);
    return ret;
}
Or use the higher-level rsxInit() wrapper:
s32 ret = rsxInit(&context, COMMAND_BUFFER_SIZE,
                  IO_BUFFER_SIZE, host_addr);
3

Configure FIFO Mode

Set the command buffer management mode:
// Set optimized FIFO mode for better performance
gcmInitDefaultFifoMode(GCM_DEFAULT_FIFO_MODE_OPTIMIZE);
4

Set Buffer Sizes (Optional)

Customize command buffer and segment sizes:
// Set custom sizes (in words, not bytes)
u32 bufferSize = 0x20000;  // 512KB in words
u32 segmentSize = 0x8000;  // 128KB in words

gcmSetDefaultFifoSize(bufferSize, segmentSize);

Command Buffer Operations

Writing Commands

Most RSX commands automatically handle command buffer writes:
// These functions write to the command buffer internally
rsxSetClearColor(context, 0x00000000);
rsxSetViewport(context, x, y, w, h, min, max, scale, offset);
rsxSetDepthTestEnable(context, GCM_TRUE);

Getting Current Position

// Get current write position in command buffer
u32 *current = rsxGetCurrentBuffer();

Flushing Commands

Flush pending commands to the RSX:
// Flush all pending commands
rsxFlushBuffer(context);
Always flush after submitting render commands to ensure they are sent to the RSX for processing.

Synchronization

Wait for RSX to finish processing:
// Wait for RSX to finish all commands
rsxFinish(context, 1);
Calling rsxFinish() causes the CPU to stall until the RSX completes all pending work. Use sparingly for performance.

Memory Mapping

The GCM system provides functions for mapping memory regions for RSX access.

Mapping Main Memory

Map a region of main memory for RSX access:
void *buffer = memalign(1024*1024, size);
u32 offset;

// Map the buffer
s32 ret = gcmMapMainMemory(buffer, size, &offset);
if (ret != 0) {
    printf("Failed to map memory: %d\n", ret);
    return ret;
}

// Use offset in RSX commands...

// Buffer is now accessible by RSX at 'offset'

Unmapping Memory

// Unmap by I/O address
gcmUnmapIoAddress(io_offset);

// Or unmap by effective address
gcmUnmapEaIoAddress(buffer);

Address Conversion

Convert between effective addresses and RSX offsets:
u32 offset;
const void *address = color_buffer;

s32 ret = gcmAddressToOffset(address, &offset);
if (ret != 0) {
    printf("Address not in RSX memory\n");
}

Configuration Access

Retrieve RSX system configuration:
gcmConfiguration config;

s32 ret = gcmGetConfiguration(&config);
if (ret == 0) {
    printf("Local address: %p\n", config.localAddress);
    printf("Local size: %u MB\n", config.localSize / (1024*1024));
    printf("I/O address: %p\n", config.ioAddress);
    printf("I/O size: %u MB\n", config.ioSize / (1024*1024));
    printf("Memory freq: %u MHz\n", config.memoryFreq / 1000000);
    printf("Core freq: %u MHz\n", config.coreFreq / 1000000);
}
Configuration structure from rsx/gcm_sys.h:745:
typedef struct _gcmCfg {
    void *localAddress;  // Effective start of RSX memory
    void *ioAddress;     // Effective start of I/O mapped main memory
    u32 localSize;       // Max RSX memory size (256MB)
    u32 ioSize;          // Max I/O mapped size
    u32 memoryFreq;      // RSX memory clock frequency
    u32 coreFreq;        // RSX core clock frequency
} gcmConfiguration;

Display Management

Flip Operations

The GCM system manages display buffer flipping:
// Queue flip command
gcmSetFlip(context, buffer_id);

// Flush commands
rsxFlushBuffer(context);

// Wait for flip to be queued
gcmSetWaitFlip(context);

Flip Status

Query and control flip status:
// Returns 0 if flip completed, non-zero otherwise
while (gcmGetFlipStatus() != 0) {
    usleep(200);
}

Flip Timing

Get flip timing information:
// Get timestamp of last flip
s64 lastFlip = gcmGetLastFlipTime();

// Get VBlank count
u64 vblankCount = gcmGetVBlankCount();

// Get current display field
u32 field = gcmGetCurrentField();

Immediate Flip

Flip without waiting for VSync:
// Flip immediately (may cause tearing)
gcmSetFlipImmediate(buffer_id);
Immediate flips can cause screen tearing. Use GCM_FLIP_VSYNC mode for smooth display.

Labels and Synchronization

Labels provide synchronization points between CPU and RSX.

Getting Label Address

// Get address of label (0-255)
u32 *label = gcmGetLabelAddress(index);

// RSX can write to this address
// CPU can read to check progress

Report Data

Use reports for GPU queries:
// Get report data address
gcmReportData *report = gcmGetReportDataAddress(index);

// Query report type
u32 value = gcmGetReport(type, index);

// Get timestamp
u64 timestamp = gcmGetTimeStamp(index);
Report types from rsx/gcm_sys.h:670-674:
  • GCM_ZPASS_PIXEL_CNT - Z-pass pixel count
  • GCM_ZCULL_STATS - Z-cull statistics
  • GCM_ZCULL_STATS1 - Z-cull statistics 1
  • GCM_ZCULL_STATS2 - Z-cull statistics 2
  • GCM_ZCULL_STATS3 - Z-cull statistics 3

Callback Functions

Register callbacks for various events:

VBlank Handler

void vblank_handler(u32 head) {
    // Called on VBlank
    printf("VBlank on head %u\n", head);
}

// Register callback
gcmSetVBlankHandler(vblank_handler);

// Set VBlank frequency (in Hz)
gcmSetVBlankFrequency(60);

Flip Handler

void flip_handler(u32 head) {
    // Called when flip occurs
    printf("Flip on head %u\n", head);
}

// Register callback  
gcmSetFlipHandler(flip_handler);

Graphics Error Handler

void graphics_handler(u32 val) {
    // Called on graphics pipeline error
    printf("Graphics error: 0x%08x\n", val);
}

// Register callback
gcmSetGraphicsHandler(graphics_handler);

Tile and Z-Cull Management

The RSX supports tiled memory regions for optimized access.

Setting Up Tiles

// Configure a tile region
gcmSetTile(
    0,                      // Tile index (0-14)
    GCM_LOCATION_RSX,       // Memory location
    color_offset[0],        // Offset in memory
    color_pitch * height,   // Size in bytes
    color_pitch,            // Pitch in bytes
    GCM_COMPMODE_DISABLED,  // Compression mode
    0,                      // Tile base
    0                       // Bank
);

// Bind the tile
gcmBindTile(0);

Invalidating Tiles

// Unbind tile
gcmUnbindTile(0);

// Invalidate tile configuration
gcmSetInvalidateTile(0);

Z-Cull Setup

Configure hierarchical Z-buffer culling:
gcmSetZcull(
    0,                      // Z-cull region index
    depth_offset,           // Depth buffer offset
    display_width,          // Width
    display_height,         // Height  
    0,                      // Cull start
    GCM_ZCULL_Z16,         // Z format
    GCM_SURFACE_CENTER_1,  // AA format
    GCM_ZCULL_LESS,        // Z-cull direction
    GCM_ZCULL_LONES,       // Z-cull format
    GCM_SCULL_SFUNC_ALWAYS,// Stencil function
    0x80,                   // Stencil ref
    0xff                    // Stencil mask
);

Control Register Access

Direct access to hardware control registers:
// Get control register structure
gcmControlRegister *ctrl = gcmGetControlRegister();

// Read current values
printf("PUT: 0x%08x\n", ctrl->put);
printf("GET: 0x%08x\n", ctrl->get);
printf("REF: 0x%08x\n", ctrl->ref);

// Wait for RSX to catch up
while (ctrl->get != ctrl->put) {
    usleep(100);
}
Direct register access is advanced usage. Incorrect use can cause system instability.

Transfer Operations

The GCM system provides blitting and transfer functions:

Transfer Structures

Scaled image blit with format conversion:
gcmTransferScale scale;

scale.conversion = GCM_TRANSFER_CONVERSION_TRUNCATE;
scale.format = GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8;
scale.operation = GCM_TRANSFER_OPERATION_SRCCOPY;

// Clipping rectangle
scale.clipX = 0;
scale.clipY = 0;
scale.clipW = dest_width;
scale.clipH = dest_height;

// Destination rectangle
scale.outX = 0;
scale.outY = 0;
scale.outW = dest_width;
scale.outH = dest_height;

// Scaling ratios (fixed point)
scale.ratioX = rsxGetFixedSint32((f32)src_width / dest_width);
scale.ratioY = rsxGetFixedSint32((f32)src_height / dest_height);

// Source dimensions
scale.inW = src_width;
scale.inH = src_height;
scale.inX = 0;
scale.inY = 0;

scale.pitch = src_pitch;
scale.origin = GCM_TRANSFER_ORIGIN_CORNER;
scale.interp = GCM_TRANSFER_INTERPOLATOR_LINEAR;
scale.offset = src_offset;

Example: Complete GCM Setup

From samples/graphics/rsxtest:
#include <rsx/rsx.h>
#include <rsx/gcm_sys.h>

#define CB_SIZE    0x80000
#define HOST_SIZE  (128*1024*1024)

gcmContextData *context;

int init_gcm() {
    // Allocate I/O buffer (1MB aligned)
    void *host_addr = memalign(1024*1024, HOST_SIZE);
    if (!host_addr) return -1;
    
    // Initialize GCM
    s32 ret = rsxInit(&context, CB_SIZE, HOST_SIZE, host_addr);
    if (ret != 0) {
        free(host_addr);
        return ret;
    }
    
    // Set optimized FIFO mode
    gcmInitDefaultFifoMode(GCM_DEFAULT_FIFO_MODE_OPTIMIZE);
    
    // Get configuration
    gcmConfiguration config;
    gcmGetConfiguration(&config);
    
    printf("RSX Configuration:\n");
    printf("  Local: %p (%u MB)\n", 
           config.localAddress, config.localSize >> 20);
    printf("  I/O: %p (%u MB)\n",
           config.ioAddress, config.ioSize >> 20);
    printf("  Core: %u MHz\n", config.coreFreq / 1000000);
    printf("  Memory: %u MHz\n", config.memoryFreq / 1000000);
    
    return 0;
}

Constants Reference

Memory Locations

GCM_LOCATION_RSX     // RSX video memory (fast)
GCM_LOCATION_CELL    // Main memory (flexible)
GCM_LOCATION_REPORT  // Report memory

DMA Contexts

GCM_DMA_MEMORY_FRAME_BUFFER  // Frame buffer DMA
GCM_DMA_MEMORY_HOST_BUFFER   // Host buffer DMA
GCM_CONTEXT_SURFACE2D        // 2D surface context
GCM_CONTEXT_SWIZZLE2D        // 2D swizzle context

Transfer Types

GCM_TRANSFER_LOCAL_TO_LOCAL  // RSX to RSX
GCM_TRANSFER_MAIN_TO_LOCAL   // Main to RSX
GCM_TRANSFER_LOCAL_TO_MAIN   // RSX to Main
GCM_TRANSFER_MAIN_TO_MAIN    // Main to Main

Performance Tips

1

Use Optimized FIFO Mode

Set GCM_DEFAULT_FIFO_MODE_OPTIMIZE for better performance.
2

Minimize Flushes

Batch commands together before flushing to reduce CPU-GPU synchronization overhead.
3

Avoid rsxFinish()

Only call rsxFinish() when absolutely necessary. It causes CPU stalls.
4

Use RSX Memory

Allocate frequently-accessed data in RSX memory (GCM_LOCATION_RSX) for faster access.
5

Align Memory Properly

Always use 64-byte or 128-byte alignment for optimal DMA performance.

API Reference

Initialize GCM context.
s32 gcmInitBody(gcmContextData **ctx, u32 cmdSize,
                u32 ioSize, const void *ioAddress);
Convert address to RSX offset.
s32 gcmAddressToOffset(const void *address, u32 *offset);
Map main memory for RSX access.
s32 gcmMapMainMemory(const void *address, u32 size, u32 *offset);
Get RSX system configuration.
s32 gcmGetConfiguration(gcmConfiguration *config);
Get control register access.
gcmControlRegister* gcmGetControlRegister();

Build docs developers (and LLMs) love