* fix(agc): add support for CMASK fast clear in rendering process * fix(agc): prevent stale cross-frame label writes from bypassing WAIT_REG_MEM Add frame-ID tracking to GpuWaitRegistry so that WAIT_REG_MEM in frame N+1 is not satisfied by a label written in frame N. Previously, a label that persisted in guest memory from the previous frame could satisfy a new WAIT_REG_MEM immediately, causing the waiting DCB to bypass its fence and execute out of order. Changes: - GpuWaitRegistry: add _labelFrameIds, _currentFrameId, AdvanceFrame(), IsLabelFresh(); RecordProduced now stamps the frame ID on each write - AgcExports: call AdvanceFrame() at RFlip (frame boundary); HandleSubmittedWaitRegMem checks IsLabelFresh before bypassing This is one piece of the Dead Cells character visibility fix (issue #833). The cross-frame label reuse could cause composite passes to execute before character-layer passes, resulting in invisible characters. * fix(video): simulate CMASK 'all clear' at frame boundary to prevent trails On real PS5, CMASK is reset to 'all clear' at each frame boundary, so render targets are effectively cleared. SharpEmu did not implement this behavior, causing GBuffer targets to LOAD stale data from the previous frame, resulting in visible trails/ghosting. Reset Initialized=false for all offscreen guest images at the present boundary (when _currentFrameSlot changes). This ensures every render target starts each frame with LoadOp.Clear, matching the hardware's CMASK behavior. This is the trail fix for Dead Cells (issue #833), complementing the cross-frame label staleness fix in GpuWaitRegistry. * fix(video): only reset render targets (not textures) for CMASK simulation Previous commit 89b4610 reset Initialized for ALL guest images, including textures and storage images, causing a completely black screen. Only images with a RenderPass (actual color render targets) should be reset, as CMASK 'all clear' only applies to color render targets, not sampled textures. * Revert "fix(video): simulate CMASK 'all clear' at frame boundary to prevent trails" This reverts commit 89b4610a015619848e4000372822dca1c08051ff. * fix(agc): implement CMASK state machine for render target clearing Implement CMASK (Color Mask) hardware behavior based on shadPS4's approach: 1. Track CMASK addresses from CB_COLORn_CMASK registers 2. Detect compute shaders that write to CMASK addresses (IsComputeMetaClear heuristic: no bitwise XOR in shader = clear shader) 3. Mark CMASK as 'all clear' when compute shader writes to it 4. EliminateFastClear mode now checks CMASK state before clearing This addresses the trail/ghosting issue in Dead Cells (issue #833) where 642x362 GBuffer targets were never cleared after first use, causing stale data from previous frames to persist. Reference: shadPS4's IsComputeMetaClear + EliminateFastClear implementation. * fix(agc): implement CMASK state machine for render target clearing Based on shadPS4's PM4 implementation, add CMASK (Color Mask) hardware behavior tracking: 1. TrackCmaskAddresses: Read CB_COLORn_CMASK registers to get CMASK addresses 2. CheckCmaskWrite: When DMA fill targets CMASK address with value 0, mark CMASK as 'all clear' (shadPS4's FillBuffer logic) 3. EliminateFastClear: Check CMASK state before clearing - only clear if CMASK is 'all clear', then mark as 'dirty' 4. DMA fill: Call TrackCmaskAddresses before processing to ensure CMASK addresses are registered when DMA fills happen This addresses the trail/ghosting issue in Dead Cells (issue #833) where 642x362 GBuffer targets were never cleared after first use. Reference: shadPS4's IsComputeMetaClear + EliminateFastClear + FillBuffer. * refactor(agc): remove diagnostic CMASK traces from hot path Remove 4 diagnostic TraceAgcShader calls that were added during CMASK investigation: - agc.cb_regs=[...] - allocated List<string> + string.Join on every draw - agc.cmask_track - per-slot interpolated string on every draw - agc.cmask_write - per-DMA-fill interpolated string - agc.eliminate_fast_clear - per-draw interpolated string These were debugging probes and are no longer needed. * fix(agc): sync constant-fill zero writes to Vulkan render targets Dead Cells GBuffer trails: the constant-fill compute kernel (TrySubmitConstantFillKernel) wrote zeros to guest memory but never invalided the host Vulkan GuestImageResource that backs the render target, so the GBuffer pass kept LoadOp.Load stale previous-frame pixels. After the guest write, request a guest color clear尷 ... clear for the destination address (RequestAgentColorClear projection (clear)) so the next render pass uses LoadOp.Clear. The pending clearandray mechanism already exists; the fill kernel was simply bypassing it. * Remove AGC lifecycle debug probes Remove transient render-target lifecycle and indirect draw debug instrumentation from AgcExports. The change strips the noisy console probing while keeping the active render-target tracking logic intact for draw sequencing and validation. * fix(video): clear frame-stale MRT groups at guest flip boundary On hardware a colour surface whose fast-clear metadata is in reset state reads back as the CB clear value instead of stale memory, so games can leave per-frame MRT groups uncleaned and rely on that implicit initialization. With no metadata layer, such a target keeps whatever touched it last; when the group shares addresses with the compositor's output, the entire finished previous frame bleeds through every region the new frame does not repaint (Dead Cells dungeon trails / ghosted characters). Arm a reset at the guest's own flip command - the authoritative frame boundary inside the submission stream - and let the first multi-attachment colour group of the fresh frame consume it, starting from LoadOp.Clear. One arm per flip yields exactly one reset per guest frame regardless of CPU/GPU pipelining; later groups keep load semantics so intra-frame pass chaining is untouched. CPU-backed images are never touched. Mirrors shadPS4's meta-state-driven attachment.is_clear at render-pass begin (vk_rasterizer.cpp BeginRendering). * fix(agc): correct CbColor0Cmask register offset and add EXT support CbColor0Cmask was 0x320 (CMASK_SLICE, tile_max:14) instead of 0x31F (CMASK_BASE_ADDRESS). TrackCmaskAddresses read the slice count instead of the base address, so CMASK registration always produced garbage or zero — the entire meta-state chain starved for every game. Fix: 0x320 → 0x31F. Add CbColor0CmaskBaseExt (0x398) and decode the full 48-bit address: ((ext & 0xFF) << 40) | ((low & 0x1FFFFFFF) << 8). Phase 0 probe confirmed Dead Cells programs neither CMASK nor DCC registers (all zeros) — it relies on unified memory semantics where unwritten surfaces read as zero. The register offset fix is still needed for games that do use CMASK metadata. * feat(agc,video): CMASK/DCC meta-state machine with CLEAR WORD support Replace the flat _cmaskClearedState dictionary with a proper meta-state ledger that separates registration, clearing, and dirtying: - MetaSurfaceInfo keyed by colour-buffer address (not meta address) - Reverse map _cmaskToColorBuffer for fill/compute write detection - TrackCmaskAddresses reads both CMASK (0x31F) and DCC (0x325) with EXT high-bit decoding; prefers CMASK, falls back to DCC - CLEAR_WORD captured at registration time, passed through to BeginTranslatedRenderPass as VkClearValue - EFC checks specific surface (not 'any cleared → clear slot0'), dirties only that surface (not entire table) - CheckCmaskWrite uses reverse map for O(1) lookup - Presenter bind loop queries IsMetaClearedForSurface → LoadOp.Clear - _metaStateOverridesFlipArm flag gates flip-arm for gradual retirement Phase 0 probe confirmed Dead Cells programs neither CMASK nor DCC (all registers zero) — flip-arm remains the correct fix for that game. The meta-state machine serves games that do use CMASK/DCC metadata. * fix(agc,video): implement surface clearing at guest flip boundary * refactor(video): remove flip-arm heuristic, meta-state machine fully replaces it The flip-arm (_frameColorResetArmed) was a heuristic that cleared the first multi-attachment colour group at guest flip boundary. The CMASK/DCC meta-state machine now handles all clearing: at flip time MarkAllSurfacesCleared() marks every registered surface as cleared; at bind time IsMetaClearedForSurface() triggers LoadOp.Clear and consumes the state. This replaces the flip-arm with a per-surface state machine that correctly handles both explicit clears (DMA fill, compute, EFC) and implicit frame-boundary resets. Also fixed a bug where TrackCmaskAddresses overwrote IsCleared to false on every call, defeating the frame-boundary reset. Now preserves existing IsCleared state when re-registering. * Thread-safe meta-surface state & meta-clear decode Add synchronization for metadata state: introduce _metaSurfaceGate and guard accesses to _metaSurfaces and _cmaskToColorBuffer in AgcExports to avoid concurrent-dictionary corruption and ensure small critical sections. Preserve cleared state only when metadata binding matches during re-registration. Make Gen5 texture format constants internal. Update logic to set/consume IsCleared under lock and check CMASK nearby windows safely. Decode meta clear values in VulkanVideoPresenter: skip CPU-backed targets for meta clears, add UnpackMetaClearValue and HalfToFloat to convert CLEAR_WORD0/1 into proper ClearColorValue for R8G8B8A8_UNORM and R16G16B16A16_FLOAT formats (with an 8_8_8_8 fallback). This ensures correct clear colours and thread-safe metadata handling.
SharpEmu
An experimental PlayStation 5 emulator for Windows, Linux and macOS.
Note
SharpEmu supports Windows x64, Linux x64, and macOS x64. Apple Silicon Macs can run the macOS x64 build through Rosetta 2, and Windows on ARM devices (e.g. Snapdragon) can run the Windows x64 build through Windows' built-in x64 emulation.
Warning
SharpEmu is an experimental PS5 emulator developed from scratch in C#. The current focus is on accuracy and infrastructure setup rather than game-specific compatibility.
Info
SharpEmu is an emulator project currently in its early stages of development.
This project is developed purely for research and educational purposes. There are no commercial goals associated with it. We enjoy learning about system architecture and reverse engineering.
SharpEmu focuses exclusively on the PlayStation 5.
Our goal is not to emulate PS4 games, as there is already an excellent emulator dedicated to that platform: ShadPS4.
Games Tested
| Demons Souls Remake | Dreaming Sarah |
|---|---|
![]() |
![]() |
| Void Terrarium | Dead Cells |
|---|---|
![]() |
![]() |
Status
The emulator can currently load the eboot.bin of real games, execute native CPU instructions, and partially handle kernel-related functionality. However, several critical components are still missing.
Current capabilities include:
- Loading
eboot.binand.elffiles - Executing native CPU instructions
- Reading basic game metadata (title, version, etc.)
- Loading system modules (
prx/sys_module) - Partial support for some kernel functions
FiberandAMPRexports- PlayGo scenarios
- Initial loading game files
- Shader/resource submits and AGC initial
- Video outputs in some games
Some games have reached like sceVideoOut and AGC stages.
SharpEmu supports Windows, Linux, and macOS hosts. Video output uses Vulkan on Windows and Linux, and MoltenVK on macOS. Platform support is still experimental, so compatibility and performance vary by game, operating system, and GPU driver.
Using
Download the release archive for your operating system, extract it, and launch
SharpEmu with the path to a legally obtained game's eboot.bin.
Windows PowerShell:
.\SharpEmu.exe "C:\path\to\game\eboot.bin" 2>&1 |
Tee-Object -FilePath "SharpEmu.log"
Linux and macOS:
chmod +x ./SharpEmu
./SharpEmu "/path/to/game/eboot.bin" 2>&1 |
tee SharpEmu.log
A Vulkan-capable GPU and current graphics driver are required. The macOS release includes the MoltenVK Vulkan implementation.
Important
This project does not support or condone piracy.
All games used during development and testing are dumped from consoles that we personally own.
Users are expected to use legally obtained copies of their games.
Build
- Install the .NET SDK version specified in
global.json. - Clone the repository:
git clone https://github.com/sharpemu/sharpemu.git - Open the solution file (
SharpEmu.slnx) in VSCode. - Build the project:
dotnet buildordotnet publish - Build artifacts will be located in the
artifactsdirectory.
Disclaimer
SharpEmu is an experimental emulator intended for research and educational purposes.
This project does not contain any copyrighted system firmware, game data, or proprietary PlayStation assets.
Special Thanks
The following projects were extremely helpful during development:
-
ShadPS4
Helped with understanding the basic architecture of the PlayStation 4. -
Kyty
One of the few PS5 emulator projects available and very useful for studying native code execution. -
Ryujinx
Provided valuable references for filesystem handling and low-level C# implementation patterns.
License
Support
Support SharpEmu via GitHub Sponsors or cryptocurrency. Every contribution helps fund ongoing development and long-term maintenance. GitHub Sponsors is the preferred way to support the project, but cryptocurrency donations are also appreciated.
ETH/USDT
0xF315F5d986c790bB3A58DbE60F1B2760997dEd82
BTC
bc1qmr9k8899njys5ny63xsues4jgmkk96erslrkmv
Contributing
Before opening an issue or pull request, please read our contribution guidelines:
The guide covers:
- Coding style and formatting
- AI-assisted contributions
- Pull request expectations
- Testing guidelines
- Legal and reverse engineering policy




