aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs
blob: 0e62665481831d07f17568f24cf54051ff4ef7cb (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
using Ryujinx.HLE.Gpu.Memory;
using System.Collections.Concurrent;
using System.Threading;

namespace Ryujinx.HLE.Gpu.Engines
{
    class NvGpuFifo
    {
        private const int MacrosCount    = 0x80;
        private const int MacroIndexMask = MacrosCount - 1;

        //Note: The size of the macro memory is unknown, we just make
        //a guess here and use 256kb as the size. Increase if needed.
        private const int MmeWords = 256 * 256;

        private NvGpu Gpu;

        private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry[])> BufferQueue;

        private NvGpuEngine[] SubChannels;

        public AutoResetEvent Event { get; private set; }

        private struct CachedMacro
        {
            public int Position { get; private set; }

            private MacroInterpreter Interpreter;

            public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, int Position)
            {
                this.Position = Position;

                Interpreter = new MacroInterpreter(PFifo, Engine);
            }

            public void PushParam(int Param)
            {
                Interpreter?.Fifo.Enqueue(Param);
            }

            public void Execute(NvGpuVmm Vmm, int[] Mme, int Param)
            {
                Interpreter?.Execute(Vmm, Mme, Position, Param);
            }
        }

        private int CurrMacroPosition;
        private int CurrMacroBindIndex;

        private CachedMacro[] Macros;

        private int[] Mme;

        public NvGpuFifo(NvGpu Gpu)
        {
            this.Gpu = Gpu;

            BufferQueue = new ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry[])>();

            SubChannels = new NvGpuEngine[8];

            Macros = new CachedMacro[MacrosCount];

            Mme = new int[MmeWords];

            Event = new AutoResetEvent(false);
        }

        public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer)
        {
            BufferQueue.Enqueue((Vmm, Buffer));

            Event.Set();
        }

        public void DispatchCalls()
        {
            while (Step());
        }

        private (NvGpuVmm Vmm, NvGpuPBEntry[] Pb) Curr;

        private int CurrPbEntryIndex;

        public bool Step()
        {
            while (Curr.Pb == null || Curr.Pb.Length <= CurrPbEntryIndex)
            {
                if (!BufferQueue.TryDequeue(out Curr))
                {
                    return false;
                }

                Gpu.Engine3d.ResetCache();

                CurrPbEntryIndex = 0;
            }

            CallMethod(Curr.Vmm, Curr.Pb[CurrPbEntryIndex++]);

            return true;
        }

        private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
        {
            if (PBEntry.Method < 0x80)
            {
                switch ((NvGpuFifoMeth)PBEntry.Method)
                {
                    case NvGpuFifoMeth.BindChannel:
                    {
                        NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];

                        SubChannels[PBEntry.SubChannel] = Engine;

                        break;
                    }

                    case NvGpuFifoMeth.SetMacroUploadAddress:
                    {
                        CurrMacroPosition = PBEntry.Arguments[0];

                        break;
                    }

                    case NvGpuFifoMeth.SendMacroCodeData:
                    {
                        foreach (int Arg in PBEntry.Arguments)
                        {
                            Mme[CurrMacroPosition++] = Arg;
                        }
                        break;
                    }

                    case NvGpuFifoMeth.SetMacroBindingIndex:
                    {
                        CurrMacroBindIndex = PBEntry.Arguments[0];

                        break;
                    }

                    case NvGpuFifoMeth.BindMacro:
                    {
                        int Position = PBEntry.Arguments[0];

                        Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);

                        break;
                    }
                }
            }
            else
            {
                switch (SubChannels[PBEntry.SubChannel])
                {
                    case NvGpuEngine._2d: Call2dMethod (Vmm, PBEntry); break;
                    case NvGpuEngine._3d: Call3dMethod (Vmm, PBEntry); break;
                    case NvGpuEngine.Dma: CallDmaMethod(Vmm, PBEntry); break;
                }
            }
        }

        private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
        {
            Gpu.Engine2d.CallMethod(Vmm, PBEntry);
        }

        private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
        {
            if (PBEntry.Method < 0xe00)
            {
                Gpu.Engine3d.CallMethod(Vmm, PBEntry);
            }
            else
            {
                int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask;

                if ((PBEntry.Method & 1) != 0)
                {
                    foreach (int Arg in PBEntry.Arguments)
                    {
                        Macros[MacroIndex].PushParam(Arg);
                    }
                }
                else
                {
                    Macros[MacroIndex].Execute(Vmm, Mme, PBEntry.Arguments[0]);
                }
            }
        }

        private void CallDmaMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
        {
            Gpu.EngineDma.CallMethod(Vmm, PBEntry);
        }
    }
}