From dce7c87c4d580e0804c6919054f2895ecf1a1206 Mon Sep 17 00:00:00 2001 From: TarkusTK <90155059+TarkusR@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:43:47 +0200 Subject: [PATCH] [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. --- src/SharpEmu.Libs/Agc/AgcExports.cs | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index 4995d79d..6c852693 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -11506,6 +11506,69 @@ public static partial class AgcExports return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK); } + private static int RemoveResourcesForOwner(SubmittedGpuState state, uint owner) + { + var stale = new List(); + 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( Nid = "pWLG7WOpVcw", ExportName = "sceAgcDriverUnregisterResource",