[AGC/Vulkan] Extend PS5 runtime and rendering compatibility (#216)

* [Core] Add POSIX native execution and PS5 SELF support

Extend the native backend, guest TLS, fixed-address memory, and loader paths needed by PS5 titles on Windows, Linux, and macOS. Keep workstation GC so high-core-count hosts do not reserve over fixed guest image bases.

* [HLE] Expand PS5 service and media compatibility

Add the kernel, threading, save-data, networking, audio, video-codec, font, dialog, and service exports required by newer PS5 software. Preserve every SysAbi NID currently registered by main while adding the compatibility surface used by ASTRO BOT.

* [AGC/Vulkan] Extend Gen5 shader and presentation support

Expand PM4 handling, Gen5 shader translation, MRT and packed export support, guest image tracking, depth initialization, texture aliasing, and Vulkan presentation. Add the performance overlay and address-filtered diagnostics used to validate ASTRO BOT with original shaders.

* [Core] Align static TLS reservation across hosts

* [Pad] Align primary user ID with UserService

* [Gpu] Preserve runtime scalar buffers across renderer seam

* [AGC] Restore omitted command helper exports

* [Vulkan] Reuse primary views for promoted MRT targets

* [Vulkan] Preserve scratch storage bindings in compute dispatches
This commit is contained in:
Miguel Cruz
2026-07-15 19:02:34 -04:00
committed by GitHub
parent ad5a7d3799
commit 864cbb0fa0
85 changed files with 36299 additions and 9176 deletions
+293
View File
@@ -0,0 +1,293 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.HLE;
namespace SharpEmu.Libs.Font;
public static class FontExports
{
private static readonly object AllocationGate = new();
private static ulong _librarySelectionAddress;
private static ulong _rendererSelectionAddress;
[SysAbiExport(
Nid = "whrS4oksXc4",
ExportName = "sceFontMemoryInit",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int MemoryInit(CpuContext ctx)
{
var descriptorAddress = ctx[CpuRegister.Rdi];
var regionAddress = ctx[CpuRegister.Rsi];
var regionSize = (uint)ctx[CpuRegister.Rdx];
var interfaceAddress = ctx[CpuRegister.Rcx];
var mspaceAddress = ctx[CpuRegister.R8];
var destroyCallback = ctx[CpuRegister.R9];
if (descriptorAddress == 0 ||
!TryWriteUInt32(ctx, descriptorAddress, 0x00000F00) ||
!TryWriteUInt32(ctx, descriptorAddress + 0x04, regionSize) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x08, regionAddress) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x10, mspaceAddress) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x18, interfaceAddress) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x20, destroyCallback) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x28, 0) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x30, 0) ||
!ctx.TryWriteUInt64(descriptorAddress + 0x38, mspaceAddress))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
return SetSuccess(ctx);
}
[SysAbiExport(
Nid = "oM+XCzVG3oM",
ExportName = "sceFontSelectLibraryFt",
Target = Generation.Gen5,
LibraryName = "libSceFontFt")]
public static int SelectLibraryFt(CpuContext ctx) =>
ReturnSelection(ctx, ref _librarySelectionAddress, 0x38);
[SysAbiExport(
Nid = "Xx974EW-QFY",
ExportName = "sceFontSelectRendererFt",
Target = Generation.Gen5,
LibraryName = "libSceFontFt")]
public static int SelectRendererFt(CpuContext ctx) =>
ReturnSelection(ctx, ref _rendererSelectionAddress, 0x100);
[SysAbiExport(
Nid = "n590hj5Oe-k",
ExportName = "sceFontCreateLibraryWithEdition",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int CreateLibraryWithEdition(CpuContext ctx) =>
CreateOpaqueHandle(ctx, ctx[CpuRegister.Rcx], 0x100, magic: 0x0F01);
[SysAbiExport(
Nid = "WaSFJoRWXaI",
ExportName = "sceFontCreateRendererWithEdition",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int CreateRendererWithEdition(CpuContext ctx) =>
CreateOpaqueHandle(ctx, ctx[CpuRegister.Rcx], 0x100, magic: 0x0F07);
[SysAbiExport(
Nid = "cKYtVmeSTcw",
ExportName = "sceFontOpenFontSet",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int OpenFontSet(CpuContext ctx) =>
CreateOpaqueHandle(ctx, ctx[CpuRegister.R8], 0x100, magic: 0x0F02);
[SysAbiExport(
Nid = "KXUpebrFk1U",
ExportName = "sceFontOpenFontMemory",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int OpenFontMemory(CpuContext ctx) =>
CreateOpaqueHandle(ctx, ctx[CpuRegister.R8], 0x100, magic: 0x0F02);
[SysAbiExport(
Nid = "JzCH3SCFnAU",
ExportName = "sceFontOpenFontInstance",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int OpenFontInstance(CpuContext ctx)
{
var sourceHandle = ctx[CpuRegister.Rdi];
var setupHandle = ctx[CpuRegister.Rsi];
var outputAddress = ctx[CpuRegister.Rdx];
if (outputAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
if (setupHandle != 0)
{
return ctx.TryWriteUInt64(outputAddress, setupHandle)
? SetSuccess(ctx)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!TryAllocateOpaque(ctx, 0x100, out var handle))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (sourceHandle != 0)
{
Span<byte> source = stackalloc byte[0x100];
if (ctx.Memory.TryRead(sourceHandle, source))
{
_ = ctx.Memory.TryWrite(handle, source);
}
}
_ = TryWriteUInt16(ctx, handle, 0x0F02);
return ctx.TryWriteUInt64(outputAddress, handle)
? SetSuccess(ctx)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "SsRbbCiWoGw",
ExportName = "sceFontSupportSystemFonts",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int SupportSystemFonts(CpuContext ctx) => SetSuccess(ctx);
[SysAbiExport(
Nid = "mz2iTY0MK4A",
ExportName = "sceFontSupportExternalFonts",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int SupportExternalFonts(CpuContext ctx) => SetSuccess(ctx);
[SysAbiExport(
Nid = "CUKn5pX-NVY",
ExportName = "sceFontAttachDeviceCacheBuffer",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int AttachDeviceCacheBuffer(CpuContext ctx) => SetSuccess(ctx);
[SysAbiExport(
Nid = "IQtleGLL5pQ",
ExportName = "sceFontGetRenderCharGlyphMetrics",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int GetRenderCharGlyphMetrics(CpuContext ctx)
{
var metricsAddress = ctx[CpuRegister.Rdx];
if (metricsAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var values = new[] { 8.0f, 16.0f, 0.0f, 12.0f, 8.0f, 0.0f, 0.0f, 16.0f };
for (var index = 0; index < values.Length; index++)
{
if (!TryWriteUInt32(
ctx,
metricsAddress + (ulong)(index * sizeof(float)),
BitConverter.SingleToUInt32Bits(values[index])))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
}
return SetSuccess(ctx);
}
[SysAbiExport(
Nid = "gdUCnU0gHdI",
ExportName = "sceFontRenderSurfaceInit",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int RenderSurfaceInit(CpuContext ctx)
{
var surfaceAddress = ctx[CpuRegister.Rdi];
var bufferAddress = ctx[CpuRegister.Rsi];
var widthBytes = (uint)ctx[CpuRegister.Rdx];
var pixelBytes = (uint)ctx[CpuRegister.Rcx] & 0xFF;
var width = (uint)ctx[CpuRegister.R8];
var height = (uint)ctx[CpuRegister.R9];
if (surfaceAddress == 0 ||
!ctx.TryWriteUInt64(surfaceAddress, bufferAddress) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x08, widthBytes) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x0C, pixelBytes) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x10, width) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x14, height) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x18, 0) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x1C, 0) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x20, width) ||
!TryWriteUInt32(ctx, surfaceAddress + 0x24, height))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
return SetSuccess(ctx);
}
private static int ReturnSelection(CpuContext ctx, ref ulong selectionAddress, uint objectSize)
{
if (ctx[CpuRegister.Rdi] != 0)
{
ctx[CpuRegister.Rax] = 0;
return 0;
}
lock (AllocationGate)
{
if (selectionAddress == 0)
{
if (!TryAllocateOpaque(ctx, 0x20, out selectionAddress) ||
!TryWriteUInt32(ctx, selectionAddress, 0) ||
!TryWriteUInt32(ctx, selectionAddress + 4, objectSize))
{
selectionAddress = 0;
}
}
}
ctx[CpuRegister.Rax] = selectionAddress;
return 0;
}
private static int CreateOpaqueHandle(CpuContext ctx, ulong outputAddress, int size, ushort magic)
{
if (outputAddress == 0 || !TryAllocateOpaque(ctx, size, out var handle))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!TryWriteUInt16(ctx, handle, magic) || !ctx.TryWriteUInt64(outputAddress, handle))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
return SetSuccess(ctx);
}
private static bool TryAllocateOpaque(CpuContext ctx, int size, out ulong address)
{
address = 0;
if (ctx.Memory is not IGuestMemoryAllocator allocator ||
!allocator.TryAllocateGuestMemory((ulong)size, 0x10, out address))
{
return false;
}
Span<byte> bytes = stackalloc byte[size];
bytes.Clear();
return ctx.Memory.TryWrite(address, bytes);
}
private static bool TryWriteUInt16(CpuContext ctx, ulong address, ushort value)
{
Span<byte> bytes = stackalloc byte[sizeof(ushort)];
BinaryPrimitives.WriteUInt16LittleEndian(bytes, value);
return ctx.Memory.TryWrite(address, bytes);
}
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
{
Span<byte> bytes = stackalloc byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
return ctx.Memory.TryWrite(address, bytes);
}
private static int SetSuccess(CpuContext ctx)
{
ctx[CpuRegister.Rax] = 0;
return 0;
}
private static int SetReturn(CpuContext ctx, OrbisGen2Result result)
{
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
return (int)result;
}
}