Delete PhysicalMemory field and replace it with a physical memory list, implement MapForeign for mapping memory from another process

This commit is contained in:
Gabriel A 2023-10-21 12:10:10 -03:00
parent 640ea94cd8
commit 4d3f5e3818
8 changed files with 261 additions and 97 deletions

View file

@ -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 &&

View file

@ -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);
}
/// <summary>

View file

@ -40,10 +40,13 @@ namespace Ryujinx.Graphics.Gpu.Image
}
private GpuContext _context;
private PhysicalMemory _physicalMemory;
private SizeInfo _sizeInfo;
/// <summary>
/// Backing memory for the texture.
/// </summary>
public PhysicalMemory PhysicalMemory { get; private set; }
/// <summary>
/// Texture format.
/// </summary>
@ -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
/// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
public void InitializeGroup(bool hasLayerViews, bool hasMipViews, List<TextureIncompatibleOverlap> 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
/// </summary>
public void SynchronizeFull()
{
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
ReadOnlySpan<byte> 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
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
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);
}
/// <summary>
@ -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++;

View file

@ -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;

View file

@ -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
/// </summary>
/// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer texture</param>
/// <param name="address">Address of the buffer in memory</param>
/// <param name="size">Size of the buffer in bytes</param>
/// <param name="hostTexture">Buffer host texture</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="format">Format of the buffer texture</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
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);
}
/// <summary>

View file

@ -17,22 +17,27 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// The buffer texture.
/// </summary>
public ITexture Texture { get; }
public Image.Texture Texture { get; }
/// <summary>
/// The buffer host texture.
/// </summary>
public ITexture HostTexture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
public BufferCache BufferCache => Texture.PhysicalMemory.BufferCache;
/// <summary>
/// The base address of the buffer binding.
/// </summary>
public ulong Address { get; }
public ulong Address => Texture.Range.GetSubRange(0).Address;
/// <summary>
/// The size of the buffer binding in bytes.
/// </summary>
public ulong Size { get; }
public ulong Size => Texture.Size;
/// <summary>
/// The image or sampler binding info for the buffer texture.
@ -54,27 +59,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
/// <param name="address">Base address</param>
/// <param name="size">Size in bytes</param>
/// <param name="hostTexture">Buffer host texture</param>
/// <param name="bindingInfo">Binding info</param>
/// <param name="format">Binding format</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
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;

View file

