emoemu 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +77 -0
- package/.node-version +1 -0
- package/CLAUDE.md +435 -0
- package/README.md +404 -0
- package/TODO.md +655 -0
- package/dist/index.cjs +25108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +25085 -0
- package/docs/building-libretro-cores-arm-mac.md +237 -0
- package/docs/config-file-format.md +488 -0
- package/docs/cores-trd.md +425 -0
- package/docs/headless-hardware-rendering-trd.md +676 -0
- package/docs/libretro-cores-trd.md +997 -0
- package/docs/mupen64-software-rendering-trd.md +751 -0
- package/docs/n64-support-trd.md +306 -0
- package/docs/native-rendering-trd.md +540 -0
- package/docs/native-ui-rendering-trd.md +1195 -0
- package/docs/netplay-trd.md +665 -0
- package/docs/retroarch-netplay-docs.md +277 -0
- package/docs/save-state-format.md +666 -0
- package/eslint.config.js +111 -0
- package/icon/icon.png +0 -0
- package/icon/icon.pxd +0 -0
- package/package.json +63 -0
- package/pnpm-workspace.yaml +10 -0
- package/src/Emulator/consts.ts +14 -0
- package/src/Emulator/index.ts +2496 -0
- package/src/Emulator/saveState/index.ts +155 -0
- package/src/Emulator/screenshot/index.ts +160 -0
- package/src/Emulator/terminalDimensions/index.ts +79 -0
- package/src/Emulator/types.ts +83 -0
- package/src/cli/commands/consts.ts +10 -0
- package/src/cli/commands/index.ts +462 -0
- package/src/cli/parseArgs/consts.ts +17 -0
- package/src/cli/parseArgs/index.ts +457 -0
- package/src/cli/parseArgs/types.ts +61 -0
- package/src/cli/runEmulator/index.ts +406 -0
- package/src/cli/runEmulator/types.ts +7 -0
- package/src/consts.ts +19 -0
- package/src/core/button/consts.ts +35 -0
- package/src/core/button/index.ts +123 -0
- package/src/core/core.ts +300 -0
- package/src/core/index.ts +19 -0
- package/src/cores/libretro/api/index.ts +334 -0
- package/src/cores/libretro/api/types.ts +148 -0
- package/src/cores/libretro/callbacks/consts.ts +41 -0
- package/src/cores/libretro/callbacks/index.ts +456 -0
- package/src/cores/libretro/consts.ts +45 -0
- package/src/cores/libretro/coreOptions/consts.ts +36 -0
- package/src/cores/libretro/coreOptions/index.ts +222 -0
- package/src/cores/libretro/environment/consts.ts +118 -0
- package/src/cores/libretro/environment/index.ts +1095 -0
- package/src/cores/libretro/index.ts +937 -0
- package/src/cores/libretro/loader/index.ts +496 -0
- package/src/cores/libretro/pixelFormat/consts.ts +43 -0
- package/src/cores/libretro/pixelFormat/index.ts +397 -0
- package/src/cores/libretro/types.ts +339 -0
- package/src/frontend/AudioManager/index.ts +420 -0
- package/src/frontend/SettingsManager/index.ts +250 -0
- package/src/frontend/config/index.ts +608 -0
- package/src/frontend/config/tests.ts +354 -0
- package/src/frontend/config/types.ts +36 -0
- package/src/frontend/consts.ts +114 -0
- package/src/frontend/coreBuilder/index.ts +644 -0
- package/src/frontend/coreBuilder/types.ts +15 -0
- package/src/frontend/coreDownloader/index.ts +620 -0
- package/src/frontend/coreDownloader/types.ts +17 -0
- package/src/frontend/corePreferences/index.ts +69 -0
- package/src/frontend/coreRegistry/index.ts +276 -0
- package/src/frontend/directoryCache/index.ts +75 -0
- package/src/frontend/index.ts +79 -0
- package/src/frontend/notifications/consts.ts +14 -0
- package/src/frontend/notifications/index.ts +250 -0
- package/src/frontend/playlist/consts.ts +168 -0
- package/src/frontend/playlist/index.ts +899 -0
- package/src/frontend/playlist/labelFormatter/consts.ts +15 -0
- package/src/frontend/playlist/labelFormatter/index.ts +155 -0
- package/src/frontend/playlist/labelFormatter/tests.ts +153 -0
- package/src/frontend/playlist/reader/index.ts +559 -0
- package/src/frontend/playlist/sync/index.ts +511 -0
- package/src/frontend/playlist/systemLookup/index.ts +233 -0
- package/src/frontend/playlist/utils/index.ts +50 -0
- package/src/frontend/romScanner/consts.ts +348 -0
- package/src/frontend/romScanner/index.ts +1957 -0
- package/src/frontend/saveServices/consts.ts +2 -0
- package/src/frontend/saveServices/index.ts +313 -0
- package/src/frontend/serviceProvider/index.ts +108 -0
- package/src/frontend/serviceProvider/types.ts +13 -0
- package/src/index.ts +428 -0
- package/src/input/Controller/consts.ts +50 -0
- package/src/input/Controller/index.ts +81 -0
- package/src/input/GamepadManager/consts.ts +22 -0
- package/src/input/GamepadManager/index.ts +418 -0
- package/src/input/InputManager/consts.ts +86 -0
- package/src/input/InputManager/index.ts +593 -0
- package/src/input/InputMapper/consts.ts +33 -0
- package/src/input/InputMapper/index.ts +436 -0
- package/src/input/consts.ts +410 -0
- package/src/input/gamepadProfiles/index.ts +1100 -0
- package/src/input/index.ts +38 -0
- package/src/input/inputUtils/index.ts +31 -0
- package/src/netplay/FrameBuffer/consts.ts +2 -0
- package/src/netplay/FrameBuffer/index.ts +364 -0
- package/src/netplay/FrameBuffer/tests.ts +286 -0
- package/src/netplay/InputBuffer/consts.ts +7 -0
- package/src/netplay/InputBuffer/index.ts +347 -0
- package/src/netplay/InputBuffer/tests.ts +166 -0
- package/src/netplay/NetplayClient/index.ts +976 -0
- package/src/netplay/NetplayConnection/index.ts +536 -0
- package/src/netplay/NetplayDiscovery/consts.ts +41 -0
- package/src/netplay/NetplayDiscovery/index.ts +525 -0
- package/src/netplay/NetplayServer/index.ts +1407 -0
- package/src/netplay/SyncManager/index.ts +984 -0
- package/src/netplay/SyncManager/tests.ts +419 -0
- package/src/netplay/consts.ts +371 -0
- package/src/netplay/crc32/consts.ts +14 -0
- package/src/netplay/crc32/index.ts +97 -0
- package/src/netplay/crc32/tests.ts +40 -0
- package/src/netplay/index.ts +41 -0
- package/src/netplay/netplayLogger/consts.ts +30 -0
- package/src/netplay/netplayLogger/index.ts +345 -0
- package/src/netplay/protocol/consts.ts +86 -0
- package/src/netplay/protocol/index.ts +1280 -0
- package/src/netplay/protocol/tests.ts +606 -0
- package/src/netplay/protocol/types.ts +20 -0
- package/src/netplay/types.ts +395 -0
- package/src/rendering/KittyRenderer/index.ts +616 -0
- package/src/rendering/NativeRenderer/index.ts +279 -0
- package/src/rendering/NativeRenderer/tests.ts +133 -0
- package/src/rendering/TerminalRenderer/index.ts +770 -0
- package/src/rendering/consts.ts +401 -0
- package/src/rendering/fonts/CozetteVector.ttf +0 -0
- package/src/rendering/index.ts +26 -0
- package/src/rendering/nativeUi/NativeWindowManager/index.ts +158 -0
- package/src/rendering/nativeUi/NativeWindowManager/tests.ts +81 -0
- package/src/rendering/nativeUi/consts.ts +6 -0
- package/src/rendering/nativeUi/index.ts +20 -0
- package/src/rendering/postProcessing/consts.ts +38 -0
- package/src/rendering/postProcessing/index.ts +923 -0
- package/src/rendering/shared/ansi/consts.ts +12 -0
- package/src/rendering/shared/ansi/index.ts +104 -0
- package/src/rendering/shared/consts.ts +2 -0
- package/src/rendering/shared/fitToTerminal/index.ts +67 -0
- package/src/ui/AddRomsPrompt/consts.ts +13 -0
- package/src/ui/AddRomsPrompt/index.tsx +781 -0
- package/src/ui/App/consts.ts +2 -0
- package/src/ui/App/index.tsx +456 -0
- package/src/ui/AppCapabilities/index.tsx +67 -0
- package/src/ui/ConfigContext/index.tsx +56 -0
- package/src/ui/CoreManager/consts.ts +11 -0
- package/src/ui/CoreManager/index.tsx +779 -0
- package/src/ui/CoreSelector/consts.ts +2 -0
- package/src/ui/CoreSelector/index.tsx +251 -0
- package/src/ui/DialogContainer/index.tsx +42 -0
- package/src/ui/DialogOptionsList/index.tsx +61 -0
- package/src/ui/DuplicateCrcPrompt/consts.ts +5 -0
- package/src/ui/DuplicateCrcPrompt/index.tsx +146 -0
- package/src/ui/GamepadContext/consts.ts +15 -0
- package/src/ui/GamepadContext/index.tsx +295 -0
- package/src/ui/NativeDialog/index.tsx +120 -0
- package/src/ui/NetplayDisconnectedDialog/index.tsx +93 -0
- package/src/ui/NetplayPauseMenu/consts.ts +2 -0
- package/src/ui/NetplayPauseMenu/index.tsx +97 -0
- package/src/ui/RomBrowser/NetplayPanel/consts.ts +24 -0
- package/src/ui/RomBrowser/NetplayPanel/index.tsx +520 -0
- package/src/ui/RomBrowser/SettingsPanel/index.tsx +478 -0
- package/src/ui/RomBrowser/consts.ts +61 -0
- package/src/ui/RomBrowser/index.tsx +1164 -0
- package/src/ui/RomBrowser/settingsConfig/index.ts +320 -0
- package/src/ui/RomBrowser/types.ts +67 -0
- package/src/ui/SaveStateDialog/consts.ts +2 -0
- package/src/ui/SaveStateDialog/index.tsx +225 -0
- package/src/ui/WarningDialog/index.tsx +113 -0
- package/src/ui/consts.ts +27 -0
- package/src/ui/hooks/useClearTerminal/consts.ts +2 -0
- package/src/ui/hooks/useClearTerminal/index.ts +37 -0
- package/src/ui/hooks/useDialogNavigation/index.ts +99 -0
- package/src/ui/hooks/useGamepad/consts.ts +21 -0
- package/src/ui/hooks/useGamepad/index.ts +194 -0
- package/src/ui/index.ts +27 -0
- package/src/utils/buffer/consts.ts +17 -0
- package/src/utils/buffer/index.ts +129 -0
- package/src/utils/color/consts.ts +58 -0
- package/src/utils/color/index.ts +183 -0
- package/src/utils/compression/consts.ts +50 -0
- package/src/utils/compression/index.ts +101 -0
- package/src/utils/consts.ts +2 -0
- package/src/utils/crc32/consts.ts +22 -0
- package/src/utils/crc32/index.ts +83 -0
- package/src/utils/ensureDirectory/index.ts +10 -0
- package/src/utils/format/consts.ts +8 -0
- package/src/utils/format/index.ts +53 -0
- package/src/utils/getErrorMessage/index.ts +10 -0
- package/src/utils/index.ts +113 -0
- package/src/utils/ini/index.ts +200 -0
- package/src/utils/kitty/consts.ts +13 -0
- package/src/utils/kitty/index.ts +181 -0
- package/src/utils/logger/consts.ts +35 -0
- package/src/utils/logger/index.ts +217 -0
- package/src/utils/paths/consts.ts +18 -0
- package/src/utils/paths/index.ts +151 -0
- package/src/utils/png/consts.ts +34 -0
- package/src/utils/png/index.ts +131 -0
- package/src/utils/readJsonFile/index.ts +16 -0
- package/src/utils/rotateLogFile/index.ts +44 -0
- package/src/utils/safeClose/index.ts +10 -0
- package/src/utils/terminal/consts.ts +8 -0
- package/src/utils/terminal/index.ts +102 -0
- package/src/utils/thumbnailRenderer/consts.ts +2 -0
- package/src/utils/thumbnailRenderer/index.ts +147 -0
- package/src/utils/typedError/index.ts +26 -0
- package/tsconfig.json +31 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
# Mupen64Plus Software-Only Rendering Build - Technical Requirements Document
|
|
2
|
+
|
|
3
|
+
This document describes the requirements and implementation plan for building a software-rendering-only version of the Mupen64Plus-Next libretro core for use in emoemu's terminal-based emulator.
|
|
4
|
+
|
|
5
|
+
## Problem Statement
|
|
6
|
+
|
|
7
|
+
emoemu is a terminal-based emulator that renders graphics using the Kitty graphics protocol, Unicode half-blocks, ASCII, or emoji characters. It has **no access to GPU acceleration** (OpenGL, Vulkan, Metal) because:
|
|
8
|
+
|
|
9
|
+
1. Terminal emulators don't provide GPU context
|
|
10
|
+
2. The emulator runs in headless/terminal environments
|
|
11
|
+
3. All rendering must go through a CPU-generated framebuffer
|
|
12
|
+
|
|
13
|
+
The pre-built Mupen64Plus-Next cores from the RetroArch buildbot **require OpenGL** because they default to GlideN64, a high-level emulation (HLE) renderer that requires OpenGL. Even when configured to use the Angrylion software renderer at runtime, the core still links against OpenGL and may fail to load or initialize in environments without OpenGL support.
|
|
14
|
+
|
|
15
|
+
### Previous Success
|
|
16
|
+
|
|
17
|
+
A custom build of mupen64plus_next was previously created that:
|
|
18
|
+
- Successfully ran N64 games in terminal mode
|
|
19
|
+
- Used Angrylion for software-only RDP rendering
|
|
20
|
+
- Used Parallel RSP with ARM64 dynarec for fast RSP emulation
|
|
21
|
+
- Did not require any GPU/OpenGL at runtime
|
|
22
|
+
|
|
23
|
+
This build has been lost and we need to recreate it.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Goals
|
|
28
|
+
|
|
29
|
+
1. **Software-Only Rendering**: Build a mupen64plus_next core that uses Angrylion RDP exclusively
|
|
30
|
+
2. **No GPU Dependencies**: Eliminate OpenGL/Vulkan/Metal linking and runtime requirements
|
|
31
|
+
3. **Optimal Performance**: Enable ARM64 dynarec for CPU emulation and Parallel RSP
|
|
32
|
+
4. **Automated Building**: Integrate the build process into emoemu's core-builder system
|
|
33
|
+
5. **Cross-Platform**: Support ARM64 macOS initially, with potential for Linux ARM64
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Background: Mupen64Plus-Next Architecture
|
|
38
|
+
|
|
39
|
+
### Renderer Plugins
|
|
40
|
+
|
|
41
|
+
The mupen64plus-libretro-nx core includes multiple RDP (graphics) plugins:
|
|
42
|
+
|
|
43
|
+
| Plugin | Type | GPU Required | Accuracy | Performance |
|
|
44
|
+
|--------|------|--------------|----------|-------------|
|
|
45
|
+
| **GlideN64** | HLE | Yes (OpenGL) | Good | Fast |
|
|
46
|
+
| **ParaLLEl RDP** | LLE | Yes (Vulkan) | Pixel-perfect | Fast |
|
|
47
|
+
| **Angrylion** | LLE | **No** (CPU) | Pixel-perfect | Slow-Medium |
|
|
48
|
+
|
|
49
|
+
For emoemu, **Angrylion is the only viable option**.
|
|
50
|
+
|
|
51
|
+
### RSP Plugins
|
|
52
|
+
|
|
53
|
+
| Plugin | Type | Notes |
|
|
54
|
+
|--------|------|-------|
|
|
55
|
+
| **HLE RSP** | High-level | Fast but incompatible with Angrylion |
|
|
56
|
+
| **Parallel RSP** | Dynarec | Fast, required for Angrylion |
|
|
57
|
+
| **Cxd4** | Interpreter | Slower fallback |
|
|
58
|
+
|
|
59
|
+
**Parallel RSP is recommended** for use with Angrylion.
|
|
60
|
+
|
|
61
|
+
### Build Flags (from Makefile analysis)
|
|
62
|
+
|
|
63
|
+
| Flag | Default (macOS) | Purpose |
|
|
64
|
+
|------|-----------------|---------|
|
|
65
|
+
| `HAVE_PARALLEL_RDP` | 1 | Vulkan-based RDP (we need 0) |
|
|
66
|
+
| `HAVE_PARALLEL_RSP` | 1 | RSP dynarec (we need 1) |
|
|
67
|
+
| `HAVE_THR_AL` | 1 | Angrylion threading (we need 1) |
|
|
68
|
+
| `LLE` | 1 | Low-level emulation (we need 1) |
|
|
69
|
+
| `WITH_DYNAREC` | (empty) | CPU dynarec (we need `aarch64`) |
|
|
70
|
+
| `GL_LIB` | `-framework OpenGL` | OpenGL linking (problematic) |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Analysis of Current Build Configuration
|
|
75
|
+
|
|
76
|
+
### Current emoemu Build Config
|
|
77
|
+
|
|
78
|
+
From `src/frontend/core-builder.ts`:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
mupen64plus_next: {
|
|
82
|
+
repo: "https://github.com/libretro/mupen64plus-libretro-nx.git",
|
|
83
|
+
buildArgs: [
|
|
84
|
+
"platform=osx",
|
|
85
|
+
"HAVE_PARALLEL_RDP=0", // Disable Vulkan-dependent ParaLLEl RDP
|
|
86
|
+
"WITH_DYNAREC=aarch64", // Enable ARM64 dynamic recompiler
|
|
87
|
+
],
|
|
88
|
+
outputFile: "mupen64plus_next_libretro.dylib",
|
|
89
|
+
installedFile: "mupen64plus_next_libretro.dylib",
|
|
90
|
+
description: "Nintendo 64 (Mupen64Plus-Next with Angrylion software renderer)",
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Problems with Current Config
|
|
95
|
+
|
|
96
|
+
1. **OpenGL Still Linked**: The Makefile's `platform=osx` section hardcodes `GL_LIB := -framework OpenGL`
|
|
97
|
+
2. **GlideN64 Still Compiled**: No flag to exclude GlideN64 sources, which depend on OpenGL
|
|
98
|
+
3. **Runtime OpenGL Check**: The core may still check for OpenGL at runtime even when using Angrylion
|
|
99
|
+
4. **Missing Flags**: `HAVE_THR_AL` and `HAVE_PARALLEL_RSP` should be explicitly set
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Proposed Solution
|
|
104
|
+
|
|
105
|
+
### Option A: Makefile Modifications (Recommended)
|
|
106
|
+
|
|
107
|
+
Create a modified build that:
|
|
108
|
+
1. Sets `GL_LIB` to empty to prevent OpenGL linking
|
|
109
|
+
2. Explicitly enables software-only flags
|
|
110
|
+
3. Potentially patches out GlideN64 compilation
|
|
111
|
+
|
|
112
|
+
**Proposed Build Command:**
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
make -j$(nproc) \
|
|
116
|
+
platform=osx \
|
|
117
|
+
HAVE_PARALLEL_RDP=0 \
|
|
118
|
+
HAVE_PARALLEL_RSP=1 \
|
|
119
|
+
HAVE_THR_AL=1 \
|
|
120
|
+
LLE=1 \
|
|
121
|
+
WITH_DYNAREC=aarch64 \
|
|
122
|
+
GL_LIB=
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The key addition is `GL_LIB=` (empty) to prevent OpenGL framework linking.
|
|
126
|
+
|
|
127
|
+
### Option B: Source Patching
|
|
128
|
+
|
|
129
|
+
If Option A doesn't work, patch the source:
|
|
130
|
+
|
|
131
|
+
1. Modify `Makefile` to add a `SOFTWARE_ONLY=1` flag
|
|
132
|
+
2. When set, skip GlideN64 sources and clear `GL_LIB`
|
|
133
|
+
3. Add preprocessor define to disable OpenGL code paths
|
|
134
|
+
|
|
135
|
+
### Option C: Fork with Software-Only Configuration
|
|
136
|
+
|
|
137
|
+
Create a minimal fork that:
|
|
138
|
+
1. Removes GlideN64 entirely
|
|
139
|
+
2. Removes OpenGL/Vulkan dependencies
|
|
140
|
+
3. Keeps only Angrylion + Parallel RSP + Cxd4
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Implementation Plan
|
|
145
|
+
|
|
146
|
+
### Phase 1: Investigate Build Failure
|
|
147
|
+
|
|
148
|
+
1. Clone mupen64plus-libretro-nx repository
|
|
149
|
+
2. Attempt build with current config
|
|
150
|
+
3. Capture and analyze build errors
|
|
151
|
+
4. Identify exactly what's failing and why
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
git clone --depth 1 https://github.com/libretro/mupen64plus-libretro-nx.git
|
|
155
|
+
cd mupen64plus-libretro-nx
|
|
156
|
+
make -j$(sysctl -n hw.ncpu) platform=osx HAVE_PARALLEL_RDP=0 WITH_DYNAREC=aarch64 2>&1 | tee build.log
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Phase 2: Test GL_LIB Override
|
|
160
|
+
|
|
161
|
+
1. Try building with `GL_LIB=` (empty)
|
|
162
|
+
2. If linker fails, identify which objects require OpenGL symbols
|
|
163
|
+
3. Determine if those objects can be excluded
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
make -j$(sysctl -n hw.ncpu) \
|
|
167
|
+
platform=osx \
|
|
168
|
+
HAVE_PARALLEL_RDP=0 \
|
|
169
|
+
HAVE_PARALLEL_RSP=1 \
|
|
170
|
+
HAVE_THR_AL=1 \
|
|
171
|
+
WITH_DYNAREC=aarch64 \
|
|
172
|
+
GL_LIB= \
|
|
173
|
+
2>&1 | tee build-no-gl.log
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Phase 3: Source Analysis
|
|
177
|
+
|
|
178
|
+
If Phase 2 fails, analyze which source files require OpenGL:
|
|
179
|
+
|
|
180
|
+
1. Search for OpenGL includes: `grep -r "OpenGL\|GL/gl\|GLES" --include="*.c" --include="*.cpp" --include="*.h"`
|
|
181
|
+
2. Identify conditional compilation: `grep -r "#ifdef.*GL\|#if.*OPENGL" --include="*.c" --include="*.cpp"`
|
|
182
|
+
3. Find GlideN64 source boundaries in Makefile.common
|
|
183
|
+
4. Determine minimal exclusion set
|
|
184
|
+
|
|
185
|
+
### Phase 4: Implement Software-Only Build
|
|
186
|
+
|
|
187
|
+
Based on findings, implement one of:
|
|
188
|
+
|
|
189
|
+
**4A: Build Flag Solution**
|
|
190
|
+
```typescript
|
|
191
|
+
// Updated core-builder.ts
|
|
192
|
+
mupen64plus_next: {
|
|
193
|
+
repo: "https://github.com/libretro/mupen64plus-libretro-nx.git",
|
|
194
|
+
buildArgs: [
|
|
195
|
+
"platform=osx",
|
|
196
|
+
"HAVE_PARALLEL_RDP=0",
|
|
197
|
+
"HAVE_PARALLEL_RSP=1",
|
|
198
|
+
"HAVE_THR_AL=1",
|
|
199
|
+
"LLE=1",
|
|
200
|
+
"WITH_DYNAREC=aarch64",
|
|
201
|
+
"GL_LIB=", // Prevent OpenGL linking
|
|
202
|
+
],
|
|
203
|
+
// ...
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**4B: Patch-Based Solution**
|
|
208
|
+
```typescript
|
|
209
|
+
// Add pre-build patching step
|
|
210
|
+
const patchMakefile = (repoDir: string): void => {
|
|
211
|
+
const makefilePath = join(repoDir, 'Makefile');
|
|
212
|
+
let content = readFileSync(makefilePath, 'utf-8');
|
|
213
|
+
|
|
214
|
+
// Remove OpenGL linking for osx platform
|
|
215
|
+
content = content.replace(
|
|
216
|
+
/GL_LIB := -framework OpenGL/g,
|
|
217
|
+
'GL_LIB :='
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
writeFileSync(makefilePath, content);
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**4C: Fork Solution**
|
|
225
|
+
|
|
226
|
+
Create `https://github.com/emoemu/mupen64plus-software-only` with:
|
|
227
|
+
- Removed GlideN64 directory
|
|
228
|
+
- Removed ParaLLEl RDP sources
|
|
229
|
+
- Modified Makefile with no OpenGL dependencies
|
|
230
|
+
- Simplified build for software-only operation
|
|
231
|
+
|
|
232
|
+
### Phase 5: Runtime Configuration
|
|
233
|
+
|
|
234
|
+
Ensure core options are set correctly at runtime:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// In core-options.ts - DEFAULT_CORE_OPTIONS
|
|
238
|
+
'mupen64plus_next': {
|
|
239
|
+
'mupen64plus-rdp-plugin': 'angrylion',
|
|
240
|
+
'mupen64plus-rsp-plugin': 'parallel',
|
|
241
|
+
'mupen64plus-cpucore': 'dynamic_recompiler',
|
|
242
|
+
'mupen64plus-angrylion-multithread': 'all threads',
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Phase 6: Validation
|
|
247
|
+
|
|
248
|
+
1. Build completes without errors
|
|
249
|
+
2. Core loads without OpenGL errors
|
|
250
|
+
3. N64 ROM boots and renders
|
|
251
|
+
4. Frame output appears in terminal
|
|
252
|
+
5. Performance is acceptable (30+ FPS on M1)
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Core Options for Software Rendering
|
|
257
|
+
|
|
258
|
+
These options must be applied when loading the core:
|
|
259
|
+
|
|
260
|
+
```ini
|
|
261
|
+
; RDP Plugin - MUST be angrylion for software rendering
|
|
262
|
+
mupen64plus-rdp-plugin = "angrylion"
|
|
263
|
+
|
|
264
|
+
; RSP Plugin - parallel is fastest, cxd4 is fallback
|
|
265
|
+
mupen64plus-rsp-plugin = "parallel"
|
|
266
|
+
|
|
267
|
+
; CPU Core - dynamic recompiler for performance
|
|
268
|
+
mupen64plus-cpucore = "dynamic_recompiler"
|
|
269
|
+
|
|
270
|
+
; Threading - use all CPU cores for Angrylion
|
|
271
|
+
mupen64plus-angrylion-multithread = "all threads"
|
|
272
|
+
|
|
273
|
+
; VI output - bilinear filtering (optional)
|
|
274
|
+
mupen64plus-angrylion-vioverlay = "Filtered"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Testing the Build
|
|
280
|
+
|
|
281
|
+
To test building the core with emoemu's built-in core builder:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
pnpm run start -- --install-core mupen64plus_next
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
This will:
|
|
288
|
+
1. Detect that mupen64plus_next requires building from source on ARM Mac
|
|
289
|
+
2. Clone the repository
|
|
290
|
+
3. Build with the configured flags
|
|
291
|
+
4. Install to cores directory (see Data Directories below)
|
|
292
|
+
5. Display progress and any errors to the console
|
|
293
|
+
|
|
294
|
+
For development, you can also build manually to capture full output:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
git clone --depth 1 https://github.com/libretro/mupen64plus-libretro-nx.git
|
|
298
|
+
cd mupen64plus-libretro-nx
|
|
299
|
+
make -j$(sysctl -n hw.ncpu) platform=osx HAVE_PARALLEL_RDP=0 WITH_DYNAREC=aarch64 2>&1 | tee build.log
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Testing Checklist
|
|
305
|
+
|
|
306
|
+
### Build Verification
|
|
307
|
+
- [ ] Clone repository succeeds
|
|
308
|
+
- [ ] Build completes without errors
|
|
309
|
+
- [ ] Output .dylib is ARM64 architecture (`file` command)
|
|
310
|
+
- [ ] No OpenGL symbols in binary (`nm -u` shows no GL references)
|
|
311
|
+
- [ ] File size is reasonable (smaller than full build)
|
|
312
|
+
|
|
313
|
+
### Runtime Verification
|
|
314
|
+
- [ ] Core loads in emoemu without OpenGL errors
|
|
315
|
+
- [ ] `retro_load_game()` succeeds
|
|
316
|
+
- [ ] Frame callback receives valid framebuffer data
|
|
317
|
+
- [ ] Video renders correctly in terminal
|
|
318
|
+
|
|
319
|
+
### Game Compatibility
|
|
320
|
+
- [ ] Super Mario 64 boots and is playable
|
|
321
|
+
- [ ] The Legend of Zelda: Ocarina of Time renders correctly
|
|
322
|
+
- [ ] Audio plays without issues
|
|
323
|
+
- [ ] Save states work
|
|
324
|
+
|
|
325
|
+
### Performance
|
|
326
|
+
- [ ] Achieves 30+ FPS on Apple M1
|
|
327
|
+
- [ ] Achieves 60 FPS on Apple M1 Pro/Max
|
|
328
|
+
- [ ] Multi-threading is functional
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Risk Assessment
|
|
333
|
+
|
|
334
|
+
| Risk | Likelihood | Impact | Mitigation |
|
|
335
|
+
|------|------------|--------|------------|
|
|
336
|
+
| GlideN64 cannot be excluded | Medium | High | Fork with sources removed |
|
|
337
|
+
| OpenGL symbols required at runtime | Low | High | Patch source to remove checks |
|
|
338
|
+
| Angrylion too slow | Low | Medium | Enable all multi-threading options |
|
|
339
|
+
| Build system changes upstream | Low | Low | Pin to specific commit |
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Alternative Approaches
|
|
344
|
+
|
|
345
|
+
### Use Different N64 Core
|
|
346
|
+
|
|
347
|
+
**parallel-n64** is another libretro N64 core that may be easier to build software-only. However:
|
|
348
|
+
- Less actively maintained
|
|
349
|
+
- May have different accuracy/compatibility
|
|
350
|
+
- Still includes GPU renderers
|
|
351
|
+
|
|
352
|
+
### RetroArch Headless Mode
|
|
353
|
+
|
|
354
|
+
RetroArch has a `--video-driver=null` option but this doesn't help with core linking requirements.
|
|
355
|
+
|
|
356
|
+
### Emscripten/WASM Build
|
|
357
|
+
|
|
358
|
+
Some libretro cores have been compiled to WebAssembly which doesn't have OpenGL linking. This approach is complex but guarantees no GPU dependencies.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Resources
|
|
363
|
+
|
|
364
|
+
- [mupen64plus-libretro-nx Repository](https://github.com/libretro/mupen64plus-libretro-nx)
|
|
365
|
+
- [Mupen64Plus-Next 2.0 Announcement](https://www.libretro.com/index.php/mupen64plus-next-2-0-64dd-support-angrylion-and-gliden64-in-one-build-parallel-rsp-support-and-android/)
|
|
366
|
+
- [Angrylion RDP Plus](https://github.com/ata4/angrylion-rdp-plus)
|
|
367
|
+
- [Libretro Docs - Mupen64Plus](https://docs.libretro.com/library/mupen64plus/)
|
|
368
|
+
- [Emulation General Wiki - N64 Plugins](https://emulation.gametechwiki.com/index.php/Recommended_N64_plugins)
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Appendix: Makefile Deep Dive
|
|
373
|
+
|
|
374
|
+
### macOS Platform Block (from Makefile)
|
|
375
|
+
|
|
376
|
+
```makefile
|
|
377
|
+
else ifeq ($(platform), osx)
|
|
378
|
+
TARGET := $(TARGET_NAME)_libretro.dylib
|
|
379
|
+
LDFLAGS += -dynamiclib
|
|
380
|
+
OSXVER = $(shell sw_vers -productVersion | cut -d. -f 1)
|
|
381
|
+
OSX_LT_MAVERICKS = $(shell [ $(OSXVER) -lt 10 ] && echo "YES")
|
|
382
|
+
LDFLAGS += -mmacosx-version-min=10.7
|
|
383
|
+
LDFLAGS += -stdlib=libc++
|
|
384
|
+
|
|
385
|
+
PLATFLAGS += -DOS_MAC_OS_X
|
|
386
|
+
GL_LIB := -framework OpenGL # <-- PROBLEM: hardcoded
|
|
387
|
+
|
|
388
|
+
CFLAGS += -DOS_MAC_OS_X
|
|
389
|
+
CPUFLAGS := -msse -msse2
|
|
390
|
+
WITH_DYNAREC = # <-- PROBLEM: dynarec disabled by default
|
|
391
|
+
HAVE_PARALLEL_RSP = 1
|
|
392
|
+
HAVE_PARALLEL_RDP = 1 # <-- We override to 0
|
|
393
|
+
HAVE_THR_AL = 1
|
|
394
|
+
LLE = 1
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Key Variables to Override
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
# Required overrides for software-only build
|
|
401
|
+
HAVE_PARALLEL_RDP=0 # Disable Vulkan RDP
|
|
402
|
+
WITH_DYNAREC=aarch64 # Enable ARM64 CPU dynarec
|
|
403
|
+
GL_LIB= # Empty to prevent OpenGL linking (needs testing)
|
|
404
|
+
|
|
405
|
+
# Should already be set by platform=osx but explicit is safer
|
|
406
|
+
HAVE_PARALLEL_RSP=1 # Enable fast RSP dynarec
|
|
407
|
+
HAVE_THR_AL=1 # Enable Angrylion threading
|
|
408
|
+
LLE=1 # Enable low-level emulation
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Implementation Status: COMPLETE
|
|
414
|
+
|
|
415
|
+
The software-only build is now working. The solution required:
|
|
416
|
+
|
|
417
|
+
### 1. Source Patches for macOS Compatibility
|
|
418
|
+
|
|
419
|
+
Two patches are automatically applied before building:
|
|
420
|
+
|
|
421
|
+
**Patch 1: libpng fp.h fix**
|
|
422
|
+
```c
|
|
423
|
+
// custom/dependencies/libpng/pngpriv.h
|
|
424
|
+
// Replace: #include <fp.h>
|
|
425
|
+
// With: #include <math.h>
|
|
426
|
+
```
|
|
427
|
+
The `fp.h` header was for Classic Mac OS and doesn't exist on modern macOS.
|
|
428
|
+
|
|
429
|
+
**Patch 2: libzlib fdopen fix**
|
|
430
|
+
```c
|
|
431
|
+
// custom/dependencies/libzlib/zutil.h
|
|
432
|
+
// Replace: #if defined(MACOS) || defined(TARGET_OS_MAC)
|
|
433
|
+
// With: #if (defined(MACOS) || defined(TARGET_OS_MAC)) && !defined(__APPLE__)
|
|
434
|
+
```
|
|
435
|
+
The bundled zlib incorrectly defines `fdopen()` as NULL for `TARGET_OS_MAC`, but modern macOS has `fdopen()`. This breaks when `_stdio.h` is included.
|
|
436
|
+
|
|
437
|
+
### 2. Build Flags
|
|
438
|
+
|
|
439
|
+
The final working build configuration:
|
|
440
|
+
```bash
|
|
441
|
+
make platform=osx \
|
|
442
|
+
HAVE_PARALLEL_RDP=0 \ # Disable Vulkan RDP
|
|
443
|
+
HAVE_PARALLEL_RSP=1 \ # Enable RSP dynarec
|
|
444
|
+
HAVE_THR_AL=1 \ # Enable Angrylion threading
|
|
445
|
+
LLE=1 # Enable low-level emulation
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Note:** `WITH_DYNAREC=aarch64` is intentionally omitted because the ARM64 dynarec assembly uses GNU syntax (`.hidden`, `.type %function`) that's incompatible with macOS's LLVM assembler. The CPU runs in interpreter mode, which is slower but compatible. The Parallel RSP still uses its own dynarec which does work.
|
|
449
|
+
|
|
450
|
+
### 3. What Gets Built
|
|
451
|
+
|
|
452
|
+
The resulting core includes:
|
|
453
|
+
- **Angrylion RDP**: Software renderer (CPU-only, no GPU)
|
|
454
|
+
- **Parallel RSP**: Fast RSP with dynarec
|
|
455
|
+
- **GlideN64**: Also compiled (OpenGL), but runtime core options select Angrylion
|
|
456
|
+
|
|
457
|
+
At runtime, the core options are set to use Angrylion:
|
|
458
|
+
```ini
|
|
459
|
+
mupen64plus-rdp-plugin = "angrylion"
|
|
460
|
+
mupen64plus-rsp-plugin = "parallel"
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Installation
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
pnpm run start -- --install-core mupen64plus_next
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
This will:
|
|
470
|
+
1. Clone the repository
|
|
471
|
+
2. Apply source patches automatically
|
|
472
|
+
3. Build with software rendering flags
|
|
473
|
+
4. Install to cores directory (see Data Directories below)
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Data Directories
|
|
478
|
+
|
|
479
|
+
emoemu stores data in platform-specific directories:
|
|
480
|
+
|
|
481
|
+
| Platform | Base Directory |
|
|
482
|
+
|----------|----------------|
|
|
483
|
+
| macOS | `~/Library/Application Support/emoemu/` |
|
|
484
|
+
| Linux | `~/.config/emoemu/` |
|
|
485
|
+
| Windows | `%APPDATA%\emoemu\` |
|
|
486
|
+
|
|
487
|
+
**Subdirectories:**
|
|
488
|
+
|
|
489
|
+
| Directory | Purpose |
|
|
490
|
+
|-----------|---------|
|
|
491
|
+
| `cores/` | Installed libretro cores (`.dylib`, `.so`, `.dll`) |
|
|
492
|
+
| `logs/` | Log files including `emoemu.log` |
|
|
493
|
+
| `saves/` | Save data (SRAM, memory cards) |
|
|
494
|
+
| `states/` | Save states |
|
|
495
|
+
| `system/` | BIOS files and system data |
|
|
496
|
+
| `config/` | Per-core configuration files |
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Debugging the N64 Core
|
|
501
|
+
|
|
502
|
+
If the core builds and installs but displays all black when running, follow these steps to debug:
|
|
503
|
+
|
|
504
|
+
### Step 1: Clean Environment
|
|
505
|
+
|
|
506
|
+
Kill any lingering node processes that might be holding state:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
killall -9 node
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Step 2: Remove Existing Core
|
|
513
|
+
|
|
514
|
+
Remove the currently installed core to ensure a fresh build:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
pnpm run start -- --remove-core mupen64plus_next
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Step 3: Rebuild the Project
|
|
521
|
+
|
|
522
|
+
If you made changes to the core builder, rebuild:
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
pnpm run build
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Step 4: Install Fresh Core
|
|
529
|
+
|
|
530
|
+
Install the core with the updated build process:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
pnpm run start -- --install-core mupen64plus_next
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Step 5: Test with Emoji Mode
|
|
537
|
+
|
|
538
|
+
Run a ROM with emoji mode (good for debugging since it's simple output):
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
pnpm run start -- --core mupen64plus_next ~/ROMs/N64/Super\ Mario\ 64\ \(USA\).z64 --emoji
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Expected behavior:**
|
|
545
|
+
- After a brief loading period, you should see colored emoji output
|
|
546
|
+
- If you only see black emojis after 10-15 seconds, the core is not rendering properly
|
|
547
|
+
|
|
548
|
+
### Step 6: Check Logs
|
|
549
|
+
|
|
550
|
+
If rendering fails, kill the process and check the logs:
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
killall -9 node
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Then examine the log file:
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
cat ~/Library/Application\ Support/emoemu/logs/emoemu.log # macOS
|
|
560
|
+
cat ~/.config/emoemu/logs/emoemu.log # Linux
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
Look for errors related to:
|
|
564
|
+
- `mupen64plus` or `angrylion` - RDP plugin issues
|
|
565
|
+
- `OpenGL` or `GL` - Indicates the core is trying to use GPU rendering
|
|
566
|
+
- `GET_VARIABLE` - Core options not being applied correctly
|
|
567
|
+
- `video callback` - Framebuffer issues
|
|
568
|
+
|
|
569
|
+
### Common Issues
|
|
570
|
+
|
|
571
|
+
| Symptom | Likely Cause | Fix |
|
|
572
|
+
|---------|--------------|-----|
|
|
573
|
+
| All black output | Core using GlideN64 instead of Angrylion | Check core options are set correctly |
|
|
574
|
+
| "OpenGL support required" | Core built with wrong flags | Rebuild with `HAVE_PARALLEL_RDP=0` |
|
|
575
|
+
| Crash on load | Missing dependencies | Check build output for errors |
|
|
576
|
+
| Very slow / stuttering | CPU dynarec not working | Expected on macOS (interpreter mode) |
|
|
577
|
+
|
|
578
|
+
### Verify Core Options
|
|
579
|
+
|
|
580
|
+
The core options should be set to use Angrylion. Check the core options file (macOS: `~/Library/Application Support/emoemu/retroarch-core-options.cfg`, Linux: `~/.config/emoemu/retroarch-core-options.cfg`) contains:
|
|
581
|
+
|
|
582
|
+
```ini
|
|
583
|
+
mupen64plus-rdp-plugin = "angrylion"
|
|
584
|
+
mupen64plus-rsp-plugin = "parallel"
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
If these aren't being applied, the core may default to GlideN64 which requires OpenGL.
|
|
588
|
+
|
|
589
|
+
### Adding More Logging
|
|
590
|
+
|
|
591
|
+
If the logs don't provide enough information to diagnose the issue, you may need to add additional logging or adjust log verbosity:
|
|
592
|
+
|
|
593
|
+
**1. Enable debug logging in config:**
|
|
594
|
+
|
|
595
|
+
Edit `~/.config/emoemu/emoemu.cfg` (or `~/Library/Application Support/emoemu/emoemu.cfg` on macOS):
|
|
596
|
+
|
|
597
|
+
```ini
|
|
598
|
+
log_verbosity = 2
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
**2. Add logging to libretro environment handler:**
|
|
602
|
+
|
|
603
|
+
Key file: `src/cores/libretro/environment/index.ts`
|
|
604
|
+
|
|
605
|
+
Add `logger.info()` calls to trace:
|
|
606
|
+
- Which environment commands are being called
|
|
607
|
+
- What values are being requested/returned for `GET_VARIABLE`
|
|
608
|
+
- When video callbacks are triggered
|
|
609
|
+
|
|
610
|
+
Example:
|
|
611
|
+
```typescript
|
|
612
|
+
case RETRO_ENVIRONMENT.GET_VARIABLE:
|
|
613
|
+
logger.info(`GET_VARIABLE called with data: ${data}`, 'Environ');
|
|
614
|
+
const result = this.handleGetVariable(data);
|
|
615
|
+
logger.info(`GET_VARIABLE result: ${result}`, 'Environ');
|
|
616
|
+
return result;
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**3. Add logging to video callback:**
|
|
620
|
+
|
|
621
|
+
Key file: `src/cores/libretro/callbacks/index.ts`
|
|
622
|
+
|
|
623
|
+
Log framebuffer details when video callback is invoked:
|
|
624
|
+
```typescript
|
|
625
|
+
logger.info(`Video callback: ${width}x${height}, pitch=${pitch}, format=${pixelFormat}`, 'Video');
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
**4. Add logging to core options:**
|
|
629
|
+
|
|
630
|
+
Key file: `src/cores/libretro/core-options.ts`
|
|
631
|
+
|
|
632
|
+
Log when default options are applied:
|
|
633
|
+
```typescript
|
|
634
|
+
logger.info(`Applying default options for ${coreName}: ${JSON.stringify(options)}`, 'CoreOptions');
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**5. Rebuild and test:**
|
|
638
|
+
|
|
639
|
+
After adding logging:
|
|
640
|
+
```bash
|
|
641
|
+
pnpm run build
|
|
642
|
+
killall -9 node
|
|
643
|
+
pnpm run start -- --remove-core mupen64plus_next
|
|
644
|
+
pnpm run start -- --install-core mupen64plus_next
|
|
645
|
+
pnpm run start -- --core mupen64plus_next ~/ROMs/N64/game.z64 --emoji
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Then check the logs again for the additional output.
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
## Solved Issue: macOS Library Loading Hang
|
|
653
|
+
|
|
654
|
+
**Status: RESOLVED**
|
|
655
|
+
|
|
656
|
+
On macOS with ARM64 (Apple Silicon), the pre-built mupen64plus_next core (and even custom builds linked against OpenGL) hung indefinitely during the initial library loading phase (`koffi.load()`). This happened before any libretro API functions were called.
|
|
657
|
+
|
|
658
|
+
### Root Cause
|
|
659
|
+
|
|
660
|
+
The mupen64plus core links against OpenGL.framework even when configured to use the Angrylion software renderer. When the OpenGL framework is loaded on macOS, it performs GPU capability probing during its static initialization. In a terminal environment without a GPU context, this probing appears to block indefinitely.
|
|
661
|
+
|
|
662
|
+
### Solution: Stub OpenGL Library
|
|
663
|
+
|
|
664
|
+
The solution was to create a stub OpenGL library that provides empty implementations of the OpenGL functions the core links against. Since we use Angrylion (which is CPU-only software rendering), the OpenGL functions are never actually called at runtime.
|
|
665
|
+
|
|
666
|
+
**Implementation:**
|
|
667
|
+
|
|
668
|
+
1. **Stub Library Creation**: The core-builder creates a stub library (`libGL_stub.dylib`) with no-op implementations of the ~35 OpenGL functions the core references.
|
|
669
|
+
|
|
670
|
+
2. **Build with Stub**: The core is built linking against the stub library instead of OpenGL.framework:
|
|
671
|
+
```bash
|
|
672
|
+
make platform=osx HAVE_PARALLEL_RDP=0 GL_LIB="-L/path/to/stub -lGL_stub"
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
3. **Library Path Fix**: After building, `install_name_tool` is used to change the stub library reference to an absolute path so it can be found at runtime.
|
|
676
|
+
|
|
677
|
+
**Files involved:**
|
|
678
|
+
- `src/frontend/core-builder.ts` - Creates stub library and builds with it
|
|
679
|
+
- The stub library is installed alongside the core in the cores directory
|
|
680
|
+
|
|
681
|
+
### Technical Details
|
|
682
|
+
|
|
683
|
+
The stub library provides empty implementations for these OpenGL functions:
|
|
684
|
+
- `glBindTexture`, `glBlendFunc`, `glClear`, `glClearColor`, etc.
|
|
685
|
+
- `glGenTextures` (returns nothing), `glGetError` (returns 0)
|
|
686
|
+
- `glGetString` (returns empty string)
|
|
687
|
+
- ~35 functions total
|
|
688
|
+
|
|
689
|
+
Since Angrylion doesn't use OpenGL for rendering, these functions are never actually called. The stub just satisfies the linker and allows the library to load without pulling in the real OpenGL framework.
|
|
690
|
+
|
|
691
|
+
### For Users
|
|
692
|
+
|
|
693
|
+
N64 emulation now works out of the box on macOS ARM64:
|
|
694
|
+
|
|
695
|
+
```bash
|
|
696
|
+
# Install the core (builds automatically with stub library)
|
|
697
|
+
pnpm run start -- --install-core mupen64plus_next
|
|
698
|
+
|
|
699
|
+
# Run an N64 game
|
|
700
|
+
pnpm run start -- ~/ROMs/N64/game.z64
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
The core uses Angrylion for software rendering, which is pixel-perfect accurate but slower than GPU-based renderers. Performance is acceptable on Apple Silicon Macs.
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## Solved Issue: Initial Black Screen During Boot
|
|
708
|
+
|
|
709
|
+
**Status: RESOLVED - Normal Behavior**
|
|
710
|
+
|
|
711
|
+
After fixing the library loading hang, the N64 emulator would show "all black" video output for the first few seconds. Investigation revealed this is **normal N64 boot behavior**, not a bug.
|
|
712
|
+
|
|
713
|
+
### What Happens During N64 Boot
|
|
714
|
+
|
|
715
|
+
1. **Frames 1-130 (~2 seconds)**: The N64 bootstrap runs. During this time, the RDP (graphics processor) hasn't been initialized yet, so the video callback receives `null` data (frame dupes).
|
|
716
|
+
|
|
717
|
+
2. **Frames 131-150**: The game starts initializing graphics. First frames are very dark (max pixel value ~1).
|
|
718
|
+
|
|
719
|
+
3. **Frames 150+**: Full video output with normal brightness (max pixel value 249).
|
|
720
|
+
|
|
721
|
+
This is the same boot sequence that happens on real N64 hardware and in RetroArch with the same core.
|
|
722
|
+
|
|
723
|
+
### Key Findings
|
|
724
|
+
|
|
725
|
+
- **Video callback IS being called** with correct dimensions (640x240) and pixel format (XRGB8888)
|
|
726
|
+
- **Frame duping** (null data) is used during boot when no RDP output is available
|
|
727
|
+
- **getFramebuffer()** correctly waits for valid frame data before returning non-empty buffers
|
|
728
|
+
- **Pixel format conversion** (XRGB8888 → RGB24) works correctly
|
|
729
|
+
|
|
730
|
+
### Core Options Confirmed Working
|
|
731
|
+
|
|
732
|
+
The following options are correctly applied:
|
|
733
|
+
|
|
734
|
+
```ini
|
|
735
|
+
mupen64plus-rdp-plugin = "angrylion" # Software RDP (confirmed)
|
|
736
|
+
mupen64plus-rsp-plugin = "cxd4" # RSP interpreter (required for Angrylion)
|
|
737
|
+
mupen64plus-cpucore = "cached_interpreter" # ARM64 has no dynarec support
|
|
738
|
+
mupen64plus-angrylion-multithread = "all threads"
|
|
739
|
+
mupen64plus-FrameDuping = "False"
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
Note: `mupen64plus-cpucore = "dynamic_recompiler"` is requested, but ARM64 macOS doesn't have a compatible dynarec, so the core automatically falls back to "Cached Interpreter". This is slower but works correctly.
|
|
743
|
+
|
|
744
|
+
### For Developers
|
|
745
|
+
|
|
746
|
+
When debugging N64 video issues:
|
|
747
|
+
|
|
748
|
+
1. **Run at least 200 frames** before concluding video is broken (boot takes ~140 frames)
|
|
749
|
+
2. **Check the video callback** - if it receives null data, that's expected during boot
|
|
750
|
+
3. **Check max pixel values** - should increase from 0 to ~249 as the game fades in
|
|
751
|
+
4. **Frame buffer size changes** - initial size matches SystemInfo, then changes to actual RDP output size
|