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 { /// /// Represents a GPU channel. /// public class GpuChannel : IDisposable { private readonly GpuContext _context; private readonly GPFifoDevice _device; private readonly GPFifoProcessor _processor; private MemoryManager _memoryManager; /// /// Channel buffer bindings manager. /// internal BufferManager BufferManager { get; } /// /// Channel texture bindings manager. /// internal TextureManager TextureManager { get; } /// /// Current channel memory manager. /// internal MemoryManager MemoryManager => _memoryManager; /// /// Host hardware capabilities from the GPU context. /// internal ref Capabilities Capabilities => ref _context.Capabilities; /// /// Creates a new instance of a GPU channel. /// /// GPU context that the channel belongs to internal GpuChannel(GpuContext context) { _context = context; _device = context.GPFifo; _processor = new GPFifoProcessor(context, this); BufferManager = new BufferManager(context, this); TextureManager = new TextureManager(context, this); } /// /// Binds a memory manager to the channel. /// All submitted and in-flight commands will use the specified memory manager for any memory operations. /// /// The new memory manager to be bound 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(); } /// /// Memory mappings change event handler. /// /// Memory manager where the mappings changed /// Information about the region that is being changed private void MemoryUnmappedHandler(object sender, UnmapEventArgs e) { TextureManager.ReloadPools(); var memoryManager = Volatile.Read(ref _memoryManager); memoryManager?.Physical.BufferCache.QueuePrune(); } /// /// Writes data directly to the state of the specified class. /// /// ID of the class to write the data into /// State offset in bytes /// Value to be written public void Write(ClassId classId, int offset, uint value) { _processor.Write(classId, offset, (int)value); } /// /// Push a GPFIFO entry in the form of a prefetched command buffer. /// It is intended to be used by nvservices to handle special cases. /// /// The command buffer containing the prefetched commands public void PushHostCommandBuffer(int[] commandBuffer) { _device.PushHostCommandBuffer(_processor, commandBuffer); } /// /// Pushes GPFIFO entries. /// /// GPFIFO entries public void PushEntries(ReadOnlySpan entries) { _device.PushEntries(_processor, entries); } /// /// Disposes the GPU channel. /// It's an error to use the GPU channel after disposal. /// public void Dispose() { _context.DeferredActions.Enqueue(Destroy); } /// /// Performs disposal of the host GPU resources used by this channel, that are not shared. /// This must only be called from the render thread. /// private void Destroy() { TextureManager.Dispose(); var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null); if (oldMemoryManager != null) { oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind; oldMemoryManager.Physical.DecrementReferenceCount(); oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; } } } }