aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/DmaPusher.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/DmaPusher.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/DmaPusher.cs164
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
{