diff --git a/Ryujinx.Graphics.Gpu/ClassId.cs b/Ryujinx.Graphics.Gpu/ClassId.cs index 3cde0ac0..be4ebe4b 100644 --- a/Ryujinx.Graphics.Gpu/ClassId.cs +++ b/Ryujinx.Graphics.Gpu/ClassId.cs @@ -1,5 +1,8 @@ namespace Ryujinx.Graphics.Gpu { + /// + /// GPU engine class ID. + /// enum ClassId { Engine2D = 0x902d, diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/Ryujinx.Graphics.Gpu/Constants.cs index 501933fb..ff5b9f94 100644 --- a/Ryujinx.Graphics.Gpu/Constants.cs +++ b/Ryujinx.Graphics.Gpu/Constants.cs @@ -1,14 +1,48 @@ namespace Ryujinx.Graphics.Gpu { + /// + /// Common Maxwell GPU constants. + /// static class Constants { + /// + /// Maximum number of compute uniform buffers. + /// public const int TotalCpUniformBuffers = 8; + + /// + /// Maximum number of compute storage buffers (this is a API limitation). + /// public const int TotalCpStorageBuffers = 16; + + /// + /// Maximum number of graphics uniform buffers. + /// public const int TotalGpUniformBuffers = 18; + + /// + /// Maximum number of graphics storage buffers (this is a API limitation). + /// public const int TotalGpStorageBuffers = 16; - public const int TotalRenderTargets = 8; - public const int TotalShaderStages = 5; - public const int TotalVertexBuffers = 16; - public const int TotalViewports = 8; + + /// + /// Maximum number of render target color buffers. + /// + public const int TotalRenderTargets = 8; + + /// + /// Number of shader stages. + /// + public const int TotalShaderStages = 5; + + /// + /// Maximum number of vertex buffers. + /// + public const int TotalVertexBuffers = 16; + + /// + /// Maximum number of viewports. + /// + public const int TotalViewports = 8; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/DmaPusher.cs b/Ryujinx.Graphics.Gpu/DmaPusher.cs index ae9cc868..d90bbd73 100644 --- a/Ryujinx.Graphics.Gpu/DmaPusher.cs +++ b/Ryujinx.Graphics.Gpu/DmaPusher.cs @@ -3,6 +3,9 @@ using System.Threading; namespace Ryujinx.Graphics.Gpu { + /// + /// GPU DMA pusher, used to push commands to the GPU. + /// public class DmaPusher { private ConcurrentQueue _ibBuffer; @@ -10,6 +13,9 @@ namespace Ryujinx.Graphics.Gpu private ulong _dmaPut; private ulong _dmaGet; + /// + /// Internal GPFIFO state. + /// private struct DmaState { public int Method; @@ -34,6 +40,10 @@ namespace Ryujinx.Graphics.Gpu private AutoResetEvent _event; + /// + /// Creates a new instance of the GPU DMA pusher. + /// + /// GPU context that the pusher belongs to internal DmaPusher(GpuContext context) { _context = context; @@ -45,6 +55,10 @@ namespace Ryujinx.Graphics.Gpu _event = new AutoResetEvent(false); } + /// + /// Pushes a GPFIFO entry. + /// + /// GPFIFO entry public void Push(ulong entry) { _ibBuffer.Enqueue(entry); @@ -52,16 +66,27 @@ namespace Ryujinx.Graphics.Gpu _event.Set(); } + /// + /// Waits until commands are pushed to the FIFO. + /// + /// True if commands were received, false if wait timed out public bool WaitForCommands() { return _event.WaitOne(8); } + /// + /// Processes commands pushed to the FIFO. + /// public void DispatchCalls() { while (Step()); } + /// + /// Processes a single command on the FIFO. + /// + /// private bool Step() { if (_dmaGet != _dmaPut) @@ -162,6 +187,10 @@ namespace Ryujinx.Graphics.Gpu return true; } + /// + /// Sets current non-immediate method call state. + /// + /// Compressed method word private void SetNonImmediateState(int word) { _state.Method = (word >> 0) & 0x1fff; @@ -169,6 +198,10 @@ namespace Ryujinx.Graphics.Gpu _state.MethodCount = (word >> 16) & 0x1fff; } + /// + /// Forwards the method call to GPU engines. + /// + /// Call argument private void CallMethod(int argument) { _context.Fifo.CallMethod(new MethodParams( diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index bb172c9e..d68dd2c0 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -5,30 +5,68 @@ using System; namespace Ryujinx.Graphics.Gpu { + /// + /// GPU emulation context. + /// public class GpuContext { + /// + /// Host renderer. + /// public IRenderer Renderer { get; } + /// + /// Physical memory access (it actually accesses the process memory, not actual physical memory). + /// internal PhysicalMemory PhysicalMemory { get; private set; } + /// + /// GPU memory manager. + /// public MemoryManager MemoryManager { get; } + /// + /// GPU memory accessor. + /// internal MemoryAccessor MemoryAccessor { get; } + /// + /// GPU engine methods processing. + /// internal Methods Methods { get; } + /// + /// GPU commands FIFO. + /// internal NvGpuFifo Fifo { get; } + /// + /// DMA pusher. + /// public DmaPusher DmaPusher { get; } + /// + /// Presentation window. + /// public Window Window { get; } + /// + /// Internal sequence number, used to avoid needless resource data updates + /// in the middle of a command buffer before synchronizations. + /// internal int SequenceNumber { get; private set; } private readonly Lazy _caps; + /// + /// Host hardware capabilities. + /// internal Capabilities Capabilities => _caps.Value; + /// + /// Creates a new instance of the GPU emulation context. + /// + /// Host renderer public GpuContext(IRenderer renderer) { Renderer = renderer; @@ -48,11 +86,20 @@ namespace Ryujinx.Graphics.Gpu _caps = new Lazy(Renderer.GetCapabilities); } + /// + /// Advances internal sequence number. + /// This forces the update of any modified GPU resource. + /// internal void AdvanceSequence() { SequenceNumber++; } + /// + /// Sets the process memory manager, after the application process is initialized. + /// This is required for any GPU memory access. + /// + /// CPU memory manager public void SetVmm(ARMeilleure.Memory.MemoryManager cpuMemory) { PhysicalMemory = new PhysicalMemory(cpuMemory); diff --git a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs index c5eaa0b3..468d3a34 100644 --- a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs +++ b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs @@ -1,12 +1,21 @@ namespace Ryujinx.Graphics.Gpu { + /// + /// General GPU and graphics configuration. + /// public static class GraphicsConfig { + /// + /// Base directory used to write shader code dumps. + /// Set to null to disable code dumping. + /// public static string ShadersDumpPath; + /// + /// Fast GPU time calculates the internal GPU time ticks as if the GPU was capable of + /// processing commands almost instantly, instead of using the host timer. + /// This can avoid lower resolution on some games when GPU performance is poor. + /// public static bool FastGpuTime = true; - - public static bool DisableTUpdate; - public static bool DisableBUpdate; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs index e424b8ff..cfc3815b 100644 --- a/Ryujinx.Graphics.Gpu/MacroInterpreter.cs +++ b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu { + /// + /// Macro code interpreter. + /// class MacroInterpreter { private enum AssignmentOperation @@ -42,10 +45,6 @@ namespace Ryujinx.Graphics.Gpu BitwiseNotAnd = 12 } - private GpuContext _context; - - private NvGpuFifo _pFifo; - public Queue Fifo { get; private set; } private int[] _gprs; @@ -61,16 +60,23 @@ namespace Ryujinx.Graphics.Gpu private int _pc; - public MacroInterpreter(GpuContext context, NvGpuFifo pFifo) + /// + /// Creates a new instance of the macro code interpreter. + /// + public MacroInterpreter() { - _context = context; - _pFifo = pFifo; - Fifo = new Queue(); _gprs = new int[8]; } + /// + /// Executes a macro program until it exits. + /// + /// Code of the program to execute + /// Start position to execute + /// Optional argument passed to the program, 0 if not used + /// Current GPU state public void Execute(int[] mme, int position, int param, GpuState state) { Reset(); @@ -88,6 +94,10 @@ namespace Ryujinx.Graphics.Gpu Step(mme, state); } + /// + /// Resets the internal interpreter state. + /// Call each time you run a new program. + /// private void Reset() { for (int index = 0; index < _gprs.Length; index++) @@ -101,6 +111,12 @@ namespace Ryujinx.Graphics.Gpu _carry = false; } + /// + /// Executes a single instruction of the program. + /// + /// Program code to execute + /// Current GPU state + /// True to continue execution, false if the program exited private bool Step(int[] mme, GpuState state) { int baseAddr = _pc - 1; @@ -226,12 +242,21 @@ namespace Ryujinx.Graphics.Gpu return !exit; } + /// + /// Fetches a single operation code from the program code. + /// + /// Program code private void FetchOpCode(int[] mme) { _opCode = _pipeOp; _pipeOp = mme[_pc++]; } + /// + /// Gets the result of the current Arithmetic and Logic unit operation. + /// + /// Current GPU state + /// Operation result private int GetAluResult(GpuState state) { AluOperation op = (AluOperation)(_opCode & 7); @@ -303,6 +328,13 @@ namespace Ryujinx.Graphics.Gpu throw new ArgumentException(nameof(_opCode)); } + /// + /// Gets the result of a Arithmetic and Logic operation using registers. + /// + /// Arithmetic and Logic unit operation with registers + /// First operand value + /// Second operand value + /// Operation result private int GetAluResult(AluRegOperation aluOp, int a, int b) { switch (aluOp) @@ -353,43 +385,70 @@ namespace Ryujinx.Graphics.Gpu throw new ArgumentOutOfRangeException(nameof(aluOp)); } + /// + /// Extracts a 32-bits signed integer constant from the current operation code. + /// + /// private int GetImm() { // Note: The immediate is signed, the sign-extension is intended here. return _opCode >> 14; } + /// + /// Sets the current method address, for method calls. + /// + /// Packed address and increment value private void SetMethAddr(int value) { _methAddr = (value >> 0) & 0xfff; _methIncr = (value >> 12) & 0x3f; } + /// + /// Sets the destination register value. + /// + /// Value to set (usually the operation result) private void SetDstGpr(int value) { _gprs[(_opCode >> 8) & 7] = value; } + /// + /// Gets first operand value from the respective register. + /// + /// Operand value private int GetGprA() { return GetGprValue((_opCode >> 11) & 7); } + /// + /// Gets second operand value from the respective register. + /// + /// Operand value private int GetGprB() { return GetGprValue((_opCode >> 14) & 7); } + /// + /// Gets the value from a register, or 0 if the R0 register is specified. + /// + /// Index of the register + /// Register value private int GetGprValue(int index) { return index != 0 ? _gprs[index] : 0; } + /// + /// Fetches a call argument from the call argument FIFO. + /// + /// The call argument, or 0 if the FIFO is empty private int FetchParam() { - int value; - - if (!Fifo.TryDequeue(out value)) + if (!Fifo.TryDequeue(out int value)) { Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument."); @@ -399,11 +458,22 @@ namespace Ryujinx.Graphics.Gpu return value; } + /// + /// Reads data from a GPU register. + /// + /// Current GPU state + /// Register offset to read + /// GPU register value private int Read(GpuState state, int reg) { return state.Read(reg); } + /// + /// Performs a GPU method call. + /// + /// Current GPU state + /// Call argument private void Send(GpuState state, int value) { MethodParams meth = new MethodParams(_methAddr, value); diff --git a/Ryujinx.Graphics.Gpu/MethodParams.cs b/Ryujinx.Graphics.Gpu/MethodParams.cs index fea4eb30..dd60f77c 100644 --- a/Ryujinx.Graphics.Gpu/MethodParams.cs +++ b/Ryujinx.Graphics.Gpu/MethodParams.cs @@ -1,14 +1,42 @@ namespace Ryujinx.Graphics { + /// + /// Method call parameters. + /// struct MethodParams { - public int Method { get; private set; } - public int Argument { get; private set; } - public int SubChannel { get; private set; } - public int MethodCount { get; private set; } + /// + /// Method offset. + /// + public int Method { get; } + /// + /// Method call argument. + /// + public int Argument { get; } + + /// + /// Sub-channel where the call should be sent. + /// + public int SubChannel { get; } + + /// + /// For multiple calls to the same method, this is the remaining calls count. + /// + public int MethodCount { get; } + + /// + /// Indicates if the current call is the last one from a batch of calls to the same method. + /// public bool IsLastCall => MethodCount <= 1; + /// + /// Constructs the method call parameters structure. + /// + /// Method offset + /// Method call argument + /// Optional sub-channel where the method should be sent (not required for macro calls) + /// Optional remaining calls count (not required for macro calls) public MethodParams( int method, int argument, diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs index 6e02f391..6f51527f 100644 --- a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs +++ b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs @@ -2,6 +2,9 @@ using Ryujinx.Graphics.Gpu.State; namespace Ryujinx.Graphics.Gpu { + /// + /// GPU commands FIFO. + /// class NvGpuFifo { private const int MacrosCount = 0x80; @@ -13,25 +16,36 @@ namespace Ryujinx.Graphics.Gpu private GpuContext _context; + /// + /// Cached GPU macro program. + /// private struct CachedMacro { - public int Position { get; private set; } + public int Position { get; } private bool _executionPending; private int _argument; private MacroInterpreter _interpreter; - public CachedMacro(GpuContext context, NvGpuFifo fifo, int position) + /// + /// Creates a new instance of the GPU cached macro program. + /// + /// Macro code start position + public CachedMacro(int position) { Position = position; _executionPending = false; _argument = 0; - _interpreter = new MacroInterpreter(context, fifo); + _interpreter = new MacroInterpreter(); } + /// + /// Sets the first argument for the macro call. + /// + /// First argument public void StartExecution(int argument) { _argument = argument; @@ -39,6 +53,11 @@ namespace Ryujinx.Graphics.Gpu _executionPending = true; } + /// + /// Starts executing the macro program code. + /// + /// Program code + /// Current GPU state public void Execute(int[] mme, GpuState state) { if (_executionPending) @@ -49,6 +68,10 @@ namespace Ryujinx.Graphics.Gpu } } + /// + /// Pushes a argument to the macro call argument FIFO. + /// + /// Argument to be pushed public void PushArgument(int argument) { _interpreter?.Fifo.Enqueue(argument); @@ -62,11 +85,24 @@ namespace Ryujinx.Graphics.Gpu private int[] _mme; + /// + /// GPU sub-channel information. + /// private class SubChannel { + /// + /// Sub-channel GPU state. + /// public GpuState State { get; } + + /// + /// Engine bound to the sub-channel. + /// public ClassId Class { get; set; } + /// + /// Creates a new instance of the GPU sub-channel. + /// public SubChannel() { State = new GpuState(); @@ -75,6 +111,10 @@ namespace Ryujinx.Graphics.Gpu private SubChannel[] _subChannels; + /// + /// Creates a new instance of the GPU commands FIFO. + /// + /// GPU emulation context public NvGpuFifo(GpuContext context) { _context = context; @@ -93,6 +133,10 @@ namespace Ryujinx.Graphics.Gpu } } + /// + /// Calls a GPU method. + /// + /// GPU method call parameters public void CallMethod(MethodParams meth) { if ((NvGpuFifoMeth)meth.Method == NvGpuFifoMeth.BindChannel) @@ -137,7 +181,7 @@ namespace Ryujinx.Graphics.Gpu { int position = meth.Argument; - _macros[_currMacroBindIndex++] = new CachedMacro(_context, this, position); + _macros[_currMacroBindIndex++] = new CachedMacro(position); break; } diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs b/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs index 09aaa6c3..89023407 100644 --- a/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs +++ b/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs @@ -1,5 +1,8 @@ namespace Ryujinx.Graphics.Gpu { + /// + /// GPU commands FIFO processor commands. + /// enum NvGpuFifoMeth { BindChannel = 0, diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs index d5494aa4..29c36248 100644 --- a/Ryujinx.Graphics.Gpu/Window.cs +++ b/Ryujinx.Graphics.Gpu/Window.cs @@ -7,17 +7,45 @@ namespace Ryujinx.Graphics.Gpu { using Texture = Image.Texture; + /// + /// GPU image presentation window. + /// public class Window { private readonly GpuContext _context; + /// + /// Texture presented on the window. + /// private struct PresentationTexture { - public TextureInfo Info { get; } - public ImageCrop Crop { get; } - public Action Callback { get; } - public object UserObj { get; } + /// + /// Texture information. + /// + public TextureInfo Info { get; } + /// + /// Texture crop region. + /// + public ImageCrop Crop { get; } + + /// + /// Texture release callback. + /// + public Action Callback { get; } + + /// + /// User defined object, passed to the release callback. + /// + public object UserObj { get; } + + /// + /// Creates a new instance of the presentation texture. + /// + /// Information of the texture to be presented + /// Texture crop region + /// Texture release callback + /// User defined object passed to the release callback, can be used to identify the texture public PresentationTexture( TextureInfo info, ImageCrop crop, @@ -33,6 +61,10 @@ namespace Ryujinx.Graphics.Gpu private readonly ConcurrentQueue _frameQueue; + /// + /// Creates a new instance of the GPU presentation window. + /// + /// GPU emulation context public Window(GpuContext context) { _context = context; @@ -40,6 +72,23 @@ namespace Ryujinx.Graphics.Gpu _frameQueue = new ConcurrentQueue(); } + /// + /// Enqueues a frame for presentation. + /// This method is thread safe and can be called from any thread. + /// When the texture is presented and not needed anymore, the release callback is called. + /// It's an error to modify the texture after calling this method, before the release callback is called. + /// + /// CPU virtual address of the texture data + /// Texture width + /// Texture height + /// Texture stride for linear texture, should be zero otherwise + /// Indicates if the texture is linear, normally false + /// GOB blocks in the Y direction, for block linear textures + /// Texture format + /// Texture format bytes per pixel (must match the format) + /// Texture crop region + /// Texture release callback + /// User defined object passed to the release callback public void EnqueueFrameThreadSafe( ulong address, int width, @@ -74,6 +123,11 @@ namespace Ryujinx.Graphics.Gpu _frameQueue.Enqueue(new PresentationTexture(info, crop, callback, userObj)); } + /// + /// Presents a texture on the queue. + /// If the queue is empty, then no texture is presented. + /// + /// Callback method to call when a new texture should be presented on the screen public void Present(Action swapBuffersCallback) { _context.AdvanceSequence();