@ -34,10 +34,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
/// <summary>
/// Physical memory where the virtual memory is mapped into.
/// </summary>
private PhysicalMemory Physical { get; }
private readonly GpuContext _context;
private readonly List<PhysicalMemory> _physicalMemoryList;
private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap;
/// <summary>
/// Cache of GPU counters.
@ -47,14 +46,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Creates a new instance of the GPU memory manager.
/// </summary>
/// <param name="context">GPU context</param>
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
internal MemoryManager(PhysicalMemory physicalMemory)
internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory)
{
Physical = physicalMemory;
_context = context;
_physicalMemoryList = new List<PhysicalMemory>()
{
physicalMemory
};
_physicalMemoryMap = new Dictionary<PhysicalMemory, byte>
{
{ 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
/// <param name="rebind">Action to be performed when the buffer cache changes</param>
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();
}
/// <summary>
/// Attaches the memory manager to a new GPU channel.
/// Detaches the memory manager from a GPU channel.
/// </summary>
/// <param name="rebind">Action that was performed when the buffer cache changed</param>
internal void DetachFromChannel(Action rebind)
{
Physical.BufferCache.NotifyBuffersModified -= rebind;
Physical.DecrementReferenceCount();
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
physicalMemory.BufferCache.NotifyBuffersModified -= rebind;
physicalMemory.DecrementReferenceCount();
}
/// <summary>
@ -84,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
internal void QueuePrune()
{
Physical.BufferCache.QueuePrune();
GetOwnPhysicalMemory().BufferCache.QueuePrune();
}
/// <summary>
@ -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<T>(address);
return physicalMemory.ReadTracked<T>(address);
}
else
{
return Physical.Read<T>(address);
return physicalMemory.Read<T>(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
}
}
/// <summary>
/// Checks if a page of memory is mapped on the GPU and its backing memory.
/// </summary>
/// <param name="va">GPU virtual address of the page</param>
/// <returns>True if mapped, false otherwise</returns>
private bool IsMappedOnGpuAndPhysical(ulong va)
{
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
if (address == PteUnmapped)
{
return false;
}
return physicalMemory.IsMapped(address);
}
/// <summary>
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
/// </summary>
@ -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
/// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.Write);
WriteImpl(va, data, (physical, va, data) => physical.Write(va, data));
}
/// <summary>
@ -298,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.WriteTrackedResource);
WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data));
}
/// <summary>
@ -308,10 +346,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.WriteUntracked);
WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data));
}
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan<byte> data);
/// <summary>
/// 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
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
{
MapImpl(pa, va, size, kind);
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address from a different process.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
/// <param name="ownedPid">PID of the process that owns the mapping</param>
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);
}
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
/// <param name="physicalMemory">Optional physical memory to import for the mapping</param>
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;
}
/// <summary>
/// Gets the backing memory for a given GPU virtual address.
/// </summary>
/// <param name="va">GPU virtual address to get the backing memory from</param>
/// <returns>The backing memory for the specified GPU virtual address</returns>
internal PhysicalMemory GetBackingMemory(ulong va)
{
return Physical;
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return GetOwnPhysicalMemory();
}
return _physicalMemoryList[UnpackPIndexFromPte(pte)];
}
/// <summary>
/// Gets the backing memory that is owned by this GPU memory manager.
/// </summary>
/// <returns>The backing memory owned by this memory manager</returns>
private PhysicalMemory GetOwnPhysicalMemory()
{
return _physicalMemoryList[0];
}
/// <summary>
/// Gets the index for a given physical memory on the list, adding it to the list if needed.
/// </summary>
/// <param name="physicalMemory">Physical memory to get the index from</param>
/// <returns>The index of the physical memory on the list</returns>
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;
}
/// <summary>
@ -698,6 +816,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
return Math.Min(maxSize, va - startVa);
}
/// <summary>
/// Translates a GPU virtual address to a CPU virtual address and the associated physical memory.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <returns>CPU virtual address with the physical memory, or <see cref="PteUnmapped"/> if unmapped</returns>
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));
}
/// <summary>
/// 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.
/// </summary>
/// <param name="pa">Physical address</param>
/// <param name="pIndex">Index of the physical memory on the list</param>
/// <param name="kind">Kind</param>
/// <returns>Page table entry</returns>
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);
}
/// <summary>
@ -783,6 +924,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return (PteKind)(pte >> 56);
}
/// <summary>
/// Unpacks the physical memory index in the list from a page table entry.
/// </summary>
/// <param name="pte">Page table entry</param>
/// <returns>Physical memory index</returns>
private static byte UnpackPIndexFromPte(ulong pte)
{
return (byte)(pte >> 48);
}
/// <summary>
/// Unpacks physical address from a page table entry.
/// </summary>

View file

@ -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);
}
/// <summary>
/// Reads data from physical memory, if it exists.
/// </summary>
/// <param name="physicalMemory">Physical memory to read the data from, might be null</param>
/// <param name="address">Address to read the data from</param>
/// <param name="size">Size of the data to read</param>
/// <returns>An array with the data</returns>
private static byte[] Read(PhysicalMemory physicalMemory, ulong address, int size)
{
if (size == 0 || physicalMemory == null)
{
return Array.Empty<byte>();
}
return physicalMemory.GetSpan(address, size).ToArray();
}
/// <summary>
/// Gets the index of a stage from a <see cref="ShaderStage"/>.
/// </summary>