Seven levels · roughly three weeks part-time

Learning KytyPS5 by doing

Reading the entire tree front to back does not work. This is an ordered set of small journeys through the emulator. For deeper source traces, native debugging exercises and evidence-based contribution gates, continue with the developer curriculum; its coverage map also identifies what remains introductory.

Completed
0%
Tasks
0
Tick tasks as you finish them. Levels build on each other — the prerequisites are real, not decorative.

Companion to the architecture guide. If a term here is unfamiliar — NID, PM4, sharp, SRT, EXEC mask — that document's glossary defines it.

What all of this producescommercial titles, running
Disgaea 6 running in KytyPS5
Disgaea 6
SILENT HILL: The Short Message running in KytyPS5
SILENT HILL: The Short Message
Neptunia ReVerse running in KytyPS5
Neptunia ReVerse
Paleo Pines running in KytyPS5
Paleo Pines
Every pixel went through the chain described here: PM4 packets parsed by a software command processor, RDNA 2 shaders recompiled to SPIR-V, guest memory tracked by page faults. Screenshots from the KytyPS5 repository.
A

Debugging playbook

Symptom to first move. This is the table you will actually use once you are past level 3.

SymptomTurn onLook at
Exits immediately, no window default logging The last few log lines. Usually a failed subsystem init, a missing eboot.bin, or an EXIT_NOT_IMPLEMENTED in the loader. The message names the file and condition.
Crashes before any rendering default logging Search for Guest fault context. The handler prints the faulting guest thread, all registers, 96 code bytes around the fault and 32 stack words, then Unhandled host exception: … address=…. If that address is small (like 0x28) suspect an unpatched fs: access.
Boots, then a function does nothing PRINT_NAME_ENABLE(true) in the suspect module The per-call trace. Also grep the log for stubbed NIDs — an import that resolved to the diagnostic thunk returns 0, which many games read as success.
Black screen, no errors --command-buffer-dump true Whether draw packets are arriving at all. No IT_DRAW_* in the dump means the problem is upstream of graphics — the game never got far enough to render.
Geometry wrong or missing --rd then RenderDoc The captured draw: vertex buffer contents, the bound pipeline, viewport and scissor. Compare against the register state in the command buffer dump.
Wrong colours or garbage pixels --shader-log-direction File The dumped IR for the pixel shader. Also check whether dispatcher fallback appears — the fallback is correct but exercises different code.
Textures flicker or go stale --readback-linear-images true A coherency bug: the CPU wrote memory the GPU has cached, or vice versa. Start at MemoryTracker, not at the texture cache.
Vulkan device lost / validation errors --vulkan-validation true --shader-validation true Validation output first. --shader-validation runs every emitted module through spirv-val, which catches malformed SPIR-V before the driver does something unpredictable with it.
Wrong GPU on a multi-GPU machine --gpu <index> The Vulkan physical device index; the default (−1) picks automatically. An invalid value is a usage error, an out-of-range one falls back to automatic with a log line. The launcher exposes the same setting.
Runs but very slowly --profile Tracy. Check first whether time goes to shader compilation (a cold cache) or to per-draw work, because the fixes are completely different.
Hangs with no CPU use attach a debugger, dump all threads A blocked WAIT_REG_MEM in the command processor whose condition never becomes true, or a guest thread waiting on an event queue nothing signals.
B

Commands you will type constantly

Configure and build

# from an x64 Native Tools prompt; run at the repository root
cmake -S . -B _Build/windows -G Ninja -DCMAKE_BUILD_TYPE=Release ^
      -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl ^
      -DCMAKE_PREFIX_PATH="C:/Qt/6.x.x/msvc2022_64"

cmake --build _Build/windows --target kyty_emulator      # emulator only, faster
cmake --build _Build/windows --target launcher            # + Qt front end
cmake --install _Build/windows --prefix _Build/windows/install

Run

# bare run
.\_Build\windows\install\kyty_emulator.exe --game "D:\Games\Example"

