Move Tipc commands and fix remaining warnings

This commit is contained in:
Aaron Robinson 2025-11-19 21:46:10 -06:00
parent 8e1e2d69df
commit 73406d1f3a
11 changed files with 86 additions and 118 deletions

3
.gitignore vendored
View file

@ -185,3 +185,6 @@ PublishProfiles/
# Ignore distribution build files
distribution/macos/temp/
distribution/macos/output/
# MSBuild logs
*.binlog

View file

@ -2,8 +2,5 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsTrimmable>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<UseArtifactsOutput>true</UseArtifactsOutput>
</PropertyGroup>
</Project>

6
Directory.Build.targets Normal file
View file

@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<IsTrimmable Condition="'$(IsTrimmable)' == '' AND $([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsTrimmable>
<EnableTrimAnalyzer Condition="'$(EnableTrimAnalyzer)' == '' AND $([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</EnableTrimAnalyzer>
</PropertyGroup>
</Project>

View file

@ -2,6 +2,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<IsTrimmable>false</IsTrimmable>
<EnableTrimAnalyzer>false</EnableTrimAnalyzer>
</PropertyGroup>
<Target Name="PostBuildTarget" AfterTargets="AfterBuild">

View file

@ -37,7 +37,9 @@ namespace Ryujinx.Common.Logging.Formatters
return;
}
#pragma warning disable IL2075 // GetProperties is *probably* fine here, it only really matters what exists anyway
PropertyInfo[] props = dynamicObject.GetType().GetProperties();
#pragma warning restore IL2075
sb.Append('{');

View file

@ -83,35 +83,41 @@ namespace Ryujinx.HLE.Generators
generator.EnterScope($"namespace {data.Namespace}");
generator.EnterScope($"partial class {data.TypeName}");
generator.EnterScope("protected override RC InvokeCmifMethod(int id, ServiceCtx context)");
generator.EnterScope("switch (id)");
foreach (var command in data.CmifCommands)
{
generator.AppendLine($"case {string.Join(" or ", command.CommandIds)}:");
generator.IncreaseIndentation();
generator.AppendLine($"LogInvoke(\"{command.MethodName}\");");
generator.AppendLine($"return (RC){command.MethodName}(context);");
generator.DecreaseIndentation();
}
generator.AppendLine("default: return base.InvokeCmifMethod(id, context);");
generator.LeaveScope();
generator.LeaveScope();
generator.EnterScope("public override int CmifCommandIdByMethodName(string name)");
generator.EnterScope("return name switch");
foreach (var command in data.CmifCommands)
{
// just return the first command with this name
generator.AppendLine($"\"{command.MethodName}\" => {command.CommandIds[0]},");
}
generator.AppendLine("_ => base.CmifCommandIdByMethodName(name),");
generator.LeaveScope(";");
generator.LeaveScope();
GenerateCommandMethod("Cmif", data.CmifCommands);
GenerateCommandMethod("Tipc", data.TipcCommands);
generator.LeaveScope();
generator.LeaveScope();
ctx.AddSource($"{data.Namespace}.{data.TypeName}.g.cs", generator.ToString());
void GenerateCommandMethod(string commandType, ImmutableArray<CommandData> commands)
{
generator.EnterScope($"protected override RC Invoke{commandType}Method(int id, ServiceCtx context)");
generator.EnterScope("switch (id)");
foreach (var command in commands)
{
generator.AppendLine($"case {string.Join(" or ", command.CommandIds)}:");
generator.IncreaseIndentation();
generator.AppendLine($"LogInvoke(\"{command.MethodName}\");");
generator.AppendLine($"return (RC){command.MethodName}(context);");
generator.DecreaseIndentation();
}
generator.AppendLine($"default: return base.Invoke{commandType}Method(id, context);");
generator.LeaveScope();
generator.LeaveScope();
generator.EnterScope($"public override int {commandType}CommandIdByMethodName(string name)");
generator.EnterScope("return name switch");
foreach (var command in commands)
{
// just return the first command with this name
generator.AppendLine($"\"{command.MethodName}\" => {command.CommandIds[0]},");
}
generator.AppendLine($"_ => base.{commandType}CommandIdByMethodName(name),");
generator.LeaveScope(";");
generator.LeaveScope();
}
});
}
}

View file

@ -49,7 +49,7 @@ namespace Ryujinx.HLE.Exceptions
StringBuilder sb = new();
var commandId = Request.Type > IpcMessageType.TipcCloseSession
? -1 // TODO: tipc name
? Service.TipcCommandIdByMethodName(MethodName)
: Service.CmifCommandIdByMethodName(MethodName);
sb.AppendLine($"Service Command: {Service.GetType().FullName}: {commandId} ({MethodName})");

View file

