aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Host1x/Host1xDevice.cs
blob: 7f6cef62f0c6009aa45fd1ca58ea3089b4905f86 (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
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Synchronization;
using System;
using System.Numerics;

namespace Ryujinx.Graphics.Host1x
{
    public sealed class Host1xDevice : IDisposable
    {
        private readonly SyncptIncrManager _syncptIncrMgr;
        private readonly AsyncWorkQueue<int[]> _commandQueue;

        private readonly Devices _devices = new Devices();

        public Host1xClass Class { get; }

        private IDeviceState _device;

        private int _count;
        private int _offset;
        private int _mask;
        private bool _incrementing;

        public Host1xDevice(SynchronizationManager syncMgr)
        {
            _syncptIncrMgr = new SyncptIncrManager(syncMgr);
            _commandQueue = new AsyncWorkQueue<int[]>(Process, "Ryujinx.Host1xProcessor");

            Class = new Host1xClass(syncMgr);

            _devices.RegisterDevice(ClassId.Host1x, Class);
        }

        public void RegisterDevice(ClassId classId, IDeviceState device)
        {
            var thi = new ThiDevice(classId, device ?? throw new ArgumentNullException(nameof(device)), _syncptIncrMgr);
            _devices.RegisterDevice(classId, thi);
        }

        public void Submit(ReadOnlySpan<int> commandBuffer)
        {
            _commandQueue.Add(commandBuffer.ToArray());
        }

        private void Process(int[] commandBuffer)
        {
            for (int index = 0; index < commandBuffer.Length; index++)
            {
                Step(commandBuffer[index]);
            }
        }

        private void Step(int value)
        {
            if (_mask != 0)
            {
                int lbs = BitOperations.TrailingZeroCount(_mask);

                _mask &= ~(1 << lbs);

                DeviceWrite(_offset + lbs, value);

                return;
            }
            else if (_count != 0)
            {
                _count--;

                DeviceWrite(_offset, value);

                if (_incrementing)
                {
                    _offset++;
                }

                return;
            }

            OpCode opCode = (OpCode)((value >> 28) & 0xf);

            switch (opCode)
            {
                case OpCode.SetClass:
                    _mask = value & 0x3f;
                    ClassId classId = (ClassId)((value >> 6) & 0x3ff);
                    _offset = (value >> 16) & 0xfff;
                    _device = _devices.GetDevice(classId);
                    break;
                case OpCode.Incr:
                case OpCode.NonIncr:
                    _count = value & 0xffff;
                    _offset = (value >> 16) & 0xfff;
                    _incrementing = opCode == OpCode.Incr;
                    break;
                case OpCode.Mask:
                    _mask = value & 0xffff;
                    _offset = (value >> 16) & 0xfff;
                    break;
                case OpCode.Imm:
                    int data = value & 0xfff;
                    _offset = (value >> 16) & 0xfff;
                    DeviceWrite(_offset, data);
                    break;
                default:
                    Logger.Error?.Print(LogClass.Host1x, $"Unsupported opcode \"{opCode}\".");
                    break;
            }
        }

        private void DeviceWrite(int offset, int data)
        {
            _device?.Write(offset * 4, data);
        }

        public void Dispose()
        {
            _commandQueue.Dispose();
            _devices.Dispose();
        }
    }
}