Compare commits

...

1 Commits

Author SHA1 Message Date
kuba aad66f89d5 [Kernel] Run one mapping transaction per sceKernelBatchMap
A mapping change has to be handed off to the GPU worker before any lock the
image and buffer reads need, and the handoff waits for the GPU to drain.
sceKernelBatchMap walked its entries calling the same per-mapping entry points
the single-mapping exports use, so every entry in the batch took its own
handoff and its own drain.

The batch now opens one transaction and runs all of its entries inside it. A
thread-local flag marks that a transaction is already open so a nested entry
point runs directly instead of handing off again, and the flag is cleared in a
finally so an entry that throws cannot leave it set.
2026-09-30 00:30:37 +02:00
2 changed files with 22 additions and 2 deletions

View File

@ -329,16 +329,31 @@ public static partial class KernelMemoryCompatExports
internal static int ReserveBackingRange(CpuContext ctx, ulong pointer, ulong length, ulong flags, ulong alignment)
=> RunMappingTransaction(() => ReserveBackingRangeCore(ctx, pointer, length, flags, alignment));
// A batch wraps its entries in one transaction, so the entries must not hand off again.
[ThreadStatic]
private static bool _insideMappingTransaction;
private static int RunMappingTransaction(Func<int> transaction)
{
// GPU handoff must precede locks needed by image and buffer reads.
if (Monitor.IsEntered(_memoryGate))
throw new InvalidOperationException("Cannot start a mapping transaction while holding the mapping lock.");
if (GuestGpuMemoryHook.Current is not { } memory)
if (_insideMappingTransaction || GuestGpuMemoryHook.Current is not { } memory)
return transaction();
var result = MemoryFault;
memory.RunMappingChange(() => result = transaction());
memory.RunMappingChange(() =>
{
_insideMappingTransaction = true;
try
{
result = transaction();
}
finally
{
_insideMappingTransaction = false;
}
});
return result;
}

View File

@ -5777,7 +5777,12 @@ public static partial class KernelMemoryCompatExports
return TryWriteCompat(ctx, address, bytes);
}
// One transaction for the whole batch: every entry would otherwise hand the GPU worker
// its own mapping change and wait for the GPU to drain before it could run.
private static int KernelBatchMapCore(CpuContext ctx, int flags)
=> RunMappingTransaction(() => KernelBatchMapTransaction(ctx, flags));
private static int KernelBatchMapTransaction(CpuContext ctx, int flags)
{
var entriesAddress = ctx[CpuRegister.Rdi];
var entryCount = unchecked((int)ctx[CpuRegister.Rsi]);