[AGC] Implement the owner-scoped resource unregister exports (#469)

sceAgcDriverUnregisterOwnerAndResources (ZLJk9r2+2Aw) and
sceAgcDriverUnregisterAllResourcesForOwner (SCoAN5fYlUM) were
unresolved. We already register owners and resources, and the guest
registers a resource owner per streaming batch, so with no way to
release one the fixed owner pool filled up: sceAgcDriverRegisterOwner
started failing and the guest logged its own "Agc registerOwner error:
0x80020003", after which it kept half-registering records. Demon's Souls
then crashed scanning that registry.

Owner-scoped teardown is straightforward because RegisteredAgcResource
already carries its owner, so both entry points share one sweep over the
resource table. UnregisterOwnerAndResources additionally drops the owner
itself and its compute queue, and reports INVALID_ARGUMENT for an owner
that was never registered. The existing single-resource
sceAgcDriverUnregisterResource (pWLG7WOpVcw) is unchanged.

Both NIDs are checked against their export names by the SHEM004
analyzer, which fails the build on a mismatch.

Tested on Demon's Souls (PPSA01342): the registerOwner error no longer
appears and the registry stays consistent across streaming batches. 470
tests pass.
This commit is contained in:
TarkusTK
2026-07-20 14:43:47 +02:00
committed by GitHub
parent 6ee445f0c2
commit dce7c87c4d
+63
View File
@@ -11506,6 +11506,69 @@ public static partial class AgcExports
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK); return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
} }
private static int RemoveResourcesForOwner(SubmittedGpuState state, uint owner)
{
var stale = new List<uint>();
foreach (var (handle, resource) in state.RegisteredResources)
{
if (resource.Owner == owner)
{
stale.Add(handle);
}
}
foreach (var handle in stale)
{
state.RegisteredResources.Remove(handle);
}
return stale.Count;
}
[SysAbiExport(
Nid = "ZLJk9r2+2Aw",
ExportName = "sceAgcDriverUnregisterOwnerAndResources",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DriverUnregisterOwnerAndResources(CpuContext ctx)
{
var owner = (uint)ctx[CpuRegister.Rdi];
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
int resources;
lock (state.Gate)
{
if (!state.ResourceOwners.Remove(owner))
{
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
resources = RemoveResourcesForOwner(state, owner);
state.ComputeQueues.Remove(owner);
}
TraceAgc($"agc.driver_unregister_owner owner={owner} resources={resources}");
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
}
[SysAbiExport(
Nid = "SCoAN5fYlUM",
ExportName = "sceAgcDriverUnregisterAllResourcesForOwner",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DriverUnregisterAllResourcesForOwner(CpuContext ctx)
{
var owner = (uint)ctx[CpuRegister.Rdi];
var state = _submittedGpuStates.GetValue(ctx.Memory, static _ => new SubmittedGpuState());
int resources;
lock (state.Gate)
{
resources = RemoveResourcesForOwner(state, owner);
}
TraceAgc($"agc.driver_unregister_owner_resources owner={owner} resources={resources}");
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
}
[SysAbiExport( [SysAbiExport(
Nid = "pWLG7WOpVcw", Nid = "pWLG7WOpVcw",
ExportName = "sceAgcDriverUnregisterResource", ExportName = "sceAgcDriverUnregisterResource",