Ryujinx/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs

130 lines
4 KiB
C#
Raw Normal View History

using Ryujinx.Common;
2019-10-13 08:02:07 +02:00
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Texture;
2019-10-13 08:02:07 +02:00
using System;
using System.Runtime.InteropServices;
2019-10-13 08:02:07 +02:00
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;
/// <summary>
/// Launches Inline-to-Memory engine DMA copy.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
2019-11-22 03:46:14 +01:00
public void LaunchDma(GpuState state, int argument)
2019-10-13 08:02:07 +02:00
{
2019-11-22 03:46:14 +01:00
_params = state.Get<Inline2MemoryParams>(MethodOffset.I2mParams);
2019-10-13 08:02:07 +02:00
_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;
2019-10-13 08:02:07 +02:00
}
/// <summary>
/// Pushes a word of data to the Inline-to-Memory engine.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
2019-11-22 03:46:14 +01:00
public void LoadInlineData(GpuState state, int argument)
2019-10-13 08:02:07 +02:00
{
if (!_finished)
2019-10-13 08:02:07 +02:00
{
_buffer[_offset++] = argument;
2019-10-13 08:02:07 +02:00
if (_offset * 4 >= _size)
{
FinishTransfer();
2019-10-13 08:02:07 +02:00
}
}
}
/// <summary>
/// Performs actual copy of the inline data after the transfer is finished.
/// </summary>
private void FinishTransfer()
{
Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
if (_isLinear && _params.LineCount == 1)
{
ulong address = _context.MemoryManager.Translate( _params.DstAddress.Pack());
_context.PhysicalMemory.Write(address, data);
}
2019-10-13 08:02:07 +02:00
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<byte> 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<byte> pixel = data.Slice(srcOffset, 1);
_context.PhysicalMemory.Write(dstAddress, pixel);
}
}
2019-10-13 08:02:07 +02:00
}
_finished = true;
2019-10-13 08:02:07 +02:00
}
}
}