From 4d3f5e3818c55512064d2c5d6b174b2a012d9db8 Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Sat, 21 Oct 2023 12:10:10 -0300 Subject: [PATCH] Delete PhysicalMemory field and replace it with a physical memory list, implement MapForeign for mapping memory from another process --- .../Engine/Dma/DmaClass.cs | 2 - src/Ryujinx.Graphics.Gpu/GpuContext.cs | 2 +- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 27 +- .../Image/TextureBindingsManager.cs | 6 +- .../Memory/BufferManager.cs | 22 +- .../Memory/BufferTextureBinding.cs | 27 +- .../Memory/MemoryManager.cs | 249 ++++++++++++++---- .../Shader/ShaderCache.cs | 23 +- 8 files changed, 261 insertions(+), 97 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index b302b443e..babd1bf32 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -412,8 +412,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma } else { - var bufferCache = memoryManager.GetBackingMemory(dstGpuVa).BufferCache; - if (remap && _state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA && _state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA && diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index a50852b05..9028edc80 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); } - return new MemoryManager(physicalMemory); + return new MemoryManager(this, physicalMemory); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 022a3839f..a7733f66d 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -40,10 +40,13 @@ namespace Ryujinx.Graphics.Gpu.Image } private GpuContext _context; - private PhysicalMemory _physicalMemory; - private SizeInfo _sizeInfo; + /// + /// Backing memory for the texture. + /// + public PhysicalMemory PhysicalMemory { get; private set; } + /// /// Texture format. /// @@ -251,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image MultiRange range) { _context = context; - _physicalMemory = physicalMemory; + PhysicalMemory = physicalMemory; _sizeInfo = sizeInfo; Range = range; @@ -315,7 +318,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Groups that overlap with this one but are incompatible public void InitializeGroup(bool hasLayerViews, bool hasMipViews, List incompatibleOverlaps) { - Group = new TextureGroup(_context, _physicalMemory, this, incompatibleOverlaps); + Group = new TextureGroup(_context, PhysicalMemory, this, incompatibleOverlaps); Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews); } @@ -336,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Image { Texture texture = new( _context, - _physicalMemory, + PhysicalMemory, info, sizeInfo, range, @@ -636,7 +639,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void SynchronizeFull() { - ReadOnlySpan data = _physicalMemory.GetSpan(Range); + ReadOnlySpan data = PhysicalMemory.GetSpan(Range); // If the host does not support ASTC compression, we need to do the decompression. // The decompression is slow, so we want to avoid it as much as possible. @@ -1027,7 +1030,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The specific host texture to flush. Defaults to this texture public void FlushTextureDataToGuest(bool tracked, ITexture texture = null) { - using WritableRegion region = _physicalMemory.GetWritableRegion(Range, tracked); + using WritableRegion region = PhysicalMemory.GetWritableRegion(Range, tracked); GetTextureDataFromGpu(region.Memory.Span, tracked, texture); } @@ -1419,7 +1422,7 @@ namespace Ryujinx.Graphics.Gpu.Image Group.SignalModified(this); } - _physicalMemory.TextureCache.Lift(this); + PhysicalMemory.TextureCache.Lift(this); } /// @@ -1440,7 +1443,7 @@ namespace Ryujinx.Graphics.Gpu.Image Group.SignalModifying(this, bound); } - _physicalMemory.TextureCache.Lift(this); + PhysicalMemory.TextureCache.Lift(this); if (bound) { @@ -1528,7 +1531,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (ShortCacheEntry != null) { - _physicalMemory.TextureCache.RemoveShortCache(this); + PhysicalMemory.TextureCache.RemoveShortCache(this); } } @@ -1557,7 +1560,7 @@ namespace Ryujinx.Graphics.Gpu.Image _viewStorage.RemoveView(this); } - _physicalMemory.TextureCache.RemoveTextureFromCache(this); + PhysicalMemory.TextureCache.RemoveTextureFromCache(this); } Debug.Assert(newRefCount >= 0); @@ -1613,7 +1616,7 @@ namespace Ryujinx.Graphics.Gpu.Image // If this is called from another thread (unmapped), the short cache will // have to remove this texture on a future tick. - _physicalMemory.TextureCache.RemoveShortCache(this); + PhysicalMemory.TextureCache.RemoveShortCache(this); } InvalidatedSequence++; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index eab971fbc..982191ff3 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -524,8 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image // Ensure that the buffer texture is using the correct buffer as storage. // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. - var bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; - _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false); + _channel.BufferManager.SetBufferTextureStorage(stage, texture, hostTexture, bindingInfo, bindingInfo.Format, false); // Cache is not used for buffer texture, it must always rebind. state.CachedTexture = null; @@ -662,8 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image format = texture.Format; } - var bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; - _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true); + _channel.BufferManager.SetBufferTextureStorage(stage, texture, hostTexture, bindingInfo, format, true); // Cache is not used for buffer texture, it must always rebind. state.CachedTexture = null; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 307b814dc..d8d788ade 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -490,17 +490,17 @@ namespace Ryujinx.Graphics.Gpu.Memory { var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); var range = binding.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore); - binding.Texture.SetStorage(range); + binding.HostTexture.SetStorage(range); // The texture must be rebound to use the new storage if it was updated. if (binding.IsImage) { - _context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format); + _context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.HostTexture, binding.Format); } else { - _context.Renderer.Pipeline.SetTextureAndSampler(binding.Stage, binding.BindingInfo.Binding, binding.Texture, null); + _context.Renderer.Pipeline.SetTextureAndSampler(binding.Stage, binding.BindingInfo.Binding, binding.HostTexture, null); } } @@ -801,25 +801,23 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Shader stage accessing the texture /// Buffer texture - /// Buffer cache that owns the buffer texture - /// Address of the buffer in memory - /// Size of the buffer in bytes + /// Buffer host texture /// Binding info for the buffer texture /// Format of the buffer texture /// Whether the binding is for an image or a sampler public void SetBufferTextureStorage( ShaderStage stage, - ITexture texture, - BufferCache bufferCache, - ulong address, - ulong size, + Image.Texture texture, + ITexture hostTexture, TextureBindingInfo bindingInfo, Format format, bool isImage) { - bufferCache.CreateBuffer(address, size); + var binding = new BufferTextureBinding(stage, texture, hostTexture, bindingInfo, format, isImage); - _bufferTextures.Add(new BufferTextureBinding(stage, texture, bufferCache, address, size, bindingInfo, format, isImage)); + texture.PhysicalMemory.BufferCache.CreateBuffer(binding.Address, binding.Size); + + _bufferTextures.Add(binding); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs index 726693772..f28497e9b 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs @@ -17,22 +17,27 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// The buffer texture. /// - public ITexture Texture { get; } + public Image.Texture Texture { get; } + + /// + /// The buffer host texture. + /// + public ITexture HostTexture { get; } /// /// Buffer cache that owns the buffer. /// - public BufferCache BufferCache { get; } + public BufferCache BufferCache => Texture.PhysicalMemory.BufferCache; /// /// The base address of the buffer binding. /// - public ulong Address { get; } + public ulong Address => Texture.Range.GetSubRange(0).Address; /// /// The size of the buffer binding in bytes. /// - public ulong Size { get; } + public ulong Size => Texture.Size; /// /// The image or sampler binding info for the buffer texture. @@ -54,27 +59,21 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Shader stage accessing the texture /// Buffer texture - /// Buffer cache that owns the buffer - /// Base address - /// Size in bytes + /// Buffer host texture /// Binding info /// Binding format /// Whether the binding is for an image or a sampler public BufferTextureBinding( ShaderStage stage, - ITexture texture, - BufferCache bufferCache, - ulong address, - ulong size, + Image.Texture texture, + ITexture hostTexture, TextureBindingInfo bindingInfo, Format format, bool isImage) { Stage = stage; Texture = texture; - BufferCache = bufferCache; - Address = address; - Size = size; + HostTexture = hostTexture; BindingInfo = bindingInfo; Format = format; IsImage = isImage; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 3b93fd59a..de78234d9 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -34,10 +34,9 @@ namespace Ryujinx.Graphics.Gpu.Memory public event EventHandler MemoryUnmapped; - /// - /// Physical memory where the virtual memory is mapped into. - /// - private PhysicalMemory Physical { get; } + private readonly GpuContext _context; + private readonly List _physicalMemoryList; + private readonly Dictionary _physicalMemoryMap; /// /// Cache of GPU counters. @@ -47,14 +46,26 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Creates a new instance of the GPU memory manager. /// + /// GPU context /// Physical memory that this memory manager will map into - internal MemoryManager(PhysicalMemory physicalMemory) + internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory) { - Physical = physicalMemory; + _context = context; + + _physicalMemoryList = new List() + { + physicalMemory + }; + + _physicalMemoryMap = new Dictionary + { + { physicalMemory, 0 } + }; + CounterCache = new CounterCache(); _pageTable = new ulong[PtLvl0Size][]; - MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler; - MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler; + MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler; + MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += CounterCache.MemoryUnmappedHandler; } @@ -64,19 +75,23 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Action to be performed when the buffer cache changes internal void AttachToChannel(Action rebind) { - Physical.IncrementReferenceCount(); - Physical.BufferCache.NotifyBuffersModified += rebind; - Physical.BufferCache.QueuePrune(); + PhysicalMemory physicalMemory = GetOwnPhysicalMemory(); + + physicalMemory.IncrementReferenceCount(); + physicalMemory.BufferCache.NotifyBuffersModified += rebind; + physicalMemory.BufferCache.QueuePrune(); } /// - /// Attaches the memory manager to a new GPU channel. + /// Detaches the memory manager from a GPU channel. /// /// Action that was performed when the buffer cache changed internal void DetachFromChannel(Action rebind) { - Physical.BufferCache.NotifyBuffersModified -= rebind; - Physical.DecrementReferenceCount(); + PhysicalMemory physicalMemory = GetOwnPhysicalMemory(); + + physicalMemory.BufferCache.NotifyBuffersModified -= rebind; + physicalMemory.DecrementReferenceCount(); } /// @@ -84,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// internal void QueuePrune() { - Physical.BufferCache.QueuePrune(); + GetOwnPhysicalMemory().BufferCache.QueuePrune(); } /// @@ -100,15 +115,15 @@ namespace Ryujinx.Graphics.Gpu.Memory if (IsContiguous(va, size)) { - ulong address = Translate(va); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); if (tracked) { - return Physical.ReadTracked(address); + return physicalMemory.ReadTracked(address); } else { - return Physical.Read(address); + return physicalMemory.Read(address); } } else @@ -132,7 +147,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, size)) { - return Physical.GetSpan(Translate(va), size, tracked); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + return physicalMemory.GetSpan(address, size, tracked); } else { @@ -157,7 +174,7 @@ namespace Ryujinx.Graphics.Gpu.Memory bool isContiguous = true; int mappedSize; - if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va))) + if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va)) { ulong endVa = va + (ulong)size; ulong endVaAligned = (endVa + PageMask) & ~PageMask; @@ -170,7 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong nextVa = currentVa + PageSize; ulong nextPa = Translate(nextVa); - if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa)) + if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa)) { break; } @@ -199,7 +216,9 @@ namespace Ryujinx.Graphics.Gpu.Memory if (isContiguous) { - return Physical.GetSpan(Translate(va), mappedSize, tracked); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + return physicalMemory.GetSpan(address, mappedSize, tracked); } else { @@ -211,6 +230,23 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Checks if a page of memory is mapped on the GPU and its backing memory. + /// + /// GPU virtual address of the page + /// True if mapped, false otherwise + private bool IsMappedOnGpuAndPhysical(ulong va) + { + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + if (address == PteUnmapped) + { + return false; + } + + return physicalMemory.IsMapped(address); + } + /// /// Reads data from a possibly non-contiguous region of GPU mapped memory. /// @@ -228,22 +264,22 @@ namespace Ryujinx.Graphics.Gpu.Memory if ((va & PageMask) != 0) { - ulong pa = Translate(va); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]); + physicalMemory.GetSpan(pa, size, tracked).CopyTo(data[..size]); offset += size; } for (; offset < data.Length; offset += size) { - ulong pa = Translate(va + (ulong)offset); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); size = Math.Min(data.Length - offset, (int)PageSize); - Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size)); + physicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size)); } } @@ -258,7 +294,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, size)) { - return Physical.GetWritableRegion(Translate(va), size, tracked); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + return physicalMemory.GetWritableRegion(address, size, tracked); } else { @@ -288,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void Write(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.Write); + WriteImpl(va, data, (physical, va, data) => physical.Write(va, data)); } /// @@ -298,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteTrackedResource(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.WriteTrackedResource); + WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data)); } /// @@ -308,10 +346,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteUntracked(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.WriteUntracked); + WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data)); } - private delegate void WriteCallback(ulong address, ReadOnlySpan data); + private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan data); /// /// Writes data to possibly non-contiguous GPU mapped memory. @@ -323,7 +361,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, data.Length)) { - writeCallback(Translate(va), data); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + writeCallback(physicalMemory, address, data); } else { @@ -331,22 +371,22 @@ namespace Ryujinx.Graphics.Gpu.Memory if ((va & PageMask) != 0) { - ulong pa = Translate(va); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - writeCallback(pa, data[..size]); + writeCallback(physicalMemory, pa, data[..size]); offset += size; } for (; offset < data.Length; offset += size) { - ulong pa = Translate(va + (ulong)offset); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); size = Math.Min(data.Length - offset, (int)PageSize); - writeCallback(pa, data.Slice(offset, size)); + writeCallback(physicalMemory, pa, data.Slice(offset, size)); } } } @@ -360,7 +400,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, data.Length)) { - Physical.Write(Translate(va), data); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + physicalMemory.Write(address, data); } else { @@ -368,13 +410,13 @@ namespace Ryujinx.Graphics.Gpu.Memory if ((va & PageMask) != 0) { - ulong pa = Translate(va); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - if (pa != PteUnmapped && Physical.IsMapped(pa)) + if (pa != PteUnmapped && physicalMemory.IsMapped(pa)) { - Physical.Write(pa, data[..size]); + physicalMemory.Write(pa, data[..size]); } offset += size; @@ -382,13 +424,13 @@ namespace Ryujinx.Graphics.Gpu.Memory for (; offset < data.Length; offset += size) { - ulong pa = Translate(va + (ulong)offset); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); size = Math.Min(data.Length - offset, (int)PageSize); - if (pa != PteUnmapped && Physical.IsMapped(pa)) + if (pa != PteUnmapped && physicalMemory.IsMapped(pa)) { - Physical.Write(pa, data.Slice(offset, size)); + physicalMemory.Write(pa, data.Slice(offset, size)); } } } @@ -421,15 +463,51 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the mapping /// Kind of the resource located at the mapping public void Map(ulong pa, ulong va, ulong size, PteKind kind) + { + MapImpl(pa, va, size, kind); + } + + /// + /// Maps a given range of pages to the specified CPU virtual address from a different process. + /// + /// + /// All addresses and sizes must be page aligned. + /// + /// CPU virtual address to map into + /// GPU virtual address to be mapped + /// Size in bytes of the mapping + /// Kind of the resource located at the mapping + /// PID of the process that owns the mapping + public void MapForeign(ulong pa, ulong va, ulong size, PteKind kind, ulong ownedPid) + { + if (_context.PhysicalMemoryRegistry.TryGetValue(ownedPid, out PhysicalMemory physicalMemory)) + { + MapImpl(pa, va, size, kind, physicalMemory); + } + } + + /// + /// Maps a given range of pages to the specified CPU virtual address. + /// + /// + /// All addresses and sizes must be page aligned. + /// + /// CPU virtual address to map into + /// GPU virtual address to be mapped + /// Size in bytes of the mapping + /// Kind of the resource located at the mapping + /// Optional physical memory to import for the mapping + private void MapImpl(ulong pa, ulong va, ulong size, PteKind kind, PhysicalMemory physicalMemory = null) { lock (_pageTable) { UnmapEventArgs e = new(va, size); MemoryUnmapped?.Invoke(this, e); + byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0; for (ulong offset = 0; offset < size; offset += PageSize) { - SetPte(va + offset, PackPte(pa + offset, kind)); + SetPte(va + offset, PackPte(pa + offset, pIndex, kind)); } RunRemapActions(e); @@ -480,12 +558,14 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int page = 0; page < pages - 1; page++) { - if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped) + ulong nextPte = GetPte(va + PageSize); + + if (!ValidateAddress(va + PageSize) || nextPte == PteUnmapped) { return false; } - if (Translate(va) + PageSize != Translate(va + PageSize)) + if (GetPte(va) + PageSize != nextPte) { return false; } @@ -592,9 +672,47 @@ namespace Ryujinx.Graphics.Gpu.Memory return true; } + /// + /// Gets the backing memory for a given GPU virtual address. + /// + /// GPU virtual address to get the backing memory from + /// The backing memory for the specified GPU virtual address internal PhysicalMemory GetBackingMemory(ulong va) { - return Physical; + ulong pte = GetPte(va); + + if (pte == PteUnmapped) + { + return GetOwnPhysicalMemory(); + } + + return _physicalMemoryList[UnpackPIndexFromPte(pte)]; + } + + /// + /// Gets the backing memory that is owned by this GPU memory manager. + /// + /// The backing memory owned by this memory manager + private PhysicalMemory GetOwnPhysicalMemory() + { + return _physicalMemoryList[0]; + } + + /// + /// Gets the index for a given physical memory on the list, adding it to the list if needed. + /// + /// Physical memory to get the index from + /// The index of the physical memory on the list + private byte GetOrAddPhysicalMemory(PhysicalMemory physicalMemory) + { + if (!_physicalMemoryMap.TryGetValue(physicalMemory, out byte pIndex)) + { + pIndex = checked((byte)_physicalMemoryList.Count); + _physicalMemoryList.Add(physicalMemory); + _physicalMemoryMap.Add(physicalMemory, pIndex); + } + + return pIndex; } /// @@ -698,6 +816,28 @@ namespace Ryujinx.Graphics.Gpu.Memory return Math.Min(maxSize, va - startVa); } + /// + /// Translates a GPU virtual address to a CPU virtual address and the associated physical memory. + /// + /// GPU virtual address to be translated + /// CPU virtual address with the physical memory, or if unmapped + private (PhysicalMemory, ulong) TranslateWithPhysicalMemory(ulong va) + { + if (!ValidateAddress(va)) + { + return (GetOwnPhysicalMemory(), PteUnmapped); + } + + ulong pte = GetPte(va); + + if (pte == PteUnmapped) + { + return (GetOwnPhysicalMemory(), PteUnmapped); + } + + return (_physicalMemoryList[UnpackPIndexFromPte(pte)], UnpackPaFromPte(pte) + (va & PageMask)); + } + /// /// Gets the kind of a given memory page. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling. @@ -766,11 +906,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Creates a page table entry from a physical address and kind. /// /// Physical address + /// Index of the physical memory on the list /// Kind /// Page table entry - private static ulong PackPte(ulong pa, PteKind kind) + private static ulong PackPte(ulong pa, byte pIndex, PteKind kind) { - return pa | ((ulong)kind << 56); + return pa | ((ulong)pIndex << 48) | ((ulong)kind << 56); } /// @@ -783,6 +924,16 @@ namespace Ryujinx.Graphics.Gpu.Memory return (PteKind)(pte >> 56); } + /// + /// Unpacks the physical memory index in the list from a page table entry. + /// + /// Page table entry + /// Physical memory index + private static byte UnpackPIndexFromPte(ulong pte) + { + return (byte)(pte >> 48); + } + /// /// Unpacks physical address from a page table entry. /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 04482ca64..ca089eaec 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -730,8 +730,8 @@ namespace Ryujinx.Graphics.Gpu.Shader codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); - byte[] cb1DataA = physical.GetSpan(cb1DataAddress, vertexA.Cb1DataSize).ToArray(); - byte[] cb1DataB = physical.GetSpan(cb1DataAddress, currentStage.Cb1DataSize).ToArray(); + byte[] cb1DataA = Read(physical, cb1DataAddress, vertexA.Cb1DataSize); + byte[] cb1DataB = Read(physical, cb1DataAddress, currentStage.Cb1DataSize); ShaderDumpPaths pathsA = default; ShaderDumpPaths pathsB = default; @@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ? channel.BufferManager.GetComputeUniformBufferAddress(1) : channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1); - byte[] cb1Data = physical.GetSpan(cb1DataAddress, context.Cb1DataSize).ToArray(); + byte[] cb1Data = Read(physical, cb1DataAddress, context.Cb1DataSize); code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray(); ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default; @@ -781,6 +781,23 @@ namespace Ryujinx.Graphics.Gpu.Shader return new TranslatedShader(new CachedShaderStage(program.Info, code, cb1Data), program); } + /// + /// Reads data from physical memory, if it exists. + /// + /// Physical memory to read the data from, might be null + /// Address to read the data from + /// Size of the data to read + /// An array with the data + private static byte[] Read(PhysicalMemory physicalMemory, ulong address, int size) + { + if (size == 0 || physicalMemory == null) + { + return Array.Empty(); + } + + return physicalMemory.GetSpan(address, size).ToArray(); + } + /// /// Gets the index of a stage from a . ///