@ -11,10 +11,8 @@ using System.Reflection;
namespace Ryujinx.HLE.HOS.Services
{
abstract partial class IpcService
abstract class IpcService
{
public IReadOnlyDictionary<int, MethodInfo> TipcCommands { get; }
public ServerBase Server { get; private set; }
private IpcService _parent;
@ -24,25 +22,6 @@ namespace Ryujinx.HLE.HOS.Services
public IpcService(ServerBase server = null)
{
if (true)
{
var sw = Stopwatch.StartNew();
TipcCommands = GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
.SelectMany(methodInfo => methodInfo.GetCustomAttributes<CommandTipcAttribute>()
.Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo);
sw.Stop();
Logger.Debug?.Print(
LogClass.Emulation,
$"{TipcCommands.Count} Tipc commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).",
GetType().AsPrettyString()
);
}
Server = server;
_parent = this;
@ -85,11 +64,31 @@ namespace Ryujinx.HLE.HOS.Services
return ResultCode.Success;
}
public virtual int CmifCommandIdByMethodName(string name) => -1;
protected virtual ResultCode InvokeTipcMethod(int id, ServiceCtx context)
{
if (!context.Device.Configuration.IgnoreMissingServices)
{
string dbgMessage = $"{this.GetType().FullName}: {id}";
throw new ServiceNotImplementedException(this, context, dbgMessage);
}
string serviceName = (this is not DummyService dummyService)
? this.GetType().FullName
: dummyService.ServiceName;
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {id} ignored");
return ResultCode.Success;
}
public virtual int TipcCommandIdByMethodName(string name) => -1;
protected void LogInvoke(string name)
=> Logger.Trace?.Print(LogClass.KernelIpc, $"{this.GetType().Name}: {name}");
public virtual int CmifCommandIdByMethodName(string name) => -1;
public void CallCmifMethod(ServiceCtx context)
{
IpcService service = this;
@ -166,39 +165,13 @@ namespace Ryujinx.HLE.HOS.Services
{
int commandId = (int)context.Request.Type - 0x10;
bool serviceExists = TipcCommands.TryGetValue(commandId, out MethodInfo processRequest);
context.ResponseData.BaseStream.Seek(0x4, SeekOrigin.Begin);
if (context.Device.Configuration.IgnoreMissingServices || serviceExists)
{
ResultCode result = ResultCode.Success;
ResultCode result = InvokeTipcMethod(commandId, context);
context.ResponseData.BaseStream.Seek(0x4, SeekOrigin.Begin);
context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
if (serviceExists)
{
Logger.Debug?.Print(LogClass.KernelIpc, $"{GetType().Name}: {processRequest.Name}");
result = (ResultCode)processRequest.Invoke(this, [context]);
}
else
{
string serviceName;
serviceName = (this is not DummyService dummyService) ? GetType().FullName : dummyService.ServiceName;
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
}
context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
context.ResponseData.Write((uint)result);
}
else
{
string dbgMessage = $"{GetType().FullName}: {commandId}";
throw new ServiceNotImplementedException(this, context, dbgMessage);
}
context.ResponseData.Write((uint)result);
}
protected void MakeObject(ServiceCtx context, IpcService obj)

View file

@ -32,20 +32,20 @@ namespace Ryujinx.HLE.HOS.Services.Nv
"/dev/nvhost-prof-gpu"
];
private static readonly Dictionary<string, Type> _deviceFileRegistry = new()
private static readonly Dictionary<string, Func<ServiceCtx, IVirtualMemoryManager, ulong, NvDeviceFile>> _deviceFileRegistry = new()
{
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
{ "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) },
{ "/dev/nvhost-as-gpu", typeof(NvHostAsGpuDeviceFile) },
{ "/dev/nvhost-gpu", typeof(NvHostGpuDeviceFile) },
//{ "/dev/nvhost-msenc", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-nvdec", typeof(NvHostChannelDeviceFile) },
//{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) },
//{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-dbg-gpu", typeof(NvHostDbgGpuDeviceFile) },
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
{ "/dev/nvmap", (ctx, mem, owner) => new NvMapDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-ctrl", (ctx, mem, owner) => new NvHostCtrlDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-ctrl-gpu", (ctx, mem, owner) => new NvHostCtrlGpuDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-as-gpu", (ctx, mem, owner) => new NvHostAsGpuDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-gpu", (ctx, mem, owner) => new NvHostGpuDeviceFile(ctx, mem, owner) },
//{ "/dev/nvhost-msenc", (ctx, mem, owner) => new NvHostChannelDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-nvdec", (ctx, mem, owner) => new NvHostChannelDeviceFile(ctx, mem, owner) },
//{ "/dev/nvhost-nvjpg", (ctx, mem, owner) => new NvHostChannelDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-vic", (ctx, mem, owner) => new NvHostChannelDeviceFile(ctx, mem, owner) },
//{ "/dev/nvhost-display", (ctx, mem, owner) => new NvHostChannelDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-dbg-gpu", (ctx, mem, owner) => new NvHostDbgGpuDeviceFile(ctx, mem, owner) },
{ "/dev/nvhost-prof-gpu", (ctx, mem, owner) => new NvHostProfGpuDeviceFile(ctx, mem, owner) },
};
private static readonly ArrayPool<byte> _byteArrayPool = ArrayPool<byte>.Create();
@ -78,12 +78,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv
return NvResult.NotSupported;
}
if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass))
if (_deviceFileRegistry.TryGetValue(path, out Func<ServiceCtx, IVirtualMemoryManager, ulong, NvDeviceFile> deviceFileFactory))
{
ConstructorInfo constructor = deviceFileClass.GetConstructor([typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(ulong)
]);
NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke([context, _clientMemory, _owner]);
NvDeviceFile deviceFile = deviceFileFactory(context, _clientMemory, _owner);
deviceFile.Path = path;

View file

@ -28,21 +28,12 @@
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<!--
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
See:
https://github.com/amwx/FluentAvalonia/issues/481
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
-->
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" />
@ -179,8 +170,4 @@
<ItemGroup>
<AdditionalFiles Include="..\..\assets\Locales\*.json" />
</ItemGroup>
<ItemGroup>
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>
</Project>

View file

@ -1,5 +0,0 @@
<linker>
<assembly fullname="Ryujinx.HLE">
<type fullname="Ryujinx.HLE.HOS.Services*" preserve="all"/>
</assembly>
</linker>