aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics/NvGpuFifo.cs
blob: a8d1b36d17782d9916abd525e2e0c464734731c4 (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
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
using Ryujinx.Graphics.Memory;

namespace Ryujinx.Graphics
{
    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 NvGpuEngine[] SubChannels;

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

            private bool ExecutionPending;
            private int  Argument;

            private MacroInterpreter Interpreter;

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

                ExecutionPending = false;
                Argument         = 0;

                Interpreter = new MacroInterpreter(PFifo, Engine);
            }

            public void StartExecution(int Argument)
            {
                this.Argument = Argument;

                ExecutionPending = true;
            }

            public void Execute(NvGpuVmm Vmm, int[] Mme)
            {
                if (ExecutionPending)
                {
                    ExecutionPending = false;

                    Interpreter?.Execute(Vmm, Mme, Position, Argument);
                }
            }

            public void PushArgument(int Argument)
            {
                Interpreter?.Fifo.Enqueue(Argument);
            }
        }

        private int CurrMacroPosition;
        private int CurrMacroBindIndex;

        private CachedMacro[] Macros;

        private int[] Mme;

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

            SubChannels = new NvGpuEngine[8];

            Macros = new CachedMacro[MacrosCount];

            Mme = new int[MmeWords];
        }

        public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
        {
            if ((NvGpuFifoMeth)MethCall.Method == NvGpuFifoMeth.BindChannel)
            {
                NvGpuEngine Engine = (NvGpuEngine)MethCall.Argument;

                SubChannels[MethCall.SubChannel] = Engine;
            }
            else
            {
                switch (SubChannels[MethCall.SubChannel])
                {
                    case NvGpuEngine._2d:  Call2dMethod  (Vmm, MethCall); break;
                    case NvGpuEngine._3d:  Call3dMethod  (Vmm, MethCall); break;
                    case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, MethCall); break;
                    case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, MethCall); break;
                }
            }
        }

        private void Call2dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
        {
            Gpu.Engine2d.CallMethod(Vmm, MethCall);
        }

        private void Call3dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
        {
            if (MethCall.Method < 0x80)
            {
                switch ((NvGpuFifoMeth)MethCall.Method)
                {
                    case NvGpuFifoMeth.SetMacroUploadAddress:
                    {
                        CurrMacroPosition = MethCall.Argument;

                        break;
                    }

                    case NvGpuFifoMeth.SendMacroCodeData:
                    {
                        Mme[CurrMacroPosition++] = MethCall.Argument;

                        break;
                    }

                    case NvGpuFifoMeth.SetMacroBindingIndex:
                    {
                        CurrMacroBindIndex = MethCall.Argument;

                        break;
                    }

                    case NvGpuFifoMeth.BindMacro:
                    {
                        int Position = MethCall.Argument;

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

                        break;
                    }

                    default: CallP2mfMethod(Vmm, MethCall); break;
                }
            }
            else if (MethCall.Method < 0xe00)
            {
                Gpu.Engine3d.CallMethod(Vmm, MethCall);
            }
            else
            {
                int MacroIndex = (MethCall.Method >> 1) & MacroIndexMask;

                if ((MethCall.Method & 1) != 0)
                {
                    Macros[MacroIndex].PushArgument(MethCall.Argument);
                }
                else
                {
                    Macros[MacroIndex].StartExecution(MethCall.Argument);
                }

                if (MethCall.IsLastCall)
                {
                    Macros[MacroIndex].Execute(Vmm, Mme);
                }
            }
        }

        private void CallP2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
        {
            Gpu.EngineP2mf.CallMethod(Vmm, MethCall);
        }

        private void CallM2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
        {
            Gpu.EngineM2mf.CallMethod(Vmm, MethCall);
        }
    }
}