 8e1adb95cf
			
		
	
	
		8e1adb95cf
		
			
		
	
	
	
	
		
			
			* Add support for HLE macros and accelerate MultiDrawElementsIndirectCount * Add missing barrier * Fix index buffer count * Add support check for each macro hle before use * Add missing xml doc Co-authored-by: gdkchan <gab.dark.100@gmail.com>
		
			
				
	
	
		
			243 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Ryujinx.Graphics.Gpu.Memory;
 | |
| using System;
 | |
| using System.Collections.Concurrent;
 | |
| using System.Runtime.CompilerServices;
 | |
| using System.Runtime.InteropServices;
 | |
| using System.Threading;
 | |
| 
 | |
| namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Represents a GPU General Purpose FIFO device.
 | |
|     /// </summary>
 | |
|     public sealed class GPFifoDevice : IDisposable
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Indicates if the command buffer has pre-fetch enabled.
 | |
|         /// </summary>
 | |
|         private enum CommandBufferType
 | |
|         {
 | |
|             Prefetch,
 | |
|             NoPrefetch
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Command buffer data.
 | |
|         /// </summary>
 | |
|         private struct CommandBuffer
 | |
|         {
 | |
|             /// <summary>
 | |
|             /// Processor used to process the command buffer. Contains channel state.
 | |
|             /// </summary>
 | |
|             public GPFifoProcessor Processor;
 | |
| 
 | |
|             /// <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 <see cref="CommandBufferType.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>
 | |
|             /// <param name="flush">If true, flushes potential GPU written data before reading the command buffer</param>
 | |
|             public void Fetch(MemoryManager memoryManager, bool flush = true)
 | |
|             {
 | |
|                 if (Words == null)
 | |
|                 {
 | |
|                     Words = MemoryMarshal.Cast<byte, int>(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, flush)).ToArray();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private readonly ConcurrentQueue<CommandBuffer> _commandBufferQueue;
 | |
| 
 | |
|         private CommandBuffer _currentCommandBuffer;
 | |
|         private GPFifoProcessor _prevChannelProcessor;
 | |
| 
 | |
|         private readonly bool _ibEnable;
 | |
|         private readonly GpuContext _context;
 | |
|         private readonly AutoResetEvent _event;
 | |
| 
 | |
|         private bool _interrupt;
 | |
|         private int _flushSkips;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a new instance of the GPU General Purpose FIFO device.
 | |
|         /// </summary>
 | |
|         /// <param name="context">GPU context that the GPFIFO belongs to</param>
 | |
|         internal GPFifoDevice(GpuContext context)
 | |
|         {
 | |
|             _commandBufferQueue = new ConcurrentQueue<CommandBuffer>();
 | |
|             _ibEnable = true;
 | |
|             _context = context;
 | |
|             _event = new AutoResetEvent(false);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Signal the FIFO that there are new entries to process.
 | |
|         /// </summary>
 | |
|         public void SignalNewEntries()
 | |
|         {
 | |
|             _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="processor">Processor used to process <paramref name="commandBuffer"/></param>
 | |
|         /// <param name="commandBuffer">The command buffer containing the prefetched commands</param>
 | |
|         internal void PushHostCommandBuffer(GPFifoProcessor processor, int[] commandBuffer)
 | |
|         {
 | |
|             _commandBufferQueue.Enqueue(new CommandBuffer
 | |
|             {
 | |
|                 Processor = processor,
 | |
|                 Type = CommandBufferType.Prefetch,
 | |
|                 Words = commandBuffer,
 | |
|                 EntryAddress = ulong.MaxValue,
 | |
|                 EntryCount = (uint)commandBuffer.Length
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Create a CommandBuffer from a GPFIFO entry.
 | |
|         /// </summary>
 | |
|         /// <param name="processor">Processor used to process the command buffer pointed to by <paramref name="entry"/></param>
 | |
|         /// <param name="entry">The GPFIFO entry</param>
 | |
|         /// <returns>A new CommandBuffer based on the GPFIFO entry</returns>
 | |
|         private static CommandBuffer CreateCommandBuffer(GPFifoProcessor processor, GPEntry entry)
 | |
|         {
 | |
|             CommandBufferType type = CommandBufferType.Prefetch;
 | |
| 
 | |
|             if (entry.Entry1Sync == Entry1Sync.Wait)
 | |
|             {
 | |
|                 type = CommandBufferType.NoPrefetch;
 | |
|             }
 | |
| 
 | |
|             ulong startAddress = ((ulong)entry.Entry0Get << 2) | ((ulong)entry.Entry1GetHi << 32);
 | |
| 
 | |
|             return new CommandBuffer
 | |
|             {
 | |
|                 Processor = processor,
 | |
|                 Type = type,
 | |
|                 Words = null,
 | |
|                 EntryAddress = startAddress,
 | |
|                 EntryCount = (uint)entry.Entry1Length
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Pushes GPFIFO entries.
 | |
|         /// </summary>
 | |
|         /// <param name="processor">Processor used to process the command buffers pointed to by <paramref name="entries"/></param>
 | |
|         /// <param name="entries">GPFIFO entries</param>
 | |
|         internal void PushEntries(GPFifoProcessor processor, ReadOnlySpan<ulong> entries)
 | |
|         {
 | |
|             bool beforeBarrier = true;
 | |
| 
 | |
|             for (int index = 0; index < entries.Length; index++)
 | |
|             {
 | |
|                 ulong entry = entries[index];
 | |
| 
 | |
|                 CommandBuffer commandBuffer = CreateCommandBuffer(processor, Unsafe.As<ulong, GPEntry>(ref entry));
 | |
| 
 | |
|                 if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
 | |
|                 {
 | |
|                     commandBuffer.Fetch(processor.MemoryManager);
 | |
|                 }
 | |
| 
 | |
|                 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>
 | |
|         public bool WaitForCommands()
 | |
|         {
 | |
|             return !_commandBufferQueue.IsEmpty || (_event.WaitOne(8) && !_commandBufferQueue.IsEmpty);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Processes commands pushed to the FIFO.
 | |
|         /// </summary>
 | |
|         public void DispatchCalls()
 | |
|         {
 | |
|             // Use this opportunity to also dispose any pending channels that were closed.
 | |
|             _context.RunDeferredActions();
 | |
| 
 | |
|             // Process command buffers.
 | |
|             while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
 | |
|             {
 | |
|                 bool flushCommandBuffer = true;
 | |
| 
 | |
|                 if (_flushSkips != 0)
 | |
|                 {
 | |
|                     _flushSkips--;
 | |
|                     flushCommandBuffer = false;
 | |
|                 }
 | |
| 
 | |
|                 _currentCommandBuffer = entry;
 | |
|                 _currentCommandBuffer.Fetch(entry.Processor.MemoryManager, flushCommandBuffer);
 | |
| 
 | |
|                 // If we are changing the current channel,
 | |
|                 // we need to force all the host state to be updated.
 | |
|                 if (_prevChannelProcessor != entry.Processor)
 | |
|                 {
 | |
|                     _prevChannelProcessor = entry.Processor;
 | |
|                     entry.Processor.ForceAllDirty();
 | |
|                 }
 | |
| 
 | |
|                 entry.Processor.Process(entry.EntryAddress, _currentCommandBuffer.Words);
 | |
|             }
 | |
| 
 | |
|             _interrupt = false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets the number of flushes that should be skipped for subsequent command buffers.
 | |
|         /// </summary>
 | |
|         /// <remarks>
 | |
|         /// This can improve performance when command buffer data only needs to be consumed by the GPU.
 | |
|         /// </remarks>
 | |
|         /// <param name="count">The amount of flushes that should be skipped</param>
 | |
|         internal void SetFlushSkips(int count)
 | |
|         {
 | |
|             _flushSkips = count;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Interrupts command processing. This will break out of the DispatchCalls loop.
 | |
|         /// </summary>
 | |
|         public void Interrupt()
 | |
|         {
 | |
|             _interrupt = true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Disposes of resources used for GPFifo command processing.
 | |
|         /// </summary>
 | |
|         public void Dispose() => _event.Dispose();
 | |
|     }
 | |
| }
 |