mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-12-30 15:21:21 -08:00
348 lines
13 KiB
C#
348 lines
13 KiB
C#
|
using ARMeilleure.Memory;
|
|||
|
using Ryujinx.Common.Logging;
|
|||
|
using Ryujinx.Graphics;
|
|||
|
using Ryujinx.Graphics.Memory;
|
|||
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
|
|||
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
|
|||
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
|||
|
using System;
|
|||
|
using System.Runtime.CompilerServices;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
|
|||
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||
|
{
|
|||
|
class NvHostChannelDeviceFile : NvDeviceFile
|
|||
|
{
|
|||
|
private uint _timeout;
|
|||
|
private uint _submitTimeout;
|
|||
|
private uint _timeslice;
|
|||
|
private NvGpu _gpu;
|
|||
|
private MemoryManager _memory;
|
|||
|
|
|||
|
public NvHostChannelDeviceFile(ServiceCtx context) : base(context)
|
|||
|
{
|
|||
|
_gpu = context.Device.Gpu;
|
|||
|
_memory = context.Memory;
|
|||
|
_timeout = 3000;
|
|||
|
_submitTimeout = 0;
|
|||
|
_timeslice = 0;
|
|||
|
}
|
|||
|
|
|||
|
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
|
|||
|
{
|
|||
|
NvInternalResult result = NvInternalResult.NotImplemented;
|
|||
|
|
|||
|
if (command.Type == NvIoctl.NvHostCustomMagic)
|
|||
|
{
|
|||
|
switch (command.Number)
|
|||
|
{
|
|||
|
case 0x01:
|
|||
|
result = Submit(arguments);
|
|||
|
break;
|
|||
|
case 0x02:
|
|||
|
result = CallIoctlMethod<GetParameterArguments>(GetSyncpoint, arguments);
|
|||
|
break;
|
|||
|
case 0x03:
|
|||
|
result = CallIoctlMethod<GetParameterArguments>(GetWaitBase, arguments);
|
|||
|
break;
|
|||
|
case 0x07:
|
|||
|
result = CallIoctlMethod<uint>(SetSubmitTimeout, arguments);
|
|||
|
break;
|
|||
|
case 0x09:
|
|||
|
result = MapCommandBuffer(arguments);
|
|||
|
break;
|
|||
|
case 0x0a:
|
|||
|
result = UnmapCommandBuffer(arguments);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (command.Type == NvIoctl.NvHostMagic)
|
|||
|
{
|
|||
|
switch (command.Number)
|
|||
|
{
|
|||
|
case 0x01:
|
|||
|
result = CallIoctlMethod<int>(SetNvMapFd, arguments);
|
|||
|
break;
|
|||
|
case 0x03:
|
|||
|
result = CallIoctlMethod<uint>(SetTimeout, arguments);
|
|||
|
break;
|
|||
|
case 0x08:
|
|||
|
result = SubmitGpfifo(arguments);
|
|||
|
break;
|
|||
|
case 0x09:
|
|||
|
result = CallIoctlMethod<AllocObjCtxArguments>(AllocObjCtx, arguments);
|
|||
|
break;
|
|||
|
case 0x0b:
|
|||
|
result = CallIoctlMethod<ZcullBindArguments>(ZcullBind, arguments);
|
|||
|
break;
|
|||
|
case 0x0c:
|
|||
|
result = CallIoctlMethod<SetErrorNotifierArguments>(SetErrorNotifier, arguments);
|
|||
|
break;
|
|||
|
case 0x0d:
|
|||
|
result = CallIoctlMethod<NvChannelPriority>(SetPriority, arguments);
|
|||
|
break;
|
|||
|
case 0x18:
|
|||
|
result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx, arguments);
|
|||
|
break;
|
|||
|
case 0x1a:
|
|||
|
result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx2, arguments);
|
|||
|
break;
|
|||
|
case 0x1d:
|
|||
|
result = CallIoctlMethod<uint>(SetTimeslice, arguments);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (command.Type == NvIoctl.NvGpuMagic)
|
|||
|
{
|
|||
|
switch (command.Number)
|
|||
|
{
|
|||
|
case 0x14:
|
|||
|
result = CallIoctlMethod<ulong>(SetUserData, arguments);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult Submit(Span<byte> arguments)
|
|||
|
{
|
|||
|
int headerSize = Unsafe.SizeOf<SubmitArguments>();
|
|||
|
SubmitArguments submitHeader = MemoryMarshal.Cast<byte, SubmitArguments>(arguments)[0];
|
|||
|
Span<CommandBuffer> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBuffer>(arguments.Slice(headerSize)).Slice(0, submitHeader.CmdBufsCount);
|
|||
|
NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm;
|
|||
|
|
|||
|
foreach (CommandBuffer commandBufferEntry in commandBufferEntries)
|
|||
|
{
|
|||
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MemoryId);
|
|||
|
|
|||
|
int[] commandBufferData = new int[commandBufferEntry.WordsCount];
|
|||
|
|
|||
|
for (int offset = 0; offset < commandBufferData.Length; offset++)
|
|||
|
{
|
|||
|
commandBufferData[offset] = _memory.ReadInt32(map.Address + commandBufferEntry.Offset + offset * 4);
|
|||
|
}
|
|||
|
|
|||
|
_gpu.PushCommandBuffer(vmm, commandBufferData);
|
|||
|
}
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult GetSyncpoint(ref GetParameterArguments arguments)
|
|||
|
{
|
|||
|
arguments.Value = 0;
|
|||
|
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult GetWaitBase(ref GetParameterArguments arguments)
|
|||
|
{
|
|||
|
arguments.Value = 0;
|
|||
|
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetSubmitTimeout(ref uint submitTimeout)
|
|||
|
{
|
|||
|
_submitTimeout = submitTimeout;
|
|||
|
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult MapCommandBuffer(Span<byte> arguments)
|
|||
|
{
|
|||
|
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
|
|||
|
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
|
|||
|
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
|
|||
|
NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm;
|
|||
|
|
|||
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
|||
|
{
|
|||
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
|
|||
|
|
|||
|
if (map == null)
|
|||
|
{
|
|||
|
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
|
|||
|
|
|||
|
return NvInternalResult.InvalidInput;
|
|||
|
}
|
|||
|
|
|||
|
lock (map)
|
|||
|
{
|
|||
|
if (map.DmaMapAddress == 0)
|
|||
|
{
|
|||
|
map.DmaMapAddress = vmm.MapLow(map.Address, map.Size);
|
|||
|
}
|
|||
|
|
|||
|
commandBufferEntry.MapAddress = (int)map.DmaMapAddress;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult UnmapCommandBuffer(Span<byte> arguments)
|
|||
|
{
|
|||
|
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
|
|||
|
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
|
|||
|
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
|
|||
|
NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm;
|
|||
|
|
|||
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
|||
|
{
|
|||
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
|
|||
|
|
|||
|
if (map == null)
|
|||
|
{
|
|||
|
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
|
|||
|
|
|||
|
return NvInternalResult.InvalidInput;
|
|||
|
}
|
|||
|
|
|||
|
lock (map)
|
|||
|
{
|
|||
|
if (map.DmaMapAddress != 0)
|
|||
|
{
|
|||
|
vmm.Free(map.DmaMapAddress, map.Size);
|
|||
|
|
|||
|
map.DmaMapAddress = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetNvMapFd(ref int nvMapFd)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetTimeout(ref uint timeout)
|
|||
|
{
|
|||
|
_timeout = timeout;
|
|||
|
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SubmitGpfifo(Span<byte> arguments)
|
|||
|
{
|
|||
|
int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>();
|
|||
|
SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast<byte, SubmitGpfifoArguments>(arguments)[0];
|
|||
|
Span<long> gpfifoEntries = MemoryMarshal.Cast<byte, long>(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries);
|
|||
|
|
|||
|
return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries);
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult AllocObjCtx(ref AllocObjCtxArguments arguments)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult ZcullBind(ref ZcullBindArguments arguments)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetErrorNotifier(ref SetErrorNotifierArguments arguments)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetPriority(ref NvChannelPriority priority)
|
|||
|
{
|
|||
|
switch (priority)
|
|||
|
{
|
|||
|
case NvChannelPriority.Low:
|
|||
|
_timeslice = 1300; // Timeslice low priority in micro-seconds
|
|||
|
break;
|
|||
|
case NvChannelPriority.Medium:
|
|||
|
_timeslice = 2600; // Timeslice medium priority in micro-seconds
|
|||
|
break;
|
|||
|
case NvChannelPriority.High:
|
|||
|
_timeslice = 5200; // Timeslice high priority in micro-seconds
|
|||
|
break;
|
|||
|
default:
|
|||
|
return NvInternalResult.InvalidInput;
|
|||
|
}
|
|||
|
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
// TODO: disable and preempt channel when GPU scheduler will be implemented.
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult AllocGpfifoEx(ref AllocGpfifoExArguments arguments)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult AllocGpfifoEx2(ref AllocGpfifoExArguments arguments)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetTimeslice(ref uint timeslice)
|
|||
|
{
|
|||
|
if (timeslice < 1000 || timeslice > 50000)
|
|||
|
{
|
|||
|
return NvInternalResult.InvalidInput;
|
|||
|
}
|
|||
|
|
|||
|
_timeslice = timeslice; // in micro-seconds
|
|||
|
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
// TODO: disable and preempt channel when GPU scheduler will be implemented.
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
private NvInternalResult SetUserData(ref ulong userData)
|
|||
|
{
|
|||
|
Logger.PrintStub(LogClass.ServiceNv);
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span<long> entries)
|
|||
|
{
|
|||
|
NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm;
|
|||
|
|
|||
|
foreach (long entry in entries)
|
|||
|
{
|
|||
|
_gpu.Pusher.Push(vmm, entry);
|
|||
|
}
|
|||
|
|
|||
|
header.Fence.Id = 0;
|
|||
|
header.Fence.Value = 0;
|
|||
|
|
|||
|
return NvInternalResult.Success;
|
|||
|
}
|
|||
|
|
|||
|
public override void Close() { }
|
|||
|
}
|
|||
|
}
|