The 3DS graphics pipeline is driven by a proprietary GPU called the PICA200, which implements a fixed-function vertex and fragment pipeline unlike any standard graphics API. Azahar emulates the PICA200 through a layered design:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/azahar-emu/azahar/llms.txt
Use this file to discover all available pages before exploring further.
VideoCore::GPU is the high-level interface to the core, Pica::PicaCore holds all PICA200 register state and command processing, and a pluggable VideoCore::RendererBase translates that state to the host GPU.
Component overview
GPU (high-level interface)
VideoCore::GPU (src/video_core/gpu.h) receives GSP commands from the HLE service layer, executes memory fills and DMA transfers, and signals VBlank interrupts on a timer tied to the 3DS frame rate (4,481,136 ARM11 cycles per frame).PICA200 core
Pica::PicaCore (in src/video_core/pica/) holds the full PICA200 register file and processes command lists submitted by the game. All renderer backends read from this shared register state.Renderer base
VideoCore::RendererBase (src/video_core/renderer_base.h) is the abstract renderer interface. Backends inherit from it and implement SwapBuffers(), TryPresent(), and Rasterizer().Rasterizer cache
src/video_core/rasterizer_cache/ provides surface and texture caching shared across the accelerated backends. It tracks GPU memory writes and invalidates or flushes cached surfaces when the game modifies them.The GPU class
VideoCore::GPU is created inside Core::System and owned by the std::unique_ptr<VideoCore::GPU> gpu member. It exposes the interface that the HLE GSP service (service/gsp/gsp_gpu.h) calls to submit work:
VBlankCallback, which fires every FRAME_TICKS (4,481,136) ARM11 clock cycles — the value measured on real hardware.
Renderer backends
VideoCore::CreateRenderer() (in src/video_core/video_core.h) inspects the active graphics API setting and constructs the appropriate RendererBase subclass:
OpenGL
src/video_core/renderer_opengl/ — The primary accelerated backend. Uses OpenGL 4.3 core profile. Supports resolution upscaling, anisotropic filtering, texture filtering, and disk shader caching.Vulkan
src/video_core/renderer_vulkan/ — The secondary accelerated backend. Targets Vulkan 1.1+. Provides the same feature set as the OpenGL backend and is preferred on platforms where OpenGL performance is poor.Software
src/video_core/renderer_software/ — A fully CPU-bound rasterizer that does not require any GPU driver support. Much slower than the accelerated backends; useful for debugging and reference comparison.RendererBase exposes the common interface both backends implement:
RendererSettings (in renderer_base.h) carries atomic flags such as screenshot_requested, bg_color_update_requested, and shader_update_requested that the frontend thread can set safely while the render thread is running.
PICA200 shader emulation
The PICA200 has a proprietary programmable vertex shader (called a “geometry shader” in Nintendo’s documentation) that does not map to GLSL or SPIR-V directly. Azahar emulates it insrc/video_core/shader/:
| File | Description |
|---|---|
shader.h / shader.cpp | Shader unit state — registers, output map, uniform data |
shader_interpreter.h / shader_interpreter.cpp | Interprets PICA shader opcodes one instruction at a time. Always available. |
shader_jit.h / shader_jit.cpp | Dispatcher that selects the JIT compiler based on host architecture |
shader_jit_x64_compiler.h / .cpp | JIT compiler targeting x86-64 using Dynarmic’s assembler |
shader_jit_a64_compiler.h / .cpp | JIT compiler targeting AArch64 |
generator/ | GLSL source generator — produces GLSL vertex shaders from PICA shader binaries for the OpenGL and Vulkan backends |
Rasterizer cache
src/video_core/rasterizer_cache/ implements surface and texture caching for the accelerated backends. Key types:
| File | Class / purpose |
|---|---|
rasterizer_cache.h | Template RasterizerCache<Traits> — the main cache, parameterized per backend |
surface_base.h / surface_params.h | SurfaceBase, SurfaceParams — describe a cached surface: format, dimensions, level count |
framebuffer_base.h | FramebufferBase — cached framebuffer objects |
texture_codec.h | Morton-order swizzle/deswizzle and pixel format conversions for PICA texture data |
pixel_format.h | Enumeration of all PICA pixel formats and their host equivalents |
VideoCore::GPU::FlushRegion() and InvalidateRegion() are the two entry points that keep the cache coherent when the CPU writes into GPU-mapped memory.
Custom textures
VideoCore::CustomTexManager (src/video_core/custom_textures/custom_tex_manager.h) manages texture replacement packs. When a game samples a texture, the rasterizer cache computes a hash of the original pixel data and checks whether a replacement exists:
Material (material.h) represents a single replacement asset, which may contain multiple mip levels. Async uploads are queued in async_uploads and drained during TickFrame() to avoid stalling the render thread.
You can dump the original textures by enabling the texture dump option in settings. Dumped textures are saved as PNG files named by their data hash, which you can then replace with higher-resolution artwork and redistribute as a texture pack.
Stereoscopic 3D
The 3DS top screen can display stereoscopic 3D content by rendering a separate right-eye frame. Azahar handles this throughVideoCore::RightEyeDisabler (src/video_core/right_eye_disabler.h):
GPU owns a std::unique_ptr<RightEyeDisabler> and exposes it via GPU::GetRightEyeDisabler(). When stereoscopic output is disabled, RightEyeDisabler intercepts GSP command-queue triggers and display-transfer commands that correspond to the right-eye frame, dropping them before they reach the rasterizer. This saves roughly half the GPU work for games that render both eyes.
Host shaders
src/video_core/host_shaders/ contains the GLSL and SPIR-V (via glslang) shaders that Azahar’s OpenGL and Vulkan renderers use for presentation and post-processing:
| Shader file | Purpose |
|---|---|
opengl_present.vert / .frag | Standard screen blit for the OpenGL backend |
opengl_present_anaglyph.frag | Red-cyan anaglyph 3D output (OpenGL) |
opengl_present_interlaced.frag | Interlaced stereoscopic output (OpenGL) |
vulkan_present.vert / .frag | Standard screen blit for the Vulkan backend |
vulkan_present_anaglyph.frag | Red-cyan anaglyph 3D output (Vulkan) |
vulkan_present_interlaced.frag | Interlaced stereoscopic output (Vulkan) |
vulkan_depth_to_buffer.comp | Compute shader for reading back depth values |
format_reinterpreter/ | Pixel-format conversion shaders (e.g., D24S8 → RGBA8) |
texture_filtering/ | Upscaling filter shaders (xBRZ, bicubic, etc.) |
source_shader.h.in template and the StringShaderHeader.cmake helper. At runtime the renderer compiles them with the host driver.