diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs index 02254e91..47057c43 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs @@ -736,6 +736,9 @@ public sealed partial class DirectExecutionBackend var expectedEqueueTimeout = string.Equals(nid, "fzyMKs9kim0", StringComparison.Ordinal) && result == OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT; + var expectedEventFlagTimeout = + string.Equals(nid, "JTvBflhYazQ", StringComparison.Ordinal) && + result == OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT; var expectedMutexTrylockBusy = string.Equals(nid, "K-jXhbt2gn4", StringComparison.Ordinal) && result == OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY; @@ -748,6 +751,7 @@ public sealed partial class DirectExecutionBackend if (!expectedFileProbeMiss && !expectedTimedWaitTimeout && !expectedEqueueTimeout && + !expectedEventFlagTimeout && !expectedMutexTrylockBusy && !expectedUserServiceNoEvent && !expectedPrivacyInvalidParameter) diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index 0715071c..420a2741 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -52,8 +52,6 @@ public static class AgcExports private const uint RFlip = 0x17; private const uint RReleaseMem = 0x18; private const uint RDmaData = 0x19; - private const uint RPredication = 0x1A; - private const uint RJump = 0x1B; private const uint SpiShaderPgmLoPs = 0x8; private const uint SpiShaderPgmHiPs = 0x9; private const uint SpiShaderPgmLoEs = 0xC8; @@ -391,7 +389,6 @@ public static class AgcExports public Dictionary ComputeQueues { get; } = new(); public Dictionary ComputeImageWriters { get; } = new(); public ulong WorkSequence { get; set; } - public ulong FlipSequence { get; set; } } private readonly record struct RegisterDefaultValue(uint Offset, uint Value); @@ -1926,90 +1923,6 @@ public static class AgcExports return ReturnPointer(ctx, commandAddress); } - [SysAbiExport( - Nid = "bbFueFP+J4k", - ExportName = "sceAgcDcbSetPredication", - Target = Generation.Gen5, - LibraryName = "libSceAgc")] - public static int DcbSetPredication(CpuContext ctx) - { - var commandBufferAddress = ctx[CpuRegister.Rdi]; - var enable = (uint)ctx[CpuRegister.Rsi]; - var predicateType = (uint)ctx[CpuRegister.Rdx]; - var continuePredicate = (uint)ctx[CpuRegister.Rcx]; - var predicateAddress = ctx[CpuRegister.R8]; - if (commandBufferAddress == 0 || enable > 1 || continuePredicate > 1) - { - return ReturnPointer(ctx, 0); - } - - if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 5, out var commandAddress) || - !ctx.TryWriteUInt32(commandAddress, Pm4(5, ItNop, RPredication)) || - !ctx.TryWriteUInt32(commandAddress + 4, enable) || - !ctx.TryWriteUInt32(commandAddress + 8, predicateType) || - !ctx.TryWriteUInt32(commandAddress + 12, continuePredicate) || - !ctx.TryWriteUInt32(commandAddress + 16, unchecked((uint)predicateAddress))) - { - return ReturnPointer(ctx, 0); - } - - TraceAgc( - $"agc.dcb_set_predication buf=0x{commandBufferAddress:X16} cmd=0x{commandAddress:X16} " + - $"enable={enable} type={predicateType} continue={continuePredicate} addr=0x{predicateAddress:X16}"); - return ReturnPointer(ctx, commandAddress); - } - - [SysAbiExport( - Nid = "xSAR0LTcRKM", - ExportName = "sceAgcDcbJump", - Target = Generation.Gen5, - LibraryName = "libSceAgc")] - public static int DcbJump(CpuContext ctx) - { - var commandBufferAddress = ctx[CpuRegister.Rdi]; - var targetAddress = ctx[CpuRegister.Rsi]; - if (commandBufferAddress == 0) - { - return ReturnPointer(ctx, 0); - } - - if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 3, out var commandAddress) || - !ctx.TryWriteUInt32(commandAddress, Pm4(3, ItNop, RJump)) || - !ctx.TryWriteUInt32(commandAddress + 4, unchecked((uint)targetAddress)) || - !ctx.TryWriteUInt32(commandAddress + 8, unchecked((uint)(targetAddress >> 32)))) - { - return ReturnPointer(ctx, 0); - } - - TraceAgc( - $"agc.dcb_jump buf=0x{commandBufferAddress:X16} cmd=0x{commandAddress:X16} " + - $"target=0x{targetAddress:X16}"); - return ReturnPointer(ctx, commandAddress); - } - - [SysAbiExport( - Nid = "w6Dj1VJt5qY", - ExportName = "sceAgcSetPacketPredication", - Target = Generation.Gen5, - LibraryName = "libSceAgc")] - public static int SetPacketPredication(CpuContext ctx) - { - var packetAddress = ctx[CpuRegister.Rdi]; - var enabled = ctx[CpuRegister.Rsi] != 0; - if (packetAddress == 0 || !ctx.TryReadUInt32(packetAddress, out var packetHeader)) - { - return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT); - } - - packetHeader = enabled ? packetHeader | 1u : packetHeader & ~1u; - if (!ctx.TryWriteUInt32(packetAddress, packetHeader)) - { - return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); - } - - return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK); - } - [SysAbiExport( Nid = "w2rJhmD+dsE", ExportName = "sceAgcDriverAddEqEvent", @@ -2062,22 +1975,9 @@ public static class AgcExports public static int DriverSubmitDcb(CpuContext ctx) { var packetAddress = ctx[CpuRegister.Rdi]; - // Gen5 supports direct RSI/RDX and packed RDI submissions. - var commandAddress = ctx[CpuRegister.Rsi]; - var directDwordCount = ctx[CpuRegister.Rdx]; - uint dwordCount; - var directArguments = commandAddress != 0 && - directDwordCount is > 0 and <= 1_000_000UL; - if (directArguments) - { - dwordCount = (uint)directDwordCount; - } - else if (packetAddress == 0 || - !ctx.TryReadUInt64(packetAddress, out commandAddress) || - !ctx.TryReadUInt32(packetAddress + 8, out dwordCount) || - commandAddress == 0 || - dwordCount == 0 || - dwordCount > 1_000_000) + if (packetAddress == 0 || + !ctx.TryReadUInt64(packetAddress, out var commandAddress) || + !ctx.TryReadUInt32(packetAddress + 8, out var dwordCount)) { return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT); } @@ -2093,29 +1993,16 @@ public static class AgcExports if (tracePackets) { - TraceAgc( - $"agc.driver_submit_dcb abi={(directArguments ? "direct" : "packed")} " + - $"packet=0x{packetAddress:X16} addr=0x{commandAddress:X16} dwords={dwordCount}"); + TraceAgc($"agc.driver_submit_dcb packet=0x{packetAddress:X16} addr=0x{commandAddress:X16} dwords={dwordCount}"); } var gpuState = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState()); - ulong flipSequenceBefore; lock (gpuState.Gate) { - flipSequenceBefore = gpuState.FlipSequence; ParseSubmittedDcb(ctx, gpuState, gpuState.Graphics, commandAddress, dwordCount, tracePackets); DrainResumableDcbs(ctx, gpuState, tracePackets); } - // Flip submissions are completed through VideoOut pacing. - if (gpuState.FlipSequence == flipSequenceBefore) - { - KernelEventQueueCompatExports.TriggerRegisteredEvents( - 0, - KernelEventQueueCompatExports.KernelEventFilterGraphics, - 0); - } - ctx[CpuRegister.Rax] = 0; return (int)OrbisGen2Result.ORBIS_GEN2_OK; } @@ -2141,10 +2028,8 @@ public static class AgcExports Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal); var gpuState = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState()); - ulong flipSequenceBefore; lock (gpuState.Gate) { - flipSequenceBefore = gpuState.FlipSequence; for (uint i = 0; i < bufferCount; i++) { if (!ctx.TryReadUInt64(addressArray + i * 8, out var commandAddress) || @@ -2168,14 +2053,6 @@ public static class AgcExports DrainResumableDcbs(ctx, gpuState, tracePackets); } - if (gpuState.FlipSequence == flipSequenceBefore) - { - KernelEventQueueCompatExports.TriggerRegisteredEvents( - 0, - KernelEventQueueCompatExports.KernelEventFilterGraphics, - 0); - } - ctx[CpuRegister.Rax] = 0; return (int)OrbisGen2Result.ORBIS_GEN2_OK; } @@ -2424,40 +2301,6 @@ public static class AgcExports } var packetType = header >> 30; - if (packetType == 0) - { - // Consume alignment padding one dword at a time. - if (header == 0) - { - offset++; - continue; - } - - // Preserve stream alignment across type-0 packets. - var type0Length = ((header >> 16) & 0x3FFFu) + 2u; - if (offset + type0Length > dwordCount) - { - offset++; - continue; - } - - offset += type0Length; - continue; - } - - if (packetType == 1) - { - const uint type1Length = 3; - if (offset + type1Length > dwordCount) - { - offset++; - continue; - } - - offset += type1Length; - continue; - } - if (packetType == 2) { if (tracePackets) @@ -2472,15 +2315,13 @@ public static class AgcExports if (packetType != 3) { - offset++; - continue; + return; } var length = Pm4Length(header); if (length == 0 || offset + length > dwordCount) { - offset++; - continue; + return; } var op = (header >> 8) & 0xFFu; @@ -2718,7 +2559,6 @@ public static class AgcExports if (op == ItNop && register == RFlip && length >= 6) { - gpuState.FlipSequence++; if (!ctx.TryReadUInt32(currentAddress + 4, out var videoOutHandle) || !ctx.TryReadUInt32(currentAddress + 8, out var displayBufferIndexRaw) || !ctx.TryReadUInt32(currentAddress + 12, out var flipMode) || @@ -2731,8 +2571,23 @@ public static class AgcExports var flipArg = unchecked((long)(((ulong)flipArgHi << 32) | flipArgLo)); var displayBufferIndex = unchecked((int)displayBufferIndexRaw); var handle = unchecked((int)videoOutHandle); - // Prefer output decoded from the current DCB. - if (state.SawIndexedDraw && + if (VideoOutExports.TryGetDisplayBufferInfo( + handle, + displayBufferIndex, + out var cachedDisplayBuffer) && + VulkanVideoPresenter.TrySubmitGuestImage( + cachedDisplayBuffer.Address, + cachedDisplayBuffer.Width, + cachedDisplayBuffer.Height, + cachedDisplayBuffer.PitchInPixel)) + { + TraceDisplayBuffer( + handle, + displayBufferIndex, + cachedDisplayBuffer, + "gpu-cache"); + } + else if (state.SawIndexedDraw && state.TranslatedDraw is { } translatedDraw && VideoOutExports.TryGetDisplayBufferInfo( handle, @@ -2796,22 +2651,6 @@ public static class AgcExports displayBuffer.Width, displayBuffer.Height); } - else if (VideoOutExports.TryGetDisplayBufferInfo( - handle, - displayBufferIndex, - out var cachedDisplayBuffer) && - VulkanVideoPresenter.TrySubmitGuestImage( - cachedDisplayBuffer.Address, - cachedDisplayBuffer.Width, - cachedDisplayBuffer.Height, - cachedDisplayBuffer.PitchInPixel)) - { - TraceDisplayBuffer( - handle, - displayBufferIndex, - cachedDisplayBuffer, - "gpu-cache"); - } _ = VideoOutExports.SubmitFlipFromAgc(ctx, handle, displayBufferIndex, unchecked((int)flipMode), flipArg); state.SawIndexedDraw = false; @@ -4133,29 +3972,26 @@ public static class AgcExports TraceTextureHash(descriptor, source); - if (_traceAgcShader) + var nonZero = 0; + for (var i = 0; i < source.Length; i++) { - var nonZero = 0; - for (var i = 0; i < source.Length; i++) + if (source[i] != 0) { - if (source[i] != 0) + nonZero++; + if (nonZero >= 64) { - nonZero++; - if (nonZero >= 64) - { - break; - } + break; } } - - TraceAgcShader( - $"agc.texture_source addr=0x{descriptor.Address:X16} " + - $"fmt={descriptor.Format} num={descriptor.NumberType} tile={descriptor.TileMode} " + - $"size={descriptor.Width}x{descriptor.Height} pitch={descriptor.Pitch} " + - $"dst=0x{descriptor.DstSelect:X3} " + - $"bytes={source.Length} nonzero64={nonZero}"); } + TraceAgcShader( + $"agc.texture_source addr=0x{descriptor.Address:X16} " + + $"fmt={descriptor.Format} num={descriptor.NumberType} tile={descriptor.TileMode} " + + $"size={descriptor.Width}x{descriptor.Height} pitch={descriptor.Pitch} " + + $"dst=0x{descriptor.DstSelect:X3} " + + $"bytes={source.Length} nonzero64={nonZero}"); + var rgba = source; texture = new VulkanGuestDrawTexture( descriptor.Address, @@ -5865,16 +5701,6 @@ public static class AgcExports Console.Error.WriteLine($"[LOADER][TRACE] {message}"); } - private static void TraceAgc(ref AgcTraceHandler message) - { - if (!_traceAgc) - { - return; - } - - Console.Error.WriteLine($"[LOADER][TRACE] {message.ToStringAndClear()}"); - } - private static void TraceAgcShader(string message) { if (!_traceAgcShader) @@ -5885,62 +5711,6 @@ public static class AgcExports Console.Error.WriteLine($"[LOADER][TRACE] {message}"); } - private static void TraceAgcShader(ref AgcShaderTraceHandler message) - { - if (!_traceAgcShader) - { - return; - } - - Console.Error.WriteLine($"[LOADER][TRACE] {message.ToStringAndClear()}"); - } - - [InterpolatedStringHandler] - internal ref struct AgcTraceHandler - { - private DefaultInterpolatedStringHandler _inner; - - public AgcTraceHandler(int literalLength, int formattedCount, out bool isEnabled) - { - isEnabled = _traceAgc; - _inner = isEnabled - ? new DefaultInterpolatedStringHandler(literalLength, formattedCount) - : default; - } - - public void AppendLiteral(string value) => _inner.AppendLiteral(value); - - public void AppendFormatted(T value) => _inner.AppendFormatted(value); - - public void AppendFormatted(T value, string? format) => - _inner.AppendFormatted(value, format); - - public string ToStringAndClear() => _inner.ToStringAndClear(); - } - - [InterpolatedStringHandler] - internal ref struct AgcShaderTraceHandler - { - private DefaultInterpolatedStringHandler _inner; - - public AgcShaderTraceHandler(int literalLength, int formattedCount, out bool isEnabled) - { - isEnabled = _traceAgcShader; - _inner = isEnabled - ? new DefaultInterpolatedStringHandler(literalLength, formattedCount) - : default; - } - - public void AppendLiteral(string value) => _inner.AppendLiteral(value); - - public void AppendFormatted(T value) => _inner.AppendFormatted(value); - - public void AppendFormatted(T value, string? format) => - _inner.AppendFormatted(value, format); - - public string ToStringAndClear() => _inner.ToStringAndClear(); - } - private static string FormatShaderDwords(IReadOnlyList values) => values.Count == 0 ? "none" diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index fa5910e4..66c2af69 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -1683,6 +1683,30 @@ public static class KernelMemoryCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + [SysAbiExport( + Nid = "E6ao34wPw+U", + ExportName = "stat", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int PosixStat(CpuContext ctx) + { + var result = KernelStat(ctx); + if (result == (int)OrbisGen2Result.ORBIS_GEN2_OK) + { + return 0; + } + + var errno = result switch + { + (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT => Einval, + (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT => Efault, + _ => 2, + }; + KernelRuntimeCompatExports.TrySetErrno(ctx, errno); + ctx[CpuRegister.Rax] = ulong.MaxValue; + return -1; + } + [SysAbiExport( Nid = "gEpBkcwxUjw", ExportName = "sceKernelAprResolveFilepathsToIdsAndFileSizes", diff --git a/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs index 086d2e4f..577fe56c 100644 --- a/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs @@ -431,6 +431,18 @@ public static class KernelRuntimeCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + [SysAbiExport( + Nid = "HoLVWNanBBc", + ExportName = "getpid", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int GetProcessId(CpuContext ctx) + { + var processId = Environment.ProcessId; + ctx[CpuRegister.Rax] = unchecked((uint)processId); + return processId; + } + [SysAbiExport( Nid = "fgxnMeTNUtY", ExportName = "sceKernelGetProcessTimeCounter", diff --git a/src/SharpEmu.Libs/SaveData/SaveDataExports.cs b/src/SharpEmu.Libs/SaveData/SaveDataExports.cs index 2d3d9210..9a5e8e05 100644 --- a/src/SharpEmu.Libs/SaveData/SaveDataExports.cs +++ b/src/SharpEmu.Libs/SaveData/SaveDataExports.cs @@ -30,13 +30,17 @@ public static class SaveDataExports private const uint MountModeCreate2 = 1u << 5; private const int MountResultSize = 0x40; private static readonly object _stateGate = new(); + private static readonly HashSet _transactionResources = []; private static string? _titleId; + private static int _nextTransactionResource; public static void ConfigureApplicationInfo(string? titleId) { lock (_stateGate) { _titleId = string.IsNullOrWhiteSpace(titleId) ? null : SanitizePathSegment(titleId.Trim()); + _transactionResources.Clear(); + _nextTransactionResource = 0; } } @@ -251,26 +255,32 @@ public static class SaveDataExports LibraryName = "libSceSaveData")] public static int SaveDataCreateTransactionResource(CpuContext ctx) { - // Gen5 ABI: memory size, resource output, reserved. var memorySize = ctx[CpuRegister.Rdi]; - var resourceAddress = ctx[CpuRegister.Rsi]; - var reserved = ctx[CpuRegister.Rdx]; - - if (resourceAddress == 0) + int resource; + lock (_stateGate) { - return ctx.SetReturn(OrbisSaveDataErrorParameter); + resource = ++_nextTransactionResource; + _transactionResources.Add(resource); } - // Offline HLE operations carry no transaction state. - if (!ctx.TryWriteUInt64(resourceAddress, 0)) + TraceSaveData($"create_transaction_resource memory_size=0x{memorySize:X} resource={resource}"); + return ctx.SetReturn(resource); + } + + [SysAbiExport( + Nid = "lJUQuaKqoKY", + ExportName = "sceSaveDataDeleteTransactionResource", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libSceSaveData")] + public static int SaveDataDeleteTransactionResource(CpuContext ctx) + { + var resource = unchecked((int)ctx[CpuRegister.Rdi]); + lock (_stateGate) { - return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); + _transactionResources.Remove(resource); } - TraceSaveData( - $"create_transaction_resource memory_size=0x{memorySize:X} reserved=0x{reserved:X} " + - $"resource_addr=0x{resourceAddress:X} resource=0x0"); - + TraceSaveData($"delete_transaction_resource resource={resource}"); return ctx.SetReturn(0); }