diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/DmaPusher.cs')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/DmaPusher.cs | 164 |
1 files changed, 136 insertions, 28 deletions
diff --git a/Ryujinx.Graphics.Gpu/DmaPusher.cs b/Ryujinx.Graphics.Gpu/DmaPusher.cs index 1c85686a..33108c31 100644 --- a/Ryujinx.Graphics.Gpu/DmaPusher.cs +++ b/Ryujinx.Graphics.Gpu/DmaPusher.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Concurrent; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Graphics.Gpu @@ -8,10 +10,61 @@ namespace Ryujinx.Graphics.Gpu /// </summary> public class DmaPusher { - private ConcurrentQueue<ulong> _ibBuffer; + private ConcurrentQueue<CommandBuffer> _commandBufferQueue; - private ulong _dmaPut; - private ulong _dmaGet; + private enum CommandBufferType + { + Prefetch, + NoPrefetch, + } + + private struct CommandBuffer + { + /// <summary> + /// The type of the command buffer. + /// </summary> + public CommandBufferType Type; + + /// <summary> + /// Fetched data. + /// </summary> + public int[] Words; + + /// <summary> + /// The GPFIFO entry address. (used in NoPrefetch mode) + /// </summary> + public ulong EntryAddress; + + /// <summary> + /// The count of entries inside this GPFIFO entry. + /// </summary> + public uint EntryCount; + + /// <summary> + /// Fetch the command buffer. + /// </summary> + public void Fetch(GpuContext context) + { + if (Words == null) + { + Words = MemoryMarshal.Cast<byte, int>(context.MemoryAccessor.GetSpan(EntryAddress, EntryCount * 4)).ToArray(); + } + } + + /// <summary> + /// Read inside the command buffer. + /// </summary> + /// <param name="context">The GPU context</param> + /// <param name="index">The index inside the command buffer</param> + /// <returns>The value read</returns> + public int ReadAt(GpuContext context, int index) + { + return Words[index]; + } + } + + private CommandBuffer _currentCommandBuffer; + private int _wordsPosition; /// <summary> /// Internal GPFIFO state. @@ -32,9 +85,6 @@ namespace Ryujinx.Graphics.Gpu private bool _sliActive; private bool _ibEnable; - private bool _nonMain; - - private ulong _dmaMGet; private GpuContext _context; @@ -48,25 +98,92 @@ namespace Ryujinx.Graphics.Gpu { _context = context; - _ibBuffer = new ConcurrentQueue<ulong>(); - _ibEnable = true; + _commandBufferQueue = new ConcurrentQueue<CommandBuffer>(); + _event = new AutoResetEvent(false); } /// <summary> - /// Pushes a GPFIFO entry. + /// Signal the pusher that there are new entries to process. /// </summary> - /// <param name="entry">GPFIFO entry</param> - public void Push(ulong entry) + public void SignalNewEntries() { - _ibBuffer.Enqueue(entry); - _event.Set(); } /// <summary> + /// Push a GPFIFO entry in the form of a prefetched command buffer. + /// It is intended to be used by nvservices to handle special cases. + /// </summary> + /// <param name="commandBuffer">The command buffer containing the prefetched commands</param> + public void PushHostCommandBuffer(int[] commandBuffer) + { + _commandBufferQueue.Enqueue(new CommandBuffer + { + Type = CommandBufferType.Prefetch, + Words = commandBuffer, + EntryAddress = ulong.MaxValue, + EntryCount = (uint)commandBuffer.Length + }); + } + + /// <summary> + /// Create a CommandBuffer from a GPFIFO entry. + /// </summary> + /// <param name="entry">The GPFIFO entry</param> + /// <returns>A new CommandBuffer based on the GPFIFO entry</returns> + private CommandBuffer CreateCommandBuffer(ulong entry) + { + ulong length = (entry >> 42) & 0x1fffff; + ulong startAddress = entry & 0xfffffffffc; + + bool noPrefetch = (entry & (1UL << 63)) != 0; + + CommandBufferType type = CommandBufferType.Prefetch; + + if (noPrefetch) + { + type = CommandBufferType.NoPrefetch; + } + + return new CommandBuffer + { + Type = type, + Words = null, + EntryAddress = startAddress, + EntryCount = (uint)length + }; + } + + /// <summary> + /// Pushes GPFIFO entries. + /// </summary> + /// <param name="entries">GPFIFO entries</param> + public void PushEntries(ReadOnlySpan<ulong> entries) + { + bool beforeBarrier = true; + + foreach (ulong entry in entries) + { + CommandBuffer commandBuffer = CreateCommandBuffer(entry); + + if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch) + { + commandBuffer.Fetch(_context); + } + + if (commandBuffer.Type == CommandBufferType.NoPrefetch) + { + beforeBarrier = false; + } + + _commandBufferQueue.Enqueue(commandBuffer); + } + } + + /// <summary> /// Waits until commands are pushed to the FIFO. /// </summary> /// <returns>True if commands were received, false if wait timed out</returns> @@ -89,16 +206,9 @@ namespace Ryujinx.Graphics.Gpu /// <returns>True if the FIFO still has commands to be processed, false otherwise</returns> private bool Step() { - if (_dmaGet != _dmaPut) + if (_wordsPosition != _currentCommandBuffer.EntryCount) { - int word = _context.MemoryAccessor.ReadInt32(_dmaGet); - - _dmaGet += 4; - - if (!_nonMain) - { - _dmaMGet = _dmaGet; - } + int word = _currentCommandBuffer.ReadAt(_context, _wordsPosition++); if (_state.LengthPending != 0) { @@ -170,14 +280,12 @@ namespace Ryujinx.Graphics.Gpu } } } - else if (_ibEnable && _ibBuffer.TryDequeue(out ulong entry)) + else if (_ibEnable && _commandBufferQueue.TryDequeue(out CommandBuffer entry)) { - ulong length = (entry >> 42) & 0x1fffff; - - _dmaGet = entry & 0xfffffffffc; - _dmaPut = _dmaGet + length * 4; + _currentCommandBuffer = entry; + _wordsPosition = 0; - _nonMain = (entry & (1UL << 41)) != 0; + _currentCommandBuffer.Fetch(_context); } else { |
