diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image/Pool.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Gpu/Image/Pool.cs | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/Pool.cs b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs new file mode 100644 index 00000000..3e557c0b --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -0,0 +1,222 @@ +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, ResourceKind.Pool); + _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 GetDescriptorRefAddress(Address + (ulong)id * DescriptorSize); + } + + /// <summary> + /// Gets a reference to the descriptor for a given address. + /// </summary> + /// <param name="address">Address of the descriptor</param> + /// <returns>A reference to the descriptor</returns> + public ref readonly T2 GetDescriptorRefAddress(ulong address) + { + return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(address, 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(); + } + } +}
\ No newline at end of file |