aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/GpuChannel.cs
blob: 33618a15ba01fe7f5f33e84707ef1265c388830b (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
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.Threading;

namespace Ryujinx.Graphics.Gpu
{
    /// <summary>
    /// Represents a GPU channel.
    /// </summary>
    public class GpuChannel : IDisposable
    {
        private readonly GpuContext _context;
        private readonly GPFifoDevice _device;
        private readonly GPFifoProcessor _processor;
        private MemoryManager _memoryManager;

        /// <summary>
        /// Channel buffer bindings manager.
        /// </summary>
        internal BufferManager BufferManager { get; }

        /// <summary>
        /// Channel texture bindings manager.
        /// </summary>
        internal TextureManager TextureManager { get; }

        /// <summary>
        /// Current channel memory manager.
        /// </summary>
        internal MemoryManager MemoryManager => _memoryManager;

        /// <summary>
        /// Host hardware capabilities from the GPU context.
        /// </summary>
        internal ref Capabilities Capabilities => ref _context.Capabilities;

        /// <summary>
        /// Creates a new instance of a GPU channel.
        /// </summary>
        /// <param name="context">GPU context that the channel belongs to</param>
        internal GpuChannel(GpuContext context)
        {
            _context = context;
            _device = context.GPFifo;
            _processor = new GPFifoProcessor(context, this);
            BufferManager = new BufferManager(context, this);
            TextureManager = new TextureManager(context, this);
        }

        /// <summary>
        /// Binds a memory manager to the channel.
        /// All submitted and in-flight commands will use the specified memory manager for any memory operations.
        /// </summary>
        /// <param name="memoryManager">The new memory manager to be bound</param>
        public void BindMemory(MemoryManager memoryManager)
        {
            var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));

            memoryManager.Physical.IncrementReferenceCount();

            if (oldMemoryManager != null)
            {
                oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
                oldMemoryManager.Physical.DecrementReferenceCount();
                oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
            }

            memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
            memoryManager.MemoryUnmapped += MemoryUnmappedHandler;

            // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
            TextureManager.ReloadPools();
            memoryManager.Physical.BufferCache.QueuePrune();
        }

        /// <summary>
        /// Memory mappings change event handler.
        /// </summary>
        /// <param name="sender">Memory manager where the mappings changed</param>
        /// <param name="e">Information about the region that is being changed</param>
        private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
        {
            TextureManager.ReloadPools();

            var memoryManager = Volatile.Read(ref _memoryManager);
            memoryManager?.Physical.BufferCache.QueuePrune();
        }

        /// <summary>
        /// Writes data directly to the state of the specified class.
        /// </summary>
        /// <param name="classId">ID of the class to write the data into</param>
        /// <param name="offset">State offset in bytes</param>
        /// <param name="value">Value to be written</param>
        public void Write(ClassId classId, int offset, uint value)
        {
            _processor.Write(classId, offset, (int)value);
        }

        /// <summary>
        /// Push a GPFIFO entry in the form of a prefetched command buffer.
        /// It is intended to be used by nvservices to handle special cases.
        /// </summary>
        /// <param name="commandBuffer">The command buffer containing the prefetched commands</param>
        public void PushHostCommandBuffer(int[] commandBuffer)
        {
            _device.PushHostCommandBuffer(_processor, commandBuffer);
        }

        /// <summary>
        /// Pushes GPFIFO entries.
        /// </summary>
        /// <param name="entries">GPFIFO entries</param>
        public void PushEntries(ReadOnlySpan<ulong> entries)
        {
            _device.PushEntries(_processor, entries);
        }

        /// <summary>
        /// Disposes the GPU channel.
        /// It's an error to use the GPU channel after disposal.
        /// </summary>
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            _context.DeferredActions.Enqueue(Destroy);
        }

        /// <summary>
        /// Performs disposal of the host GPU resources used by this channel, that are not shared.
        /// This must only be called from the render thread.
        /// </summary>
        private void Destroy()
        {
            _processor.Dispose();
            TextureManager.Dispose();

            var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
            if (oldMemoryManager != null)
            {
                oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
                oldMemoryManager.Physical.DecrementReferenceCount();
                oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
            }
        }
    }
}