From 8b44eb1c981d7106be37107755c7c71c3c3c0ce4 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 7 Jul 2021 20:56:06 -0300 Subject: Separate GPU engines and make state follow official docs (part 1/2) (#2422) * Use DeviceState for compute and i2m * Migrate 2D class, more comments * Migrate DMA copy engine * Remove now unused code * Replace GpuState by GpuAccessorState on GpuAcessor, since compute no longer has a GpuState * More comments * Add logging (disabled) * Add back i2m on 3D engine --- .../Engine/InlineToMemory/InlineToMemoryClass.cs | 211 +++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs (limited to 'Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs') diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs new file mode 100644 index 00000000..0e7d6fb0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -0,0 +1,211 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.Device; +using Ryujinx.Graphics.Texture; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory +{ + /// + /// Represents a Inline-to-Memory engine class. + /// + class InlineToMemoryClass : IDeviceState + { + private readonly GpuContext _context; + private readonly GpuChannel _channel; + private readonly DeviceState _state; + + private bool _isLinear; + + private int _offset; + private int _size; + + private ulong _dstGpuVa; + private int _dstX; + private int _dstY; + private int _dstWidth; + private int _dstHeight; + private int _dstStride; + private int _dstGobBlocksInY; + private int _lineLengthIn; + private int _lineCount; + + private bool _finished; + + private int[] _buffer; + + /// + /// Creates a new instance of the Inline-to-Memory engine class. + /// + /// GPU context + /// GPU channel + /// Indicates if the internal state should be initialized. Set to false if part of another engine + protected InlineToMemoryClass(GpuContext context, GpuChannel channel, bool initializeState) + { + _context = context; + _channel = channel; + + if (initializeState) + { + _state = new DeviceState(new Dictionary + { + { nameof(InlineToMemoryClassState.LaunchDma), new RwCallback(LaunchDma, null) }, + { nameof(InlineToMemoryClassState.LoadInlineData), new RwCallback(LoadInlineData, null) } + }); + } + } + + /// + /// Creates a new instance of the inline-to-memory engine class. + /// + /// GPU context + /// GPU channel + public InlineToMemoryClass(GpuContext context, GpuChannel channel) : this(context, channel, true) + { + } + + /// + /// Reads data from the class registers. + /// + /// Register byte offset + /// Data at the specified offset + public virtual int Read(int offset) => _state.Read(offset); + + /// + /// Writes data to the class registers. + /// + /// Register byte offset + /// Data to be written + public virtual void Write(int offset, int data) => _state.Write(offset, data); + + /// + /// Launches Inline-to-Memory engine DMA copy. + /// + /// Method call argument + protected virtual void LaunchDma(int argument) + { + LaunchDma(ref _state.State, argument); + } + + /// + /// Launches Inline-to-Memory engine DMA copy. + /// + /// Current class state + /// Method call argument + protected void LaunchDma(ref InlineToMemoryClassState state, int argument) + { + _isLinear = (argument & 1) != 0; + + _offset = 0; + _size = (int)(state.LineLengthIn * state.LineCount); + + int count = BitUtils.DivRoundUp(_size, 4); + + if (_buffer == null || _buffer.Length < count) + { + _buffer = new int[count]; + } + + ulong dstGpuVa = ((ulong)state.OffsetOutUpperValue << 32) | state.OffsetOut; + + ulong dstBaseAddress = _channel.MemoryManager.Translate(dstGpuVa); + + // Trigger read tracking, to flush any managed resources in the destination region. + _channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true); + + _dstGpuVa = dstGpuVa; + _dstX = state.SetDstOriginBytesXV; + _dstY = state.SetDstOriginSamplesYV; + _dstWidth = (int)state.SetDstWidth; + _dstHeight = (int)state.SetDstHeight; + _dstStride = (int)state.PitchOut; + _dstGobBlocksInY = 1 << (int)state.SetDstBlockSizeHeight; + _lineLengthIn = (int)state.LineLengthIn; + _lineCount = (int)state.LineCount; + + _finished = false; + } + + /// + /// Pushes a word of data to the Inline-to-Memory engine. + /// + /// Method call argument + protected void LoadInlineData(int argument) + { + if (!_finished) + { + _buffer[_offset++] = argument; + + if (_offset * 4 >= _size) + { + FinishTransfer(); + } + } + } + + /// + /// Performs actual copy of the inline data after the transfer is finished. + /// + private void FinishTransfer() + { + Span data = MemoryMarshal.Cast(_buffer).Slice(0, _size); + + if (_isLinear && _lineCount == 1) + { + ulong address = _channel.MemoryManager.Translate(_dstGpuVa); + + _channel.MemoryManager.Physical.Write(address, data); + } + else + { + var dstCalculator = new OffsetCalculator( + _dstWidth, + _dstHeight, + _dstStride, + _isLinear, + _dstGobBlocksInY, + 1); + + int srcOffset = 0; + + ulong dstBaseAddress = _channel.MemoryManager.Translate(_dstGpuVa); + + for (int y = _dstY; y < _dstY + _lineCount; y++) + { + int x1 = _dstX; + int x2 = _dstX + _lineLengthIn; + int x2Trunc = _dstX + BitUtils.AlignDown(_lineLengthIn, 16); + + int x; + + for (x = x1; x < x2Trunc; x += 16, srcOffset += 16) + { + int dstOffset = dstCalculator.GetOffset(x, y); + + ulong dstAddress = dstBaseAddress + (ulong)dstOffset; + + Span pixel = data.Slice(srcOffset, 16); + + _channel.MemoryManager.Physical.Write(dstAddress, pixel); + } + + for (; x < x2; x++, srcOffset++) + { + int dstOffset = dstCalculator.GetOffset(x, y); + + ulong dstAddress = dstBaseAddress + (ulong)dstOffset; + + Span pixel = data.Slice(srcOffset, 1); + + _channel.MemoryManager.Physical.Write(dstAddress, pixel); + } + } + } + + _finished = true; + + _context.AdvanceSequence(); + } + } +} -- cgit v1.2.3