# the configuration you will use for most debugging
kyty_emulator.exe --game "D:\Games\Example" ^
  --printf-direction File --printf-output-file _kyty.txt ^
  --vulkan-validation true --shader-validation true

# graphics investigation
kyty_emulator.exe --game "..." --command-buffer-dump true ^
  --shader-log-direction File --graphics-debug-dump true

Where output lands

PathWritten byNote
_kyty.txt--printf-direction FileDefault is Silent, so enable the file sink before a diagnostic session.
_Shaders/--shader-log-direction FileDecoded RDNA 2, CFG and IR per shader.
_Buffers/--command-buffer-dump trueDecoded PM4 listings.
_Textures/--graphics-debug-dump trueCleared on every start by ClearDebugTextureFolder().
_SaveData/, _DownloadData/, _TempData/the guestPer-title, keyed by TITLE_ID.

src/common/emulatorConfig.h src/main.cpp

Build and run a test

cmake --build _Build/windows --target memory_tracker_tests
.\_Build\windows\memory_tracker_tests.exe

# every test target is EXCLUDE_FROM_ALL, so a normal build skips them:
shader_cfg_tests               shader_recompiler_compute_tests   scalar_provenance_tests
resource_tracking_tests        resource_materialization_tests    shader_vertex_metadata_tests
memory_tracker_tests           page_manager_tests                image_page_table_tests
virtual_memory_allocation_tests sync_on_address_tests            event_queue_lifetime_tests
kernel_file_system_tests       audio_out2_port_tests             ime_dialog_tests
bit_array_tests

# or the aggregate suite, then run them through ctest:
cmake --build _Build/windows --target kyty_tests
ctest --test-dir _Build/windows --output-on-failure
C

Reading conventions in this codebase

Five things worth internalising before you read much source. They are used consistently, so recognising them saves a lot of time.

You will seeIt means
EXIT_NOT_IMPLEMENTED(cond)
about 980 sites
"If this is true, I did not handle this case." Not an assertion of impossibility — a marker of the frontier. Grepping these is the fastest way to map what the emulator does not yet do.
EXIT("...")
about 500 sites
An unrecoverable error with a message. Distinguished from the above: this is a real invariant violation, usually a bug rather than a gap.
KYTY_SYSV_ABIThis function is called by guest code. Its arguments arrive in System V registers. Never call it directly from emulator code without thinking about the ABI.
PRINT_NAME()First line of a guest-facing function. Logs the call when the module's thread-local flag is on. Free tracing, already wired up.
KYTY_CLASS_NO_COPY(T)Deleted copy constructor and assignment. Ubiquitous on anything owning a Vulkan or OS handle.
Common::Singleton<T>::Instance()How cross-cutting services are reached — the runtime linker, the subsystem list, the config. There is no dependency injection here.

Naming that maps to hardware

  • HwCtx* / HwSh* / HwUc* — handlers for context, shader and user-config register writes. The three register spaces a PM4 stream can target.
  • CpOp* — a command-processor operation, i.e. a handler for one PM4 opcode.
  • IT_* — PM4 opcodes ("indirect-buffer type"). R_* — Sony's sub-selectors carried in IT_NOP packets.
  • V# T# S# — buffer, image and sampler descriptors. In code, "sharp".
  • Agc / Gen5 — the current and legacy names around the PS5 graphics API. Start in agc.cpp and libAgcDriver.cpp.
One habit above all

Whenever you read a pointer, ask: guest address or host address? Whenever you read a function, ask: which thread runs this — the guest thread, the GPU thread, or the main/window thread? Most genuinely confusing emulator bugs are a confusion about one of those two questions, and the codebase does not always spell out the answer.

Start here defines the beginner-to-contributor route. This page is an optional project practice checklist for readers already comfortable with C++.

Built against KytyPS5 main at d2413fc2 (7 October 2026); follow source paths and symbols at that revision. Times are rough and assume you are comfortable with C++ but new to emulation. If a level takes twice as long as stated, that is normal — level 5 in particular is a compiler course wearing a disguise.