# Practical Limitations and Memory Considerations When Using Big-Textures (256x256) in Pyrite64 Projects

> Explore practical limitations and memory considerations for using 256x256 big-textures in Pyrite64 projects. Learn VRAM management to prevent crashes.

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

---

**Pyrite64 supports rendering textures up to 256×256 pixels, but each big-texture consumes significant portions of the Nintendo 64’s 4 MiB VRAM budget, requiring careful format selection and memory management to avoid runtime crashes.**

The **Pyrite64** engine enables developers to utilize **big-textures** (2⁶×2⁶) for high-resolution sprites, UI panels, and full-screen backgrounds. While this feature expands visual fidelity beyond standard N64 texture sizes, it introduces strict hardware constraints and memory trade-offs defined by the console’s GPU architecture and the engine’s texture allocator.

## Hardware Constraints for 256×256 Textures in Pyrite64

The Nintendo 64 GPU imposes hard limits on texture dimensions and memory alignment that Pyrite64 must enforce through its validation logic.

### Maximum Dimensions and Power-of-Two Requirements

The N64 GPU natively supports textures up to **256×256 pixels** (or 512×512 for specific formats), with all dimensions required to be powers of two. In [`src/renderer/texture.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/texture.h), the `Renderer::Texture` class validates these constraints through the `rasterWidth` and `rasterHeight` constructor parameters. Attempting to load textures exceeding these limits results in silent down-scaling or initialization failure.

### Tile Alignment and VRAM Budget

N64 texture memory stores images as 4×4 pixel tiles, meaning width and height must be multiples of four. The `Renderer::Texture` constructor in [`src/renderer/texture.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/texture.cpp) automatically pads non-aligned images to meet this requirement. More critically, the console provides only **4 MiB of VRAM**. A single 256×256 RGBA16 texture occupies **128 KiB**, while an RGBA32 texture consumes **256 KiB**—rapidly exhausting the available pool when multiple big-textures are loaded simultaneously.

## Memory Usage by Texture Format

Pyrite64 automatically selects the most compact compatible format based on source image properties, as defined in [`src/utils/textureFormats.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/utils/textureFormats.h). Understanding these format-specific costs is essential for VRAM budgeting:

- **CI8 (Indexed Color)**: Uses **1 byte per pixel** (64 KiB for 256×256) plus a small palette. Ideal for artwork with ≤256 colors.
- **RGBA16 (Default)**: Uses **2 bytes per pixel** (128 KiB for 256×256). The standard choice for general-purpose sprites and UI elements.
- **RGBA32 (High Fidelity)**: Uses **4 bytes per pixel** (256 KiB for 256×256). Reserved for HDR textures or artwork requiring full color precision.

The engine’s texture allocator tracks total consumption against the 4 MiB hardware limit, throwing exceptions when allocations exceed available VRAM.

## Loading Big-Textures in Code

To load a 256×256 texture, explicitly specify the raster dimensions in the constructor to force the big-texture code path.

### Loading a 256×256 Background

```cpp
#include "renderer/texture.h"

void Scene::loadBigBackground()
{
    // Force 256×256 raster size (2⁶×2⁶)
    // Source image will be resized if dimensions differ
    auto* bgTex = new Renderer::Texture(
        gpuDevice,
        "data/img/big_background.png",
        false,      // not monochrome
        256,        // rasterWidth
        256         // rasterHeight
    );
    
    backgroundTexture = bgTex;
}

```

### Optimizing with Indexed CI8 Format

Convert source images to 8-bit paletted formats to halve memory usage compared to RGBA16:

```bash

# Pre-process with ImageMagick to create indexed image

convert big_sprite.png -colors 256 big_sprite_ci8.png

```

```cpp
// Engine auto-detects CI8 format based on file properties
auto* spriteTex = new Renderer::Texture(
    gpuDevice, 
    "data/img/big_sprite_ci8.png"
);
// Memory impact: ~64 KiB + 1 KiB palette vs 128 KiB RGBA16

```

## Managing VRAM in Pyrite64 Projects

Effective VRAM management requires monitoring and architectural discipline:

- **Minimize big-texture count**: Ten 256×256 RGBA16 textures consume over 1 MiB—one-quarter of total VRAM.
- **Share texture instances**: The `Renderer::Texture` class lacks reference counting; implement a resource manager to reuse textures across scenes rather than duplicating allocations.
- **Monitor allocation**: Access the global counter during debugging to track consumption:

```cpp
#include "renderer/texture.h"

void debugVRAM()
{
    extern size_t gTextureMemoryUsed;  // Defined in texture.cpp
    printf("VRAM: %zu KiB / 4096 KiB\n", gTextureMemoryUsed / 1024);
}

```

- **Test on accurate emulators**: Validate VRAM usage on Ares v147+ or Gopher64 to ensure compatibility with hardware constraints.

## Summary

- Pyrite64 supports **256×256 big-textures** (2⁶×2⁶) through the `Renderer::Texture` class by setting `rasterWidth` and `rasterHeight` to 256.
- Hardware limits enforce **power-of-two dimensions**, **4×4 tile alignment**, and a strict **4 MiB VRAM ceiling**.
- **CI8 indexed formats** reduce 256×256 texture costs to **64 KiB**, while **RGBA16** requires **128 KiB** and **RGBA32** requires **256 KiB**.
- The engine tracks allocations in [`src/renderer/texture.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/texture.cpp), throwing exceptions when VRAM is exhausted.
- Reuse texture instances and prefer indexed formats to maximize available memory for rendering.

## Frequently Asked Questions

### What is the maximum texture size supported by Pyrite64?

Pyrite64 supports textures up to **256×256 pixels** (512×512 for specific formats), constrained by the Nintendo 64 GPU hardware limits documented in the engine’s [`Readme.md`](https://github.com/HailToDodongo/pyrite64/blob/main/Readme.md) and enforced in [`src/renderer/texture.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/texture.cpp). Larger images are either down-scaled or rejected during initialization.

### How much VRAM does a 256×256 texture consume?

Memory consumption depends on the format: **64 KiB** for CI8 (plus palette), **128 KiB** for RGBA16, and **256 KiB** for RGBA32. The `Renderer::Texture` constructor in [`src/renderer/texture.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/texture.cpp) allocates this memory from the engine’s bounded VRAM pool, which cannot exceed the N64’s 4 MiB limit.

### Can I use non-power-of-two textures with Pyrite64?

No. The N64 GPU requires texture dimensions to be powers of two. The `Renderer::Texture` constructor validates `rasterWidth` and `rasterHeight` against this requirement, and non-compliant images must be padded or resized before upload.

### How do I optimize memory usage for large backgrounds?

Convert backgrounds to **CI8 indexed format** using tools like ImageMagick to reduce memory by 50% compared to RGBA16. Load the texture once and share the pointer across scenes, as Pyrite64 does not implement automatic reference counting. Monitor `gTextureMemoryUsed` during development to ensure you stay within the 4 MiB hardware budget.