mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-30 22:49:53 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c082c23552 | |||
| 14fe861ef0 | |||
| 787d3a1efb |
@@ -736,6 +736,9 @@ public sealed partial class DirectExecutionBackend
|
|||||||
var expectedEqueueTimeout =
|
var expectedEqueueTimeout =
|
||||||
string.Equals(nid, "fzyMKs9kim0", StringComparison.Ordinal) &&
|
string.Equals(nid, "fzyMKs9kim0", StringComparison.Ordinal) &&
|
||||||
result == OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT;
|
result == OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT;
|
||||||
|
var expectedEventFlagTimeout =
|
||||||
|
string.Equals(nid, "JTvBflhYazQ", StringComparison.Ordinal) &&
|
||||||
|
result == OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT;
|
||||||
var expectedMutexTrylockBusy =
|
var expectedMutexTrylockBusy =
|
||||||
string.Equals(nid, "K-jXhbt2gn4", StringComparison.Ordinal) &&
|
string.Equals(nid, "K-jXhbt2gn4", StringComparison.Ordinal) &&
|
||||||
result == OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY;
|
result == OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY;
|
||||||
@@ -748,6 +751,7 @@ public sealed partial class DirectExecutionBackend
|
|||||||
if (!expectedFileProbeMiss &&
|
if (!expectedFileProbeMiss &&
|
||||||
!expectedTimedWaitTimeout &&
|
!expectedTimedWaitTimeout &&
|
||||||
!expectedEqueueTimeout &&
|
!expectedEqueueTimeout &&
|
||||||
|
!expectedEventFlagTimeout &&
|
||||||
!expectedMutexTrylockBusy &&
|
!expectedMutexTrylockBusy &&
|
||||||
!expectedUserServiceNoEvent &&
|
!expectedUserServiceNoEvent &&
|
||||||
!expectedPrivacyInvalidParameter)
|
!expectedPrivacyInvalidParameter)
|
||||||
|
|||||||
+286
-264
@@ -52,8 +52,6 @@ public static class AgcExports
|
|||||||
private const uint RFlip = 0x17;
|
private const uint RFlip = 0x17;
|
||||||
private const uint RReleaseMem = 0x18;
|
private const uint RReleaseMem = 0x18;
|
||||||
private const uint RDmaData = 0x19;
|
private const uint RDmaData = 0x19;
|
||||||
private const uint RPredication = 0x1A;
|
|
||||||
private const uint RJump = 0x1B;
|
|
||||||
private const uint SpiShaderPgmLoPs = 0x8;
|
private const uint SpiShaderPgmLoPs = 0x8;
|
||||||
private const uint SpiShaderPgmHiPs = 0x9;
|
private const uint SpiShaderPgmHiPs = 0x9;
|
||||||
private const uint SpiShaderPgmLoEs = 0xC8;
|
private const uint SpiShaderPgmLoEs = 0xC8;
|
||||||
@@ -122,6 +120,9 @@ public static class AgcExports
|
|||||||
private const uint RegisterDefaultsVersion13 = 13;
|
private const uint RegisterDefaultsVersion13 = 13;
|
||||||
private const int RegisterDefaultsSize = 0x40;
|
private const int RegisterDefaultsSize = 0x40;
|
||||||
private const int RegisterDefaultBlockSize = 16 * 8;
|
private const int RegisterDefaultBlockSize = 16 * 8;
|
||||||
|
private const ulong ResourceRegistrationBytesPerResource = 0x118;
|
||||||
|
private const ulong ResourceRegistrationBytesPerOwner = 0x1E0;
|
||||||
|
private const int ResourceRegistrationMaxNameLength = 256;
|
||||||
|
|
||||||
private const ulong ShaderUserDataOffset = 0x08;
|
private const ulong ShaderUserDataOffset = 0x08;
|
||||||
private const ulong ShaderCodeOffset = 0x10;
|
private const ulong ShaderCodeOffset = 0x10;
|
||||||
@@ -390,10 +391,26 @@ public static class AgcExports
|
|||||||
public SubmittedDcbState Graphics { get; } = new();
|
public SubmittedDcbState Graphics { get; } = new();
|
||||||
public Dictionary<uint, SubmittedDcbState> ComputeQueues { get; } = new();
|
public Dictionary<uint, SubmittedDcbState> ComputeQueues { get; } = new();
|
||||||
public Dictionary<ulong, ComputeImageWriter> ComputeImageWriters { get; } = new();
|
public Dictionary<ulong, ComputeImageWriter> ComputeImageWriters { get; } = new();
|
||||||
|
public Dictionary<uint, string> ResourceOwners { get; } = new();
|
||||||
|
public Dictionary<uint, RegisteredAgcResource> RegisteredResources { get; } = new();
|
||||||
|
public bool ResourceRegistrationInitialized { get; set; }
|
||||||
|
public ulong ResourceRegistrationMemory { get; set; }
|
||||||
|
public ulong ResourceRegistrationMemorySize { get; set; }
|
||||||
|
public uint ResourceRegistrationMaxOwners { get; set; }
|
||||||
|
public uint DefaultOwner { get; set; } = DefaultAgcOwner;
|
||||||
|
public uint NextOwner { get; set; } = 1;
|
||||||
|
public uint NextResource { get; set; } = 1;
|
||||||
public ulong WorkSequence { get; set; }
|
public ulong WorkSequence { get; set; }
|
||||||
public ulong FlipSequence { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly record struct RegisteredAgcResource(
|
||||||
|
uint Owner,
|
||||||
|
ulong Address,
|
||||||
|
ulong Size,
|
||||||
|
string Name,
|
||||||
|
uint Type,
|
||||||
|
uint Flags);
|
||||||
|
|
||||||
private readonly record struct RegisterDefaultValue(uint Offset, uint Value);
|
private readonly record struct RegisterDefaultValue(uint Offset, uint Value);
|
||||||
|
|
||||||
private readonly record struct RegisterDefaultGroup(
|
private readonly record struct RegisterDefaultGroup(
|
||||||
@@ -1926,90 +1943,6 @@ public static class AgcExports
|
|||||||
return ReturnPointer(ctx, commandAddress);
|
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(
|
[SysAbiExport(
|
||||||
Nid = "w2rJhmD+dsE",
|
Nid = "w2rJhmD+dsE",
|
||||||
ExportName = "sceAgcDriverAddEqEvent",
|
ExportName = "sceAgcDriverAddEqEvent",
|
||||||
@@ -2062,22 +1995,9 @@ public static class AgcExports
|
|||||||
public static int DriverSubmitDcb(CpuContext ctx)
|
public static int DriverSubmitDcb(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var packetAddress = ctx[CpuRegister.Rdi];
|
var packetAddress = ctx[CpuRegister.Rdi];
|
||||||
// Gen5 supports direct RSI/RDX and packed RDI submissions.
|
if (packetAddress == 0 ||
|
||||||
var commandAddress = ctx[CpuRegister.Rsi];
|
!ctx.TryReadUInt64(packetAddress, out var commandAddress) ||
|
||||||
var directDwordCount = ctx[CpuRegister.Rdx];
|
!ctx.TryReadUInt32(packetAddress + 8, out var dwordCount))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
}
|
}
|
||||||
@@ -2093,29 +2013,16 @@ public static class AgcExports
|
|||||||
|
|
||||||
if (tracePackets)
|
if (tracePackets)
|
||||||
{
|
{
|
||||||
TraceAgc(
|
TraceAgc($"agc.driver_submit_dcb packet=0x{packetAddress:X16} addr=0x{commandAddress:X16} dwords={dwordCount}");
|
||||||
$"agc.driver_submit_dcb abi={(directArguments ? "direct" : "packed")} " +
|
|
||||||
$"packet=0x{packetAddress:X16} addr=0x{commandAddress:X16} dwords={dwordCount}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var gpuState = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
var gpuState = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
ulong flipSequenceBefore;
|
|
||||||
lock (gpuState.Gate)
|
lock (gpuState.Gate)
|
||||||
{
|
{
|
||||||
flipSequenceBefore = gpuState.FlipSequence;
|
|
||||||
ParseSubmittedDcb(ctx, gpuState, gpuState.Graphics, commandAddress, dwordCount, tracePackets);
|
ParseSubmittedDcb(ctx, gpuState, gpuState.Graphics, commandAddress, dwordCount, tracePackets);
|
||||||
DrainResumableDcbs(ctx, gpuState, 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;
|
ctx[CpuRegister.Rax] = 0;
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
@@ -2141,10 +2048,8 @@ public static class AgcExports
|
|||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal);
|
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal);
|
||||||
|
|
||||||
var gpuState = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
var gpuState = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
ulong flipSequenceBefore;
|
|
||||||
lock (gpuState.Gate)
|
lock (gpuState.Gate)
|
||||||
{
|
{
|
||||||
flipSequenceBefore = gpuState.FlipSequence;
|
|
||||||
for (uint i = 0; i < bufferCount; i++)
|
for (uint i = 0; i < bufferCount; i++)
|
||||||
{
|
{
|
||||||
if (!ctx.TryReadUInt64(addressArray + i * 8, out var commandAddress) ||
|
if (!ctx.TryReadUInt64(addressArray + i * 8, out var commandAddress) ||
|
||||||
@@ -2168,14 +2073,6 @@ public static class AgcExports
|
|||||||
DrainResumableDcbs(ctx, gpuState, tracePackets);
|
DrainResumableDcbs(ctx, gpuState, tracePackets);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpuState.FlipSequence == flipSequenceBefore)
|
|
||||||
{
|
|
||||||
KernelEventQueueCompatExports.TriggerRegisteredEvents(
|
|
||||||
0,
|
|
||||||
KernelEventQueueCompatExports.KernelEventFilterGraphics,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
ctx[CpuRegister.Rax] = 0;
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
@@ -2243,12 +2140,87 @@ public static class AgcExports
|
|||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx.TryWriteUInt32(outAddress, 256))
|
if (!ctx.TryWriteUInt32(outAddress, ResourceRegistrationMaxNameLength))
|
||||||
{
|
{
|
||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceAgc($"agc.driver_get_resource_registration_max_name_length out=0x{outAddress:X16} value=256");
|
TraceAgc(
|
||||||
|
$"agc.driver_get_resource_registration_max_name_length " +
|
||||||
|
$"out=0x{outAddress:X16} value={ResourceRegistrationMaxNameLength}");
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "AOLcoIkQDgM",
|
||||||
|
ExportName = "sceAgcDriverQueryResourceRegistrationUserMemoryRequirements",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DriverQueryResourceRegistrationUserMemoryRequirements(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var sizeAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var resourceCount = ctx[CpuRegister.Rsi];
|
||||||
|
var ownerCount = ctx[CpuRegister.Rdx];
|
||||||
|
if (sizeAddress == 0 || resourceCount == 0 || ownerCount == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong requiredSize;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
requiredSize = checked(
|
||||||
|
resourceCount * ResourceRegistrationBytesPerResource +
|
||||||
|
ownerCount * ResourceRegistrationBytesPerOwner);
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteUInt64(sizeAddress, requiredSize))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc(
|
||||||
|
$"agc.driver_query_resource_registration_memory resources={resourceCount} " +
|
||||||
|
$"owners={ownerCount} bytes=0x{requiredSize:X}");
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "F0Y42t-3e18",
|
||||||
|
ExportName = "sceAgcDriverInitResourceRegistration",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DriverInitResourceRegistration(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var memoryAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var memorySize = ctx[CpuRegister.Rsi];
|
||||||
|
var ownerCount = ctx[CpuRegister.Rdx];
|
||||||
|
if (memoryAddress == 0 || memorySize == 0 || ownerCount == 0 || ownerCount > uint.MaxValue)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
state.ResourceRegistrationInitialized = true;
|
||||||
|
state.ResourceRegistrationMemory = memoryAddress;
|
||||||
|
state.ResourceRegistrationMemorySize = memorySize;
|
||||||
|
state.ResourceRegistrationMaxOwners = (uint)ownerCount;
|
||||||
|
state.ResourceOwners.Clear();
|
||||||
|
state.RegisteredResources.Clear();
|
||||||
|
state.DefaultOwner = DefaultAgcOwner;
|
||||||
|
state.NextOwner = 1;
|
||||||
|
state.NextResource = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc(
|
||||||
|
$"agc.driver_init_resource_registration memory=0x{memoryAddress:X16} " +
|
||||||
|
$"bytes=0x{memorySize:X} owners={ownerCount}");
|
||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2267,12 +2239,97 @@ public static class AgcExports
|
|||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx.TryWriteUInt32(ownerAddress, DefaultAgcOwner))
|
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
|
uint owner;
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
owner = state.DefaultOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteUInt32(ownerAddress, owner))
|
||||||
{
|
{
|
||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceAgc($"agc.driver_get_default_owner out=0x{ownerAddress:X16} owner={DefaultAgcOwner}");
|
TraceAgc($"agc.driver_get_default_owner out=0x{ownerAddress:X16} owner={owner}");
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "U9ueyEhSkF4",
|
||||||
|
ExportName = "sceAgcDriverRegisterDefaultOwner",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DriverRegisterDefaultOwner(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var owner = (uint)ctx[CpuRegister.Rdi];
|
||||||
|
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
state.DefaultOwner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc($"agc.driver_register_default_owner owner={owner}");
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "X-Nm5KLREeg",
|
||||||
|
ExportName = "sceAgcDriverRegisterOwner",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DriverRegisterOwner(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var ownerAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var nameAddress = ctx[CpuRegister.Rsi];
|
||||||
|
if (ownerAddress == 0 || nameAddress == 0 ||
|
||||||
|
!TryReadGuestCString(
|
||||||
|
ctx,
|
||||||
|
nameAddress,
|
||||||
|
ResourceRegistrationMaxNameLength,
|
||||||
|
out var nameBytes))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
|
uint owner;
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
if (!state.ResourceRegistrationInitialized ||
|
||||||
|
state.ResourceRegistrationMaxOwners != 0 &&
|
||||||
|
state.ResourceOwners.Count >= state.ResourceRegistrationMaxOwners)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
owner = state.NextOwner;
|
||||||
|
while (owner == state.DefaultOwner || state.ResourceOwners.ContainsKey(owner))
|
||||||
|
{
|
||||||
|
owner++;
|
||||||
|
if (owner == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.NextOwner = owner + 1;
|
||||||
|
state.ResourceOwners.Add(owner, System.Text.Encoding.UTF8.GetString(nameBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteUInt32(ownerAddress, owner))
|
||||||
|
{
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
state.ResourceOwners.Remove(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc(
|
||||||
|
$"agc.driver_register_owner out=0x{ownerAddress:X16} owner={owner} " +
|
||||||
|
$"name={System.Text.Encoding.UTF8.GetString(nameBytes)}");
|
||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2283,19 +2340,90 @@ public static class AgcExports
|
|||||||
LibraryName = "libSceAgc")]
|
LibraryName = "libSceAgc")]
|
||||||
public static int DriverRegisterResource(CpuContext ctx)
|
public static int DriverRegisterResource(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var resourceAddress = ctx[CpuRegister.Rdi];
|
var resourceHandleAddress = ctx[CpuRegister.Rdi];
|
||||||
var owner = (uint)ctx[CpuRegister.Rsi];
|
var owner = (uint)ctx[CpuRegister.Rsi];
|
||||||
var nameAddress = ctx[CpuRegister.Rdx];
|
var resourceAddress = ctx[CpuRegister.Rdx];
|
||||||
var type = (uint)ctx[CpuRegister.R8];
|
var resourceSize = ctx[CpuRegister.Rcx];
|
||||||
var flags = (uint)ctx[CpuRegister.R9];
|
var nameAddress = ctx[CpuRegister.R8];
|
||||||
|
var type = (uint)ctx[CpuRegister.R9];
|
||||||
|
if (resourceHandleAddress == 0 || resourceAddress == 0 || resourceSize == 0 ||
|
||||||
|
!ctx.TryReadUInt32(ctx[CpuRegister.Rsp] + sizeof(ulong), out var flags) ||
|
||||||
|
!TryReadGuestCString(
|
||||||
|
ctx,
|
||||||
|
nameAddress,
|
||||||
|
ResourceRegistrationMaxNameLength,
|
||||||
|
out var nameBytes))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
|
uint resourceHandle;
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
if (!state.ResourceRegistrationInitialized ||
|
||||||
|
owner != state.DefaultOwner &&
|
||||||
|
!state.ResourceOwners.ContainsKey(owner))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceHandle = state.NextResource++;
|
||||||
|
if (resourceHandle == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.RegisteredResources.Add(
|
||||||
|
resourceHandle,
|
||||||
|
new RegisteredAgcResource(
|
||||||
|
owner,
|
||||||
|
resourceAddress,
|
||||||
|
resourceSize,
|
||||||
|
System.Text.Encoding.UTF8.GetString(nameBytes),
|
||||||
|
type,
|
||||||
|
flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteUInt32(resourceHandleAddress, resourceHandle))
|
||||||
|
{
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
state.RegisteredResources.Remove(resourceHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
TraceAgc(
|
TraceAgc(
|
||||||
$"agc.driver_register_resource resource=0x{resourceAddress:X16} owner={owner} " +
|
$"agc.driver_register_resource handle={resourceHandle} owner={owner} " +
|
||||||
$"name=0x{nameAddress:X16} type={type} flags={flags}");
|
$"resource=0x{resourceAddress:X16} bytes=0x{resourceSize:X} " +
|
||||||
|
$"name={System.Text.Encoding.UTF8.GetString(nameBytes)} type={type} flags={flags}");
|
||||||
|
|
||||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "pWLG7WOpVcw",
|
||||||
|
ExportName = "sceAgcDriverUnregisterResource",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DriverUnregisterResource(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var resourceHandle = (uint)ctx[CpuRegister.Rdi];
|
||||||
|
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
|
||||||
|
lock (state.Gate)
|
||||||
|
{
|
||||||
|
if (!state.RegisteredResources.Remove(resourceHandle))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc($"agc.driver_unregister_resource handle={resourceHandle}");
|
||||||
|
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "-KRzWekV120",
|
Nid = "-KRzWekV120",
|
||||||
ExportName = "sceAgcDriverUnknown_KRzWekV120",
|
ExportName = "sceAgcDriverUnknown_KRzWekV120",
|
||||||
@@ -2424,40 +2552,6 @@ public static class AgcExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
var packetType = header >> 30;
|
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 (packetType == 2)
|
||||||
{
|
{
|
||||||
if (tracePackets)
|
if (tracePackets)
|
||||||
@@ -2472,15 +2566,13 @@ public static class AgcExports
|
|||||||
|
|
||||||
if (packetType != 3)
|
if (packetType != 3)
|
||||||
{
|
{
|
||||||
offset++;
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var length = Pm4Length(header);
|
var length = Pm4Length(header);
|
||||||
if (length == 0 || offset + length > dwordCount)
|
if (length == 0 || offset + length > dwordCount)
|
||||||
{
|
{
|
||||||
offset++;
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var op = (header >> 8) & 0xFFu;
|
var op = (header >> 8) & 0xFFu;
|
||||||
@@ -2718,7 +2810,6 @@ public static class AgcExports
|
|||||||
|
|
||||||
if (op == ItNop && register == RFlip && length >= 6)
|
if (op == ItNop && register == RFlip && length >= 6)
|
||||||
{
|
{
|
||||||
gpuState.FlipSequence++;
|
|
||||||
if (!ctx.TryReadUInt32(currentAddress + 4, out var videoOutHandle) ||
|
if (!ctx.TryReadUInt32(currentAddress + 4, out var videoOutHandle) ||
|
||||||
!ctx.TryReadUInt32(currentAddress + 8, out var displayBufferIndexRaw) ||
|
!ctx.TryReadUInt32(currentAddress + 8, out var displayBufferIndexRaw) ||
|
||||||
!ctx.TryReadUInt32(currentAddress + 12, out var flipMode) ||
|
!ctx.TryReadUInt32(currentAddress + 12, out var flipMode) ||
|
||||||
@@ -2731,8 +2822,23 @@ public static class AgcExports
|
|||||||
var flipArg = unchecked((long)(((ulong)flipArgHi << 32) | flipArgLo));
|
var flipArg = unchecked((long)(((ulong)flipArgHi << 32) | flipArgLo));
|
||||||
var displayBufferIndex = unchecked((int)displayBufferIndexRaw);
|
var displayBufferIndex = unchecked((int)displayBufferIndexRaw);
|
||||||
var handle = unchecked((int)videoOutHandle);
|
var handle = unchecked((int)videoOutHandle);
|
||||||
// Prefer output decoded from the current DCB.
|
if (VideoOutExports.TryGetDisplayBufferInfo(
|
||||||
if (state.SawIndexedDraw &&
|
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 &&
|
state.TranslatedDraw is { } translatedDraw &&
|
||||||
VideoOutExports.TryGetDisplayBufferInfo(
|
VideoOutExports.TryGetDisplayBufferInfo(
|
||||||
handle,
|
handle,
|
||||||
@@ -2796,22 +2902,6 @@ public static class AgcExports
|
|||||||
displayBuffer.Width,
|
displayBuffer.Width,
|
||||||
displayBuffer.Height);
|
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);
|
_ = VideoOutExports.SubmitFlipFromAgc(ctx, handle, displayBufferIndex, unchecked((int)flipMode), flipArg);
|
||||||
state.SawIndexedDraw = false;
|
state.SawIndexedDraw = false;
|
||||||
@@ -4133,8 +4223,6 @@ public static class AgcExports
|
|||||||
|
|
||||||
TraceTextureHash(descriptor, source);
|
TraceTextureHash(descriptor, source);
|
||||||
|
|
||||||
if (_traceAgcShader)
|
|
||||||
{
|
|
||||||
var nonZero = 0;
|
var nonZero = 0;
|
||||||
for (var i = 0; i < source.Length; i++)
|
for (var i = 0; i < source.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -4154,7 +4242,6 @@ public static class AgcExports
|
|||||||
$"size={descriptor.Width}x{descriptor.Height} pitch={descriptor.Pitch} " +
|
$"size={descriptor.Width}x{descriptor.Height} pitch={descriptor.Pitch} " +
|
||||||
$"dst=0x{descriptor.DstSelect:X3} " +
|
$"dst=0x{descriptor.DstSelect:X3} " +
|
||||||
$"bytes={source.Length} nonzero64={nonZero}");
|
$"bytes={source.Length} nonzero64={nonZero}");
|
||||||
}
|
|
||||||
|
|
||||||
var rgba = source;
|
var rgba = source;
|
||||||
texture = new VulkanGuestDrawTexture(
|
texture = new VulkanGuestDrawTexture(
|
||||||
@@ -4414,7 +4501,7 @@ public static class AgcExports
|
|||||||
var localSizeZ = GetComputeLocalSize(state.ShRegisters, ComputeNumThreadZ);
|
var localSizeZ = GetComputeLocalSize(state.ShRegisters, ComputeNumThreadZ);
|
||||||
var gpuDispatch = false;
|
var gpuDispatch = false;
|
||||||
var computeError = string.Empty;
|
var computeError = string.Empty;
|
||||||
if (hasStorageBinding &&
|
if ((hasStorageBinding || evaluation.GlobalMemoryBindings.Count != 0) &&
|
||||||
(ulong)localSizeX * localSizeY * localSizeZ <= 1024)
|
(ulong)localSizeX * localSizeY * localSizeZ <= 1024)
|
||||||
{
|
{
|
||||||
var shaderKey = (
|
var shaderKey = (
|
||||||
@@ -4485,6 +4572,7 @@ public static class AgcExports
|
|||||||
$"local={localSizeX}x{localSizeY}x{localSizeZ} " +
|
$"local={localSizeX}x{localSizeY}x{localSizeZ} " +
|
||||||
$"sys={DescribeComputeSystemRegisters(computeSystemRegisters)} " +
|
$"sys={DescribeComputeSystemRegisters(computeSystemRegisters)} " +
|
||||||
$"gpu={gpuDispatch} blits={blitCount} " +
|
$"gpu={gpuDispatch} blits={blitCount} " +
|
||||||
|
$"globals={evaluation.GlobalMemoryBindings.Count}" +
|
||||||
(computeError.Length == 0 ? string.Empty : $" error={computeError}") +
|
(computeError.Length == 0 ? string.Empty : $" error={computeError}") +
|
||||||
$" bindings=[{string.Join(',', descriptions)}]");
|
$" bindings=[{string.Join(',', descriptions)}]");
|
||||||
}
|
}
|
||||||
@@ -5865,16 +5953,6 @@ public static class AgcExports
|
|||||||
Console.Error.WriteLine($"[LOADER][TRACE] {message}");
|
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)
|
private static void TraceAgcShader(string message)
|
||||||
{
|
{
|
||||||
if (!_traceAgcShader)
|
if (!_traceAgcShader)
|
||||||
@@ -5885,62 +5963,6 @@ public static class AgcExports
|
|||||||
Console.Error.WriteLine($"[LOADER][TRACE] {message}");
|
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>(T value) => _inner.AppendFormatted(value);
|
|
||||||
|
|
||||||
public void AppendFormatted<T>(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>(T value) => _inner.AppendFormatted(value);
|
|
||||||
|
|
||||||
public void AppendFormatted<T>(T value, string? format) =>
|
|
||||||
_inner.AppendFormatted(value, format);
|
|
||||||
|
|
||||||
public string ToStringAndClear() => _inner.ToStringAndClear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatShaderDwords(IReadOnlyList<uint> values) =>
|
private static string FormatShaderDwords(IReadOnlyList<uint> values) =>
|
||||||
values.Count == 0
|
values.Count == 0
|
||||||
? "none"
|
? "none"
|
||||||
|
|||||||
@@ -1683,6 +1683,30 @@ public static class KernelMemoryCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
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(
|
[SysAbiExport(
|
||||||
Nid = "gEpBkcwxUjw",
|
Nid = "gEpBkcwxUjw",
|
||||||
ExportName = "sceKernelAprResolveFilepathsToIdsAndFileSizes",
|
ExportName = "sceKernelAprResolveFilepathsToIdsAndFileSizes",
|
||||||
@@ -3965,7 +3989,7 @@ public static class KernelMemoryCompatExports
|
|||||||
_ => unchecked((int)argumentSource.NextGpArg())
|
_ => unchecked((int)argumentSource.NextGpArg())
|
||||||
};
|
};
|
||||||
|
|
||||||
var formatted = value.ToString();
|
var formatted = value.ToString(CultureInfo.InvariantCulture);
|
||||||
if (showSign && value >= 0)
|
if (showSign && value >= 0)
|
||||||
formatted = "+" + formatted;
|
formatted = "+" + formatted;
|
||||||
else if (spaceForSign && value >= 0)
|
else if (spaceForSign && value >= 0)
|
||||||
@@ -3989,7 +4013,7 @@ public static class KernelMemoryCompatExports
|
|||||||
_ => (uint)argumentSource.NextGpArg()
|
_ => (uint)argumentSource.NextGpArg()
|
||||||
};
|
};
|
||||||
|
|
||||||
var formatted = value.ToString();
|
var formatted = value.ToString(CultureInfo.InvariantCulture);
|
||||||
sb.Append(PadString(formatted, width, leftAlign, padWithZero && !leftAlign));
|
sb.Append(PadString(formatted, width, leftAlign, padWithZero && !leftAlign));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -4010,8 +4034,8 @@ public static class KernelMemoryCompatExports
|
|||||||
};
|
};
|
||||||
|
|
||||||
var formatted = specifier == 'x'
|
var formatted = specifier == 'x'
|
||||||
? value.ToString("x")
|
? value.ToString("x", CultureInfo.InvariantCulture)
|
||||||
: value.ToString("X");
|
: value.ToString("X", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
if (alternateForm && value != 0)
|
if (alternateForm && value != 0)
|
||||||
formatted = specifier == 'x' ? "0x" + formatted : "0X" + formatted;
|
formatted = specifier == 'x' ? "0x" + formatted : "0X" + formatted;
|
||||||
@@ -4119,7 +4143,10 @@ public static class KernelMemoryCompatExports
|
|||||||
var formatStr = precision >= 0
|
var formatStr = precision >= 0
|
||||||
? $"{{0:{specifier}{precision}}}"
|
? $"{{0:{specifier}{precision}}}"
|
||||||
: $"{{0:{specifier}}}";
|
: $"{{0:{specifier}}}";
|
||||||
var formatted = string.Format(formatStr, value);
|
var formatted = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
formatStr,
|
||||||
|
value);
|
||||||
|
|
||||||
if (showSign && value >= 0)
|
if (showSign && value >= 0)
|
||||||
formatted = "+" + formatted;
|
formatted = "+" + formatted;
|
||||||
|
|||||||
@@ -431,6 +431,18 @@ public static class KernelRuntimeCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
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(
|
[SysAbiExport(
|
||||||
Nid = "fgxnMeTNUtY",
|
Nid = "fgxnMeTNUtY",
|
||||||
ExportName = "sceKernelGetProcessTimeCounter",
|
ExportName = "sceKernelGetProcessTimeCounter",
|
||||||
|
|||||||
@@ -30,13 +30,17 @@ public static class SaveDataExports
|
|||||||
private const uint MountModeCreate2 = 1u << 5;
|
private const uint MountModeCreate2 = 1u << 5;
|
||||||
private const int MountResultSize = 0x40;
|
private const int MountResultSize = 0x40;
|
||||||
private static readonly object _stateGate = new();
|
private static readonly object _stateGate = new();
|
||||||
|
private static readonly HashSet<int> _transactionResources = [];
|
||||||
private static string? _titleId;
|
private static string? _titleId;
|
||||||
|
private static int _nextTransactionResource;
|
||||||
|
|
||||||
public static void ConfigureApplicationInfo(string? titleId)
|
public static void ConfigureApplicationInfo(string? titleId)
|
||||||
{
|
{
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
{
|
{
|
||||||
_titleId = string.IsNullOrWhiteSpace(titleId) ? null : SanitizePathSegment(titleId.Trim());
|
_titleId = string.IsNullOrWhiteSpace(titleId) ? null : SanitizePathSegment(titleId.Trim());
|
||||||
|
_transactionResources.Clear();
|
||||||
|
_nextTransactionResource = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,26 +255,32 @@ public static class SaveDataExports
|
|||||||
LibraryName = "libSceSaveData")]
|
LibraryName = "libSceSaveData")]
|
||||||
public static int SaveDataCreateTransactionResource(CpuContext ctx)
|
public static int SaveDataCreateTransactionResource(CpuContext ctx)
|
||||||
{
|
{
|
||||||
// Gen5 ABI: memory size, resource output, reserved.
|
|
||||||
var memorySize = ctx[CpuRegister.Rdi];
|
var memorySize = ctx[CpuRegister.Rdi];
|
||||||
var resourceAddress = ctx[CpuRegister.Rsi];
|
int resource;
|
||||||
var reserved = ctx[CpuRegister.Rdx];
|
lock (_stateGate)
|
||||||
|
|
||||||
if (resourceAddress == 0)
|
|
||||||
{
|
{
|
||||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
resource = ++_nextTransactionResource;
|
||||||
|
_transactionResources.Add(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offline HLE operations carry no transaction state.
|
TraceSaveData($"create_transaction_resource memory_size=0x{memorySize:X} resource={resource}");
|
||||||
if (!ctx.TryWriteUInt64(resourceAddress, 0))
|
return ctx.SetReturn(resource);
|
||||||
{
|
|
||||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceSaveData(
|
[SysAbiExport(
|
||||||
$"create_transaction_resource memory_size=0x{memorySize:X} reserved=0x{reserved:X} " +
|
Nid = "lJUQuaKqoKY",
|
||||||
$"resource_addr=0x{resourceAddress:X} resource=0x0");
|
ExportName = "sceSaveDataDeleteTransactionResource",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceSaveData")]
|
||||||
|
public static int SaveDataDeleteTransactionResource(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var resource = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
|
lock (_stateGate)
|
||||||
|
{
|
||||||
|
_transactionResources.Remove(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceSaveData($"delete_transaction_resource resource={resource}");
|
||||||
return ctx.SetReturn(0);
|
return ctx.SetReturn(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user