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 else
{ {
var bufferCache = memoryManager.GetBackingMemory(dstGpuVa).BufferCache;
if (remap && if (remap &&
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA && _state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
_state.State.SetRemapComponentsDstY == 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)); 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> /// <summary>

View file

@ -40,10 +40,13 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
private GpuContext _context; private GpuContext _context;
private PhysicalMemory _physicalMemory;
private SizeInfo _sizeInfo; private SizeInfo _sizeInfo;
/// <summary>
/// Backing memory for the texture.
/// </summary>
public PhysicalMemory PhysicalMemory { get; private set; }
/// <summary> /// <summary>
/// Texture format. /// Texture format.
/// </summary> /// </summary>
@ -251,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image
MultiRange range) MultiRange range)
{ {
_context = context; _context = context;
_physicalMemory = physicalMemory; PhysicalMemory = physicalMemory;
_sizeInfo = sizeInfo; _sizeInfo = sizeInfo;
Range = range; Range = range;
@ -315,7 +318,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param> /// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
public void InitializeGroup(bool hasLayerViews, bool hasMipViews, List<TextureIncompatibleOverlap> incompatibleOverlaps) 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); Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
} }
@ -336,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
Texture texture = new( Texture texture = new(
_context, _context,
_physicalMemory, PhysicalMemory,
info, info,
sizeInfo, sizeInfo,
range, range,
@ -636,7 +639,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void SynchronizeFull() 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. // 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. // 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> /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
public void FlushTextureDataToGuest(bool tracked, ITexture texture = null) 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); GetTextureDataFromGpu(region.Memory.Span, tracked, texture);
} }
@ -1419,7 +1422,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Group.SignalModified(this); Group.SignalModified(this);
} }
_physicalMemory.TextureCache.Lift(this); PhysicalMemory.TextureCache.Lift(this);
} }
/// <summary> /// <summary>
@ -1440,7 +1443,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Group.SignalModifying(this, bound); Group.SignalModifying(this, bound);
} }
_physicalMemory.TextureCache.Lift(this); PhysicalMemory.TextureCache.Lift(this);
if (bound) if (bound)
{ {
@ -1528,7 +1531,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (ShortCacheEntry != null) if (ShortCacheEntry != null)
{ {
_physicalMemory.TextureCache.RemoveShortCache(this); PhysicalMemory.TextureCache.RemoveShortCache(this);
} }
} }
@ -1557,7 +1560,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_viewStorage.RemoveView(this); _viewStorage.RemoveView(this);
} }
_physicalMemory.TextureCache.RemoveTextureFromCache(this); PhysicalMemory.TextureCache.RemoveTextureFromCache(this);
} }
Debug.Assert(newRefCount >= 0); 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 // If this is called from another thread (unmapped), the short cache will
// have to remove this texture on a future tick. // have to remove this texture on a future tick.
_physicalMemory.TextureCache.RemoveShortCache(this); PhysicalMemory.TextureCache.RemoveShortCache(this);
} }
InvalidatedSequence++; InvalidatedSequence++;

View file

@ -524,8 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// Ensure that the buffer texture is using the correct buffer as storage. // 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 // 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. // 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, texture, hostTexture, bindingInfo, bindingInfo.Format, false);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
// Cache is not used for buffer texture, it must always rebind. // Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null; state.CachedTexture = null;
@ -662,8 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image
format = texture.Format; format = texture.Format;
} }
var bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; _channel.BufferManager.SetBufferTextureStorage(stage, texture, hostTexture, bindingInfo, format, true);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
// Cache is not used for buffer texture, it must always rebind. // Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null; state.CachedTexture = null;

View file

@ -490,17 +490,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
var range = binding.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore); 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. // The texture must be rebound to use the new storage if it was updated.
if (binding.IsImage) 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 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> /// </summary>
/// <param name="stage">Shader stage accessing the texture</param> /// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param> /// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer texture</param> /// <param name="hostTexture">Buffer host texture</param>
/// <param name="address">Address of the buffer in memory</param>
/// <param name="size">Size of the buffer in bytes</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param> /// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="format">Format of 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> /// <param name="isImage">Whether the binding is for an image or a sampler</param>
public void SetBufferTextureStorage( public void SetBufferTextureStorage(
ShaderStage stage, ShaderStage stage,
ITexture texture, Image.Texture texture,
BufferCache bufferCache, ITexture hostTexture,
ulong address,
ulong size,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
Format format, Format format,
bool isImage) 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> /// <summary>

View file

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

View file

@ -34,10 +34,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
public event EventHandler<UnmapEventArgs> MemoryUnmapped; public event EventHandler<UnmapEventArgs> MemoryUnmapped;
/// <summary> private readonly GpuContext _context;
/// Physical memory where the virtual memory is mapped into. private readonly List<PhysicalMemory> _physicalMemoryList;
/// </summary> private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap;
private PhysicalMemory Physical { get; }
/// <summary> /// <summary>
/// Cache of GPU counters. /// Cache of GPU counters.
@ -47,14 +46,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Creates a new instance of the GPU memory manager. /// Creates a new instance of the GPU memory manager.
/// </summary> /// </summary>
/// <param name="context">GPU context</param>
/// <param name="physicalMemory">Physical memory that this memory manager will map into</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(); CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][]; _pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler; MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.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> /// <param name="rebind">Action to be performed when the buffer cache changes</param>
internal void AttachToChannel(Action rebind) internal void AttachToChannel(Action rebind)
{ {
Physical.IncrementReferenceCount(); PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
Physical.BufferCache.NotifyBuffersModified += rebind;
Physical.BufferCache.QueuePrune(); physicalMemory.IncrementReferenceCount();
physicalMemory.BufferCache.NotifyBuffersModified += rebind;
physicalMemory.BufferCache.QueuePrune();
} }
/// <summary> /// <summary>
/// Attaches the memory manager to a new GPU channel. /// Detaches the memory manager from a GPU channel.
/// </summary> /// </summary>
/// <param name="rebind">Action that was performed when the buffer cache changed</param> /// <param name="rebind">Action that was performed when the buffer cache changed</param>
internal void DetachFromChannel(Action rebind) internal void DetachFromChannel(Action rebind)
{ {
Physical.BufferCache.NotifyBuffersModified -= rebind; PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
Physical.DecrementReferenceCount();
physicalMemory.BufferCache.NotifyBuffersModified -= rebind;
physicalMemory.DecrementReferenceCount();
} }
/// <summary> /// <summary>
@ -84,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
internal void QueuePrune() internal void QueuePrune()
{ {
Physical.BufferCache.QueuePrune(); GetOwnPhysicalMemory().BufferCache.QueuePrune();
} }
/// <summary> /// <summary>
@ -100,15 +115,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (IsContiguous(va, size)) if (IsContiguous(va, size))
{ {
ulong address = Translate(va); (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
if (tracked) if (tracked)
{ {
return Physical.ReadTracked<T>(address); return physicalMemory.ReadTracked<T>(address);
} }
else else
{ {
return Physical.Read<T>(address); return physicalMemory.Read<T>(address);
} }
} }
else else
@ -132,7 +147,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (IsContiguous(va, size)) if (IsContiguous(va, size))
{ {
return Physical.GetSpan(Translate(va), size, tracked); (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetSpan(address, size, tracked);
} }
else else
{ {
@ -157,7 +174,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
bool isContiguous = true; bool isContiguous = true;
int mappedSize; int mappedSize;
if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va))) if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va))
{ {
ulong endVa = va + (ulong)size; ulong endVa = va + (ulong)size;
ulong endVaAligned = (endVa + PageMask) & ~PageMask; ulong endVaAligned = (endVa + PageMask) & ~PageMask;
@ -170,7 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong nextVa = currentVa + PageSize; ulong nextVa = currentVa + PageSize;
ulong nextPa = Translate(nextVa); ulong nextPa = Translate(nextVa);
if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa)) if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa))
{ {
break; break;
} }
@ -199,7 +216,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (isContiguous) if (isContiguous)
{ {
return Physical.GetSpan(Translate(va), mappedSize, tracked); (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetSpan(address, mappedSize, tracked);
} }
else 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> /// <summary>
/// Reads data from a possibly non-contiguous region of GPU mapped memory. /// Reads data from a possibly non-contiguous region of GPU mapped memory.
/// </summary> /// </summary>
@ -228,22 +264,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0) if ((va & PageMask) != 0)
{ {
ulong pa = Translate(va); (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); 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; offset += size;
} }
for (; offset < data.Length; 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); 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)) if (IsContiguous(va, size))
{ {
return Physical.GetWritableRegion(Translate(va), size, tracked); (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetWritableRegion(address, size, tracked);
} }
else else
{ {
@ -288,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data) public void Write(ulong va, ReadOnlySpan<byte> data)
{ {
WriteImpl(va, data, Physical.Write); WriteImpl(va, data, (physical, va, data) => physical.Write(va, data));
} }
/// <summary> /// <summary>
@ -298,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data) public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
{ {
WriteImpl(va, data, Physical.WriteTrackedResource); WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data));
} }
/// <summary> /// <summary>
@ -308,10 +346,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data) 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> /// <summary>
/// Writes data to possibly non-contiguous GPU mapped memory. /// Writes data to possibly non-contiguous GPU mapped memory.
@ -323,7 +361,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (IsContiguous(va, data.Length)) if (IsContiguous(va, data.Length))
{ {
writeCallback(Translate(va), data); (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
writeCallback(physicalMemory, address, data);
} }
else else
{ {
@ -331,22 +371,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0) if ((va & PageMask) != 0)
{ {
ulong pa = Translate(va); (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
writeCallback(pa, data[..size]); writeCallback(physicalMemory, pa, data[..size]);
offset += size; offset += size;
} }
for (; offset < data.Length; 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); 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)) if (IsContiguous(va, data.Length))
{ {
Physical.Write(Translate(va), data); (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
physicalMemory.Write(address, data);
} }
else else
{ {
@ -368,13 +410,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0) if ((va & PageMask) != 0)
{ {
ulong pa = Translate(va); (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); 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; offset += size;
@ -382,13 +424,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (; offset < data.Length; 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); 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="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at 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) 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) lock (_pageTable)
{ {
UnmapEventArgs e = new(va, size); UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e); MemoryUnmapped?.Invoke(this, e);
byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0;
for (ulong offset = 0; offset < size; offset += PageSize) 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); RunRemapActions(e);
@ -480,12 +558,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int page = 0; page < pages - 1; page++) 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; return false;
} }
if (Translate(va) + PageSize != Translate(va + PageSize)) if (GetPte(va) + PageSize != nextPte)
{ {
return false; return false;
} }
@ -592,9 +672,47 @@ namespace Ryujinx.Graphics.Gpu.Memory
return true; 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) 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> /// <summary>
@ -698,6 +816,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
return Math.Min(maxSize, va - startVa); 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> /// <summary>
/// Gets the kind of a given memory page. /// 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. /// 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. /// Creates a page table entry from a physical address and kind.
/// </summary> /// </summary>
/// <param name="pa">Physical address</param> /// <param name="pa">Physical address</param>
/// <param name="pIndex">Index of the physical memory on the list</param>
/// <param name="kind">Kind</param> /// <param name="kind">Kind</param>
/// <returns>Page table entry</returns> /// <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> /// <summary>
@ -783,6 +924,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return (PteKind)(pte >> 56); 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> /// <summary>
/// Unpacks physical address from a page table entry. /// Unpacks physical address from a page table entry.
/// </summary> /// </summary>

View file

@ -730,8 +730,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
byte[] cb1DataA = physical.GetSpan(cb1DataAddress, vertexA.Cb1DataSize).ToArray(); byte[] cb1DataA = Read(physical, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = physical.GetSpan(cb1DataAddress, currentStage.Cb1DataSize).ToArray(); byte[] cb1DataB = Read(physical, cb1DataAddress, currentStage.Cb1DataSize);
ShaderDumpPaths pathsA = default; ShaderDumpPaths pathsA = default;
ShaderDumpPaths pathsB = default; ShaderDumpPaths pathsB = default;
@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
? channel.BufferManager.GetComputeUniformBufferAddress(1) ? channel.BufferManager.GetComputeUniformBufferAddress(1)
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 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(); code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default; 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); 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> /// <summary>
/// Gets the index of a stage from a <see cref="ShaderStage"/>. /// Gets the index of a stage from a <see cref="ShaderStage"/>.
/// </summary> /// </summary>