# How Framebuffer Configuration Impacts N64 GPU Performance and Memory Bandwidth in Pyrite64

> Discover how framebuffer configuration impacts N64 GPU performance and memory bandwidth on Pyrite64. Learn how width, height, and format affect RDP fill-rate and bandwidth limits.

- Repository: [Max Bebök/pyrite64](https://github.com/hailtododongo/pyrite64)
- Tags: performance
- Published: 2026-02-19

---

**Framebuffer width, height, and pixel format directly determine memory bandwidth consumption on the N64, where RGBA32 doubles the data load compared to RGBA16 and larger resolutions linearly increase fill-rate pressure against the RDP's ~15 MiB/s limit.**

The Pyrite64 engine exposes these hardware-sensitive parameters through the `SceneConf` struct, allowing developers to balance visual fidelity against the Nintendo 64's constrained 4 MiB VRAM budget. Understanding how `fbWidth`, `fbHeight`, and `fbFormat` propagate from editor settings to runtime surface allocation is essential for maintaining stable frame times.

## Where Framebuffer Settings Are Defined in the Source

The configuration originates in the project layer and flows through the build system into the engine runtime.

In [`src/project/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/scene/scene.h), the `SceneConf` struct stores the user-defined parameters:

```cpp
// src/project/scene/scene.h (lines 30-34)
struct SceneConf {
  uint16_t fbWidth = 320;
  uint16_t fbHeight = 240;
  uint8_t fbFormat = 0;  // 0 = RGBA16, 1 = RGBA32
  // ...
};

```

The [`src/build/sceneBuilder.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/build/sceneBuilder.cpp) module serializes these values into the binary scene file consumed by the N64 executable:

```cpp
// src/build/sceneBuilder.cpp (lines 37-44)
fileScene.write<uint16_t>(sc->conf.fbWidth);
fileScene.write<uint16_t>(sc->conf.fbHeight);
fileScene.write<uint8_t>(sc->conf.fbFormat);

```

At runtime, [`n64/engine/include/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/include/scene/scene.h) defines the `FLAG_SCR_32BIT` constant, which the render pipelines query to determine surface allocation:

```cpp
// n64/engine/include/scene/scene.h (lines 45-47)
enum SceneFlags {
  FLAG_SCR_32BIT = 1 << 0,
  // ...
};

```

## How Resolution Impacts Memory Bandwidth

The product of `fbWidth` and `fbHeight` determines the total pixel count the RDP must process each frame. On the N64, memory bandwidth scales linearly with this pixel count because every operation—clearing, rendering, and VI output—must touch every byte.

For a standard 320×240 resolution:

- **RGBA16**: 320 × 240 × 2 bytes = ~150 KB per framebuffer
- **RGBA32**: 320 × 240 × 4 bytes = ~300 KB per framebuffer

With double-buffering (standard for tear-free rendering), these values double, consuming 300 KB or 600 KB of the 4 MiB VRAM respectively. The remaining memory must accommodate textures, depth buffers, and audio buffers, leaving minimal headroom for high-resolution framebuffers.

The RDP's empirical fill-rate ceiling of approximately 15 MiB/s means that increasing framebuffer dimensions directly reduces the maximum sustainable frame rate. A 640×480 RGBA16 buffer requires ~600 KB per frame, consuming 4 MiB of bandwidth at 60 Hz—approaching the hardware limit before any geometry or texture fetches are considered.

## How Pixel Format Impacts GPU Performance

The `fbFormat` parameter selects between **RGBA16** (2 bytes/pixel) and **RGBA32** (4 bytes/pixel). This choice has multiplicative effects on memory bandwidth and cache efficiency.

In [`n64/engine/src/renderer/pipelineDefault.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/src/renderer/pipelineDefault.cpp), the engine checks the `FLAG_SCR_32BIT` flag to determine which surface format to allocate:

```cpp
// n64/engine/src/renderer/pipelineDefault.cpp (lines 30-34)
if (scene.getConf().flags & P64::SceneConf::FLAG_SCR_32BIT) {
  fmt = FMT_RGBA32;
} else {
  fmt = FMT_RGBA16;
}

```

Switching to RGBA32 doubles the memory traffic for every framebuffer operation:

- **Clear operations** must write twice as many bytes
- **RDP rendering** transfers double the data across the RDRAM bus
- **VI scanning** reads twice the data to output to the display

The N64's **RDRAM cache** (8 KB) sees increased pressure with RGBA32, as fewer pixels fit into cache lines, causing more frequent evictions and memory stalls.

However, not all pipelines support RGBA32. The [`n64/engine/src/renderer/pipelineHDRBloom.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/n64/engine/src/renderer/pipelineHDRBloom.cpp) and [`pipelineBigTex.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/pipelineBigTex.cpp) modules contain static assertions that enforce RGBA16 exclusivity:

```cpp
// n64/engine/src/renderer/pipelineHDRBloom.cpp (lines 36-38)
assert(!(scene.getConf().flags & P64::SceneConf::FLAG_SCR_32BIT) && 
       "HDR-Bloom pipeline only supports RGBA16");

```

These restrictions exist because the microcode and surface allocation logic for these effects are optimized specifically for 16-bit pixel operations. Attempting to use RGBA32 with these pipelines triggers a runtime abort, protecting developers from undefined behavior or severe performance degradation.

## Practical Configuration Examples

### Configuring via JSON Scene Files

Developers can specify framebuffer parameters directly in the scene JSON:

```json
{
  "fbWidth": 320,
  "fbHeight": 240,
  "fbFormat": 0,
  "renderPipeline": 0
}

```

The `fbFormat` value maps to the `TexFormat` enum defined in [`src/utils/textureFormats.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/utils/textureFormats.h):

```cpp
// src/utils/textureFormats.h (lines 10-13)
enum TexFormat {
  TEX_FMT_RGBA16 = 0,
  TEX_FMT_RGBA32 = 1,
  // ...
};

```

### Using the Editor Interface

The [`src/editor/pages/parts/sceneInspector.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/editor/pages/parts/sceneInspector.cpp) module exposes these settings through an ImGui interface:

```cpp
// src/editor/pages/parts/sceneInspector.cpp (lines 58-60)
constexpr const char* const FORMATS[] = {"RGBA16","RGBA32"};
ImTable::addComboBox("Format", scene->conf.fbFormat, FORMATS, 2);

```

The editor automatically disables the RGBA32 option when the HDR-Bloom or BigTex pipelines are selected, preventing invalid configurations.

### Runtime Flag Checking

Render pipelines access the configuration through the scene header flags:

```cpp
// Runtime check pattern used across pipelines
if (scene.getConf().flags & P64::SceneConf::FLAG_SCR_32BIT) {
    // Execute RGBA32-specific path
} else {
    // Execute RGBA16 path
}

```

This pattern appears in [`pipelineDefault.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/pipelineDefault.cpp) during surface initialization and determines which RDP surface format constant to pass to the graphics microcode.

## Summary

- **Framebuffer dimensions** (`fbWidth` × `fbHeight`) linearly scale memory bandwidth requirements and RDP fill-rate consumption on the N64.
- **Pixel format selection** (`fbFormat`) doubles memory traffic when switching from RGBA16 to RGBA32, impacting cache efficiency and VI output bandwidth.
- **VRAM constraints** mean that high-resolution RGBA32 framebuffers can consume over 600 KB of the 4 MiB budget, leaving insufficient space for textures and depth buffers.
- **Pipeline compatibility** restricts RGBA32 usage to the Default pipeline only; HDR-Bloom and BigTex pipelines enforce RGBA16 through runtime assertions.
- **Configuration flow** propagates from `SceneConf` in [`src/project/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/scene/scene.h) through the scene builder to runtime flags checked by [`pipelineDefault.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/pipelineDefault.cpp).

## Frequently Asked Questions

### What is the default framebuffer configuration in Pyrite64?

The default configuration uses a resolution of 320×240 pixels with the RGBA16 format (2 bytes per pixel). This is defined in the `SceneConf` struct in [`src/project/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/scene/scene.h) and provides a balance between visual quality and memory bandwidth suitable for the N64's hardware constraints.

### Why does switching to RGBA32 halve the frame rate on N64?

RGBA32 uses 4 bytes per pixel compared to RGBA16's 2 bytes, doubling the memory bandwidth required for every framebuffer operation including clearing, RDP rendering, and VI scanning. Given the N64's RDRAM bandwidth limitations and RDP fill-rate ceiling of approximately 15 MiB/s, this doubling often pushes the system past sustainable performance thresholds.

### Which render pipelines support RGBA32 framebuffer format?

Only the **Default** pipeline (`RenderPipelineDefault`) supports RGBA32. The HDR-Bloom and BigTex pipelines contain static assertions in [`pipelineHDRBloom.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/pipelineHDRBloom.cpp) and [`pipelineBigTex.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/pipelineBigTex.cpp) that abort execution if `FLAG_SCR_32BIT` is set, as their microcode and surface allocation logic are optimized exclusively for 16-bit pixel operations.