Architecture¶
opengnm is structured as a layered library with a shared core and two platform-specific backends. This page describes the subsystems, data flow, and design principles.
Subsystem Overview¶
graph TB
subgraph "Public API Headers"
TYPES[gnm_types.h<br/>Enums & Constants]
ERROR[gnm_error.h<br/>Error Codes]
DF[gnm_dataformat.h<br/>Data Formats]
RES[Resource Descriptors<br/>buffer, texture, sampler,<br/>rendertarget, depthrt]
CTRL[gnm_controls.h<br/>Control Registers]
SH[gnm_shader.h<br/>Stage Registers & Fetch]
SHB[gnm_shaderbinary.h<br/>Shader Binary Format]
CB[gnm_commandbuffer.h<br/>Command Buffer]
DCB[gnm_drawcommandbuffer.h<br/>Draw Command Buffer]
DRV[gnmdriver.h<br/>sceGnm* Runtime]
GPA[gpuaddr.h<br/>sceGpa* Surface Math]
PLAT[platform.h<br/>GPU Mode Detection]
HELP[gnm_helpers.h<br/>VideoOut & Memory Helpers]
end
subgraph "Core Implementation"
GCN[GCN Assembler<br/>src/gcn/]
PM4[PM4 Encoder<br/>src/pm4/]
GPA_IMPL[GpuAddr / AddrLib<br/>src/gpuaddr/]
CORE[Resource Logic<br/>src/*.c]
end
subgraph "Backends"
ORBIS[driver_orbis.c<br/>Firmware Delegation]
GENERIC[driver_generic.c<br/>PM4 Emission]
end
TYPES --> DF
DF --> RES
RES --> CB
CB --> DCB
SH --> DCB
SHB --> HELP
CTRL --> DCB
DCB --> DRV
GPA --> RES
PLAT --> HELP
CORE --> GCN
CORE --> PM4
CORE --> GPA_IMPL
DRV --> ORBIS
DRV --> GENERIC
DCB --> ORBIS
DCB --> GENERIC
Design Principles¶
1. ABI Compatibility First¶
Every public struct uses _Static_assert to verify its binary size matches the
Sony SDK layout. This ensures that code compiled against the official SDK
headers produces identical binary descriptors.
_Static_assert(sizeof(GnmTexture) == 0x20, "");
_Static_assert(sizeof(GnmRenderTarget) == 0x40, "");
_Static_assert(sizeof(GnmCommandBuffer) == 0x40, "");
2. Header-Only Where Possible¶
Many accessor functions are static inline in the headers, meaning they
generate no library symbols and can be used without linking against opengnm.
This includes texture, buffer, render target, and depth render target getters
and setters.
3. Two Backends, One API¶
The same sceGnm* function signatures are implemented differently depending on
the selected backend:
| Aspect | Orbis Backend | Generic Backend |
|---|---|---|
| File | src/driver_orbis.c |
src/driver_generic.c |
| Target | PS4 hardware | Host (macOS/Linux/Windows) |
| Submit | Forwards to libSceGnmDriver firmware |
No-op / PM4 buffer capture |
| Draw commands | Firmware packet building | Direct PM4 emission |
| Use case | Production PS4 apps | Testing, development, CI |
See Backends for details.
4. Source-Level freegnm Compatibility¶
The <compat/freegnm.h> header provides #define aliases that map old gnm*
wrapper names to sceGnm* ABI symbols. This is source-only — no second set of
exported symbols is created. Existing freegnm consumers can switch to opengnm
by changing their include path.
Data Flow¶
Typical Rendering Frame¶
sequenceDiagram
participant App
participant CB as Command Buffer
participant DCB as Draw Cmd Buffer
participant DRV as sceGnm Driver
participant FW as Firmware
participant VO as VideoOut
App->>VO: sceGnmVideoOutOpen()
App->>CB: sceGnmCmdInit(buffer, size, ...)
App->>DCB: sceGnmDrawCmdInitDefaultHardwareState(cmd)
App->>DCB: sceGnmDrawCmdSetRenderTarget(cmd, 0, &rt)
App->>DCB: sceGnmDrawCmdSetVsShader(cmd, &vsRegs, 0)
App->>DCB: sceGnmDrawCmdSetPsShader(cmd, &psRegs)
App->>DCB: sceGnmDrawCmdSetPrimitiveType(cmd, GNM_PT_TRILIST)
App->>DCB: sceGnmDrawCmdDrawIndexAuto(cmd, vertexCount)
App->>DRV: sceGnmSubmitAndFlipCommandBuffers(...)
DRV->>FW: libSceGnmDriver submit
FW->>VO: Flip display buffer
App->>VO: sceGnmVideoOutSubmitFlipAndWait()
Surface Setup Flow¶
graph LR
A[Choose format<br/>GnmDataFormat] --> B[sceGpaComputeSurfaceInfo]
B --> C[Allocate direct memory<br/>sceGnmDirectMemoryAllocate]
C --> D[Create descriptor<br/>sceGnmCreateTexture /<br/>sceGnmCreateRenderTarget]
D --> E[Bind to command buffer<br/>sceGnmDrawCmdSetRenderTarget]
Source Layout¶
opengnm/
├── include/ # Public API headers
│ ├── gnm.h # Master include
│ ├── gnm_types.h # Enums, constants, limits
│ ├── gnm_error.h # Error codes
│ ├── gnm_dataformat.h # Data formats
│ ├── gnm_buffer.h # Buffer descriptor
│ ├── gnm_texture.h # Texture descriptor
│ ├── gnm_sampler.h # Sampler descriptor
│ ├── gnm_rendertarget.h # Render target descriptor
│ ├── gnm_depthrendertarget.h
│ ├── gnm_controls.h # Control register structs
│ ├── gnm_shader.h # Shader stage registers
│ ├── gnm_shaderbinary.h # Shader binary format
│ ├── gnm_commandbuffer.h
│ ├── gnm_drawcommandbuffer.h
│ ├── gnmdriver.h # sceGnm* runtime (207+ functions)
│ ├── gpuaddr.h # sceGpa* surface computation
│ ├── platform.h # GPU mode detection
│ ├── gnm_helpers.h # VideoOut, memory, validation
│ ├── gnm_strings.h # Enum string converters
│ ├── compat/freegnm.h # freegnm source compatibility
│ └── gnm/ # Forwarding headers for compat
├── src/ # Implementation
│ ├── *.c # Core resource logic
│ ├── driver_orbis.c # Orbis backend
│ ├── driver_generic.c # Generic backend
│ ├── platform_orbis.c
│ ├── platform_generic.c
│ ├── gcn/ # GCN assembler (fetch shaders)
│ ├── pm4/ # PM4 packet encoder
│ ├── gpuaddr/ # AddrLib surface computation
│ └── u/ # Internal utilities
├── tests/ # Test suite (54+ tests)
├── CMakeLists.txt # CMake build
├── Makefile # Make build
└── config.{generic,orbis}.mak
Key Constants¶
| Constant | Value | Description |
|---|---|---|
GNM_NUM_SHADER_STAGES |
8 | Number of shader stages |
GNM_MAX_RENDERTARGETS |
8 | Maximum simultaneous render targets |
GNM_MAX_VIEWPORTS |
16 | Maximum simultaneous viewports |
GNM_MAX_TSHARP_USERDATA_SLOTS |
8 | Max texture user data slots |
GNM_MAX_SSHARP_USERDATA_SLOTS |
12 | Max sampler user data slots |
GNM_MAX_VSHARP_USERDATA_SLOTS |
12 | Max buffer user data slots |
GNM_MAX_POINTER_USERDATA_SLOTS |
14 | Max pointer user data slots |
GNM_ALIGNMENT_SHADER_BYTES |
256 | Shader code alignment |
GNM_ALIGNMENT_BUFFER_BYTES |
4 | Buffer base address alignment |
GNM_INDIRECT_BUFFER_MAX_BYTESIZE |
0x3FFFFC | Max indirect buffer size |