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 .
///