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

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, 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 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. 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

#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:


# Pre-process with ImageMagick to create indexed image

convert big_sprite.png -colors 256 big_sprite_ci8.png
// 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:
#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, 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 and enforced in 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →