aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
blob: 3d23b785c2066f2163bae70108cddc61bcc0e637 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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;

        /// <summary>
        /// Launches Inline-to-Memory engine DMA copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        public void LaunchDma(GpuState state, int argument)
        {
            _params = state.Get<Inline2MemoryParams>(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 = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());

            // Trigger read tracking, to flush any managed resources in the destination region.
            state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true);

            _finished = false;
        }

        /// <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>
        public void LoadInlineData(GpuState state, int argument)
        {
            if (!_finished)
            {
                _buffer[_offset++] = argument;

                if (_offset * 4 >= _size)
                {
                    FinishTransfer(state);
                }
            }
        }

        /// <summary>
        /// Performs actual copy of the inline data after the transfer is finished.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void FinishTransfer(GpuState state)
        {
            Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);

            if (_isLinear && _params.LineCount == 1)
            {
                ulong address = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());

                state.Channel.MemoryManager.Physical.Write(address, data);
            }
            else
            {
                var dstCalculator = new OffsetCalculator(
                    _params.DstWidth,
                    _params.DstHeight,
                    _params.DstStride,
                    _isLinear,
                    _params.DstMemoryLayout.UnpackGobBlocksInY(),
                    1);

                int srcOffset = 0;

                ulong dstBaseAddress = state.Channel.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);

                        state.Channel.MemoryManager.Physical.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);

                        state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
                    }
                }
            }

            _finished = true;

            _context.AdvanceSequence();
        }
    }
}