mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-31 06:59:45 +08:00
Sdl backend (#670)
* [audio] added sdl audio backend and in-tree atrac9 decoder * [input] replaced per-platform pad readers with sdl gamepad input * [video] added sdl window and host display plumbing * [gui] added host display options and per-game render settings * [bink] synced host movie playback to the guest audio clock * [cpu] hooked windows write faults into guest image tracking * [perf] added guest and render profiling, reserved host cpu lanes * [kernel] fixed stale pthread mutex handle alias * [host] wired the sdl session, save-data paths and project references * [audio] hoisted ajm trace stackalloc out of its loop * [video] Add guest image sync setting * [build] Strip native symbols * reuse
This commit is contained in:
@@ -2,13 +2,17 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Acm;
|
||||
|
||||
public static class AcmExports
|
||||
{
|
||||
private const int AcmBatchErrorBytes = 32;
|
||||
private static readonly ConcurrentDictionary<uint, byte> Contexts = new();
|
||||
private static int _nextContextHandle;
|
||||
private static int _nextBatchHandle;
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "ZIXln2K3XMk",
|
||||
@@ -23,12 +27,17 @@ public static class AcmExports
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
var handle = (ulong)Interlocked.Increment(ref _nextContextHandle);
|
||||
Span<byte> handleBytes = stackalloc byte[sizeof(ulong)];
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(handleBytes, handle);
|
||||
return ctx.Memory.TryWrite(outContextAddress, handleBytes)
|
||||
? ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
: ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
var handle = unchecked((uint)Interlocked.Increment(ref _nextContextHandle));
|
||||
Span<byte> handleBytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(handleBytes, handle);
|
||||
if (!ctx.Memory.TryWrite(outContextAddress, handleBytes))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
Contexts[handle] = 0;
|
||||
Trace($"context_create context={handle}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -38,7 +47,196 @@ public static class AcmExports
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmContextDestroy(CpuContext ctx)
|
||||
{
|
||||
_ = ctx;
|
||||
var context = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
Contexts.TryRemove(context, out _);
|
||||
Trace($"context_destroy context={context}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "tW9W+CAG4FE",
|
||||
ExportName = "sceAcmBatchStartBuffer",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmBatchStartBuffer(CpuContext ctx)
|
||||
{
|
||||
var context = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
var commandsAddress = ctx[CpuRegister.Rsi];
|
||||
var commandsSize = ctx[CpuRegister.Rdx];
|
||||
var errorAddress = ctx[CpuRegister.Rcx];
|
||||
var batchAddress = ctx[CpuRegister.R8];
|
||||
|
||||
if (!Contexts.ContainsKey(context) ||
|
||||
batchAddress == 0 ||
|
||||
(commandsSize != 0 && commandsAddress == 0))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
return CompleteBatchStart(ctx, context, 1, errorAddress, batchAddress);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "8fe55ktlNVo",
|
||||
ExportName = "sceAcmBatchStartBuffers",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmBatchStartBuffers(CpuContext ctx)
|
||||
{
|
||||
var context = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
var infoCount = unchecked((uint)ctx[CpuRegister.Rsi]);
|
||||
var infoArrayAddress = ctx[CpuRegister.Rdx];
|
||||
var errorAddress = ctx[CpuRegister.Rcx];
|
||||
var batchAddress = ctx[CpuRegister.R8];
|
||||
|
||||
if (!Contexts.ContainsKey(context) ||
|
||||
batchAddress == 0 ||
|
||||
(infoCount != 0 && infoArrayAddress == 0))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
return CompleteBatchStart(ctx, context, infoCount, errorAddress, batchAddress);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "RLN3gRlXJLE",
|
||||
ExportName = "sceAcmBatchWait",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmBatchWait(CpuContext ctx)
|
||||
{
|
||||
var context = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
return ctx.SetReturn(
|
||||
Contexts.ContainsKey(context)
|
||||
? OrbisGen2Result.ORBIS_GEN2_OK
|
||||
: OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "r7z5YQFZo+U",
|
||||
ExportName = "sceAcmBatchJobNotification",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmBatchJobNotification(CpuContext ctx) =>
|
||||
AdvanceBatchInfo(ctx, 32);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "u70oWo92SYQ",
|
||||
ExportName = "sceAcm_ConvReverb_SharedInput",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmConvReverbSharedInput(CpuContext ctx) =>
|
||||
AdvanceBatchInfo(ctx, 1024);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9nLbWmRDpa8",
|
||||
ExportName = "sceAcm_ConvReverb_SharedIr",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmConvReverbSharedIr(CpuContext ctx) =>
|
||||
AdvanceBatchInfo(ctx, 1024);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "KovqaFbmtsM",
|
||||
ExportName = "sceAcm_FFT",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmFft(CpuContext ctx) =>
|
||||
AdvanceBatchInfo(ctx, 256);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "DR-ZCmvVR9Q",
|
||||
ExportName = "sceAcm_IFFT",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmIfft(CpuContext ctx) =>
|
||||
AdvanceBatchInfo(ctx, 256);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "LA4RCNKnFjg",
|
||||
ExportName = "sceAcm_Panner",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAcm")]
|
||||
public static int AcmPanner(CpuContext ctx) =>
|
||||
AdvanceBatchInfo(ctx, 512);
|
||||
|
||||
internal static void ResetForTests()
|
||||
{
|
||||
Contexts.Clear();
|
||||
Interlocked.Exchange(ref _nextContextHandle, 0);
|
||||
Interlocked.Exchange(ref _nextBatchHandle, 0);
|
||||
}
|
||||
|
||||
private static int CompleteBatchStart(
|
||||
CpuContext ctx,
|
||||
uint context,
|
||||
uint infoCount,
|
||||
ulong errorAddress,
|
||||
ulong batchAddress)
|
||||
{
|
||||
if (errorAddress != 0)
|
||||
{
|
||||
Span<byte> error = stackalloc byte[AcmBatchErrorBytes];
|
||||
error.Clear();
|
||||
if (!ctx.Memory.TryWrite(errorAddress, error))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
var batch = unchecked((uint)Interlocked.Increment(ref _nextBatchHandle));
|
||||
Span<byte> batchBytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(batchBytes, batch);
|
||||
if (!ctx.Memory.TryWrite(batchAddress, batchBytes))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
Trace($"batch_start context={context} count={infoCount} batch={batch}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static int AdvanceBatchInfo(CpuContext ctx, ulong byteCount)
|
||||
{
|
||||
var infoAddress = ctx[CpuRegister.Rdi];
|
||||
if (infoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
Span<byte> info = stackalloc byte[24];
|
||||
if (!ctx.Memory.TryRead(infoAddress, info))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
var buffer = BinaryPrimitives.ReadUInt64LittleEndian(info);
|
||||
var offset = BinaryPrimitives.ReadUInt64LittleEndian(info[8..]);
|
||||
var size = BinaryPrimitives.ReadUInt64LittleEndian(info[16..]);
|
||||
if (buffer != 0 && size != 0)
|
||||
{
|
||||
var nextOffset = offset > ulong.MaxValue - byteCount
|
||||
? ulong.MaxValue
|
||||
: offset + byteCount;
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(info[8..], Math.Min(size, nextOffset));
|
||||
if (!ctx.Memory.TryWrite(infoAddress, info))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static void Trace(string message)
|
||||
{
|
||||
if (string.Equals(
|
||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_ACM"),
|
||||
"1",
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][TRACE] acm.{message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user