using Ryujinx.Common; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Texture; using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Engine { partial class Methods { private Inline2MemoryParams _params; private bool _isLinear; private int _offset; private int _size; private bool _finished; private int[] _buffer; /// /// Launches Inline-to-Memory engine DMA copy. /// /// Current GPU state /// Method call argument public void LaunchDma(GpuState state, int argument) { _params = state.Get(MethodOffset.I2mParams); _isLinear = (argument & 1) != 0; _offset = 0; _size = _params.LineLengthIn * _params.LineCount; int count = BitUtils.DivRoundUp(_size, 4); if (_buffer == null || _buffer.Length < count) { _buffer = new int[count]; } ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack()); _context.Methods.TextureManager.Flush(dstBaseAddress, (ulong)_size); _finished = false; } /// /// Pushes a word of data to the Inline-to-Memory engine. /// /// Current GPU state /// Method call argument public void LoadInlineData(GpuState state, 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 && _params.LineCount == 1) { ulong address = _context.MemoryManager.Translate( _params.DstAddress.Pack()); _context.PhysicalMemory.Write(address, data); } else { var dstCalculator = new OffsetCalculator( _params.DstWidth, _params.DstHeight, _params.DstStride, _isLinear, _params.DstMemoryLayout.UnpackGobBlocksInY(), 1); int srcOffset = 0; ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack()); for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++) { int x1 = _params.DstX; int x2 = _params.DstX + _params.LineLengthIn; int x2Trunc = _params.DstX + BitUtils.AlignDown(_params.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); _context.PhysicalMemory.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); _context.PhysicalMemory.Write(dstAddress, pixel); } } } _finished = true; } } }