mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-01-12 21:59:12 +00:00
Delete PhysicalMemory field and replace it with a physical memory list, implement MapForeign for mapping memory from another process
This commit is contained in:
parent
640ea94cd8
commit
4d3f5e3818
8 changed files with 261 additions and 97 deletions
|
@ -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 &&
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue