mirror of
				https://github.com/Ryujinx/Ryujinx.git
				synced 2025-10-25 13:22:30 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			212 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Ryujinx.Cpu.Tracking;
 | |
| using Ryujinx.Graphics.Gpu.Memory;
 | |
| using System;
 | |
| using System.Runtime.InteropServices;
 | |
| 
 | |
| namespace Ryujinx.Graphics.Gpu.Image
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Represents a pool of GPU resources, such as samplers or textures.
 | |
|     /// </summary>
 | |
|     /// <typeparam name="T1">Type of the GPU resource</typeparam>
 | |
|     /// <typeparam name="T2">Type of the descriptor</typeparam>
 | |
|     abstract class Pool<T1, T2> : IDisposable where T2 : unmanaged
 | |
|     {
 | |
|         protected const int DescriptorSize = 0x20;
 | |
| 
 | |
|         protected GpuContext Context;
 | |
|         protected PhysicalMemory PhysicalMemory;
 | |
|         protected int SequenceNumber;
 | |
|         protected int ModifiedSequenceNumber;
 | |
| 
 | |
|         protected T1[] Items;
 | |
|         protected T2[] DescriptorCache;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The maximum ID value of resources on the pool (inclusive).
 | |
|         /// </summary>
 | |
|         /// <remarks>
 | |
|         /// The maximum amount of resources on the pool is equal to this value plus one.
 | |
|         /// </remarks>
 | |
|         public int MaximumId { get; }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The address of the pool in guest memory.
 | |
|         /// </summary>
 | |
|         public ulong Address { get; }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The size of the pool in bytes.
 | |
|         /// </summary>
 | |
|         public ulong Size { get; }
 | |
| 
 | |
|         private readonly CpuMultiRegionHandle _memoryTracking;
 | |
|         private readonly Action<ulong, ulong> _modifiedDelegate;
 | |
| 
 | |
|         private int _modifiedSequenceOffset;
 | |
|         private bool _modified;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a new instance of the GPU resource pool.
 | |
|         /// </summary>
 | |
|         /// <param name="context">GPU context that the pool belongs to</param>
 | |
|         /// <param name="physicalMemory">Physical memory where the resource descriptors are mapped</param>
 | |
|         /// <param name="address">Address of the pool in physical memory</param>
 | |
|         /// <param name="maximumId">Maximum index of an item on the pool (inclusive)</param>
 | |
|         public Pool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId)
 | |
|         {
 | |
|             Context = context;
 | |
|             PhysicalMemory = physicalMemory;
 | |
|             MaximumId = maximumId;
 | |
| 
 | |
|             int count = maximumId + 1;
 | |
| 
 | |
|             ulong size = (ulong)(uint)count * DescriptorSize;
 | |
| 
 | |
|             Items = new T1[count];
 | |
|             DescriptorCache = new T2[count];
 | |
| 
 | |
|             Address = address;
 | |
|             Size    = size;
 | |
| 
 | |
|             _memoryTracking = physicalMemory.BeginGranularTracking(address, size);
 | |
|             _memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
 | |
|             _modifiedDelegate = RegionModified;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the descriptor for a given ID.
 | |
|         /// </summary>
 | |
|         /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
 | |
|         /// <returns>The descriptor</returns>
 | |
|         public T2 GetDescriptor(int id)
 | |
|         {
 | |
|             return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a reference to the descriptor for a given ID.
 | |
|         /// </summary>
 | |
|         /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
 | |
|         /// <returns>A reference to the descriptor</returns>
 | |
|         public ref readonly T2 GetDescriptorRef(int id)
 | |
|         {
 | |
|             return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(Address + (ulong)id * DescriptorSize, DescriptorSize))[0];
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the GPU resource with the given ID.
 | |
|         /// </summary>
 | |
|         /// <param name="id">ID of the resource. This is effectively a zero-based index</param>
 | |
|         /// <returns>The GPU resource with the given ID</returns>
 | |
|         public abstract T1 Get(int id);
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Checks if a given ID is valid and inside the range of the pool.
 | |
|         /// </summary>
 | |
|         /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
 | |
|         /// <returns>True if the specified ID is valid, false otherwise</returns>
 | |
|         public bool IsValidId(int id)
 | |
|         {
 | |
|             return (uint)id <= MaximumId;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Synchronizes host memory with guest memory.
 | |
|         /// This causes invalidation of pool entries,
 | |
|         /// if a modification of entries by the CPU is detected.
 | |
|         /// </summary>
 | |
|         public void SynchronizeMemory()
 | |
|         {
 | |
|             _modified = false;
 | |
|             _memoryTracking.QueryModified(_modifiedDelegate);
 | |
| 
 | |
|             if (_modified)
 | |
|             {
 | |
|                 UpdateModifiedSequence();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Indicate that a region of the pool was modified, and must be loaded from memory.
 | |
|         /// </summary>
 | |
|         /// <param name="mAddress">Start address of the modified region</param>
 | |
|         /// <param name="mSize">Size of the modified region</param>
 | |
|         private void RegionModified(ulong mAddress, ulong mSize)
 | |
|         {
 | |
|             _modified = true;
 | |
| 
 | |
|             if (mAddress < Address)
 | |
|             {
 | |
|                 mAddress = Address;
 | |
|             }
 | |
| 
 | |
|             ulong maxSize = Address + Size - mAddress;
 | |
| 
 | |
|             if (mSize > maxSize)
 | |
|             {
 | |
|                 mSize = maxSize;
 | |
|             }
 | |
| 
 | |
|             InvalidateRangeImpl(mAddress, mSize);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Updates the modified sequence number using the current sequence number and offset,
 | |
|         /// indicating that it has been modified.
 | |
|         /// </summary>
 | |
|         protected void UpdateModifiedSequence()
 | |
|         {
 | |
|             ModifiedSequenceNumber = SequenceNumber + _modifiedSequenceOffset;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// An action to be performed when a precise memory access occurs to this resource.
 | |
|         /// Makes sure that the dirty flags are checked.
 | |
|         /// </summary>
 | |
|         /// <param name="address">Address of the memory action</param>
 | |
|         /// <param name="size">Size in bytes</param>
 | |
|         /// <param name="write">True if the access was a write, false otherwise</param>
 | |
|         private bool PreciseAction(ulong address, ulong size, bool write)
 | |
|         {
 | |
|             if (write && Context.SequenceNumber == SequenceNumber)
 | |
|             {
 | |
|                 if (ModifiedSequenceNumber == SequenceNumber + _modifiedSequenceOffset)
 | |
|                 {
 | |
|                     // The modified sequence number is offset when PreciseActions occur so that
 | |
|                     // users checking it will see an increment and know the pool has changed since
 | |
|                     // their last look, even though the main SequenceNumber has not been changed.
 | |
| 
 | |
|                     _modifiedSequenceOffset++;
 | |
|                 }
 | |
| 
 | |
|                 // Force the pool to be checked again the next time it is used.
 | |
|                 SequenceNumber--;
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         protected abstract void InvalidateRangeImpl(ulong address, ulong size);
 | |
| 
 | |
|         protected abstract void Delete(T1 item);
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Performs the disposal of all resources stored on the pool.
 | |
|         /// It's an error to try using the pool after disposal.
 | |
|         /// </summary>
 | |
|         public virtual void Dispose()
 | |
|         {
 | |
|             if (Items != null)
 | |
|             {
 | |
|                 for (int index = 0; index < Items.Length; index++)
 | |
|                 {
 | |
|                     Delete(Items[index]);
 | |
|                 }
 | |
| 
 | |
|                 Items = null;
 | |
|             }
 | |
|             _memoryTracking.Dispose();
 | |
|         }
 | |
|     }
 | |
| } |