Support for resources on non-contiguous GPU memory regions (#1905)

* Support for resources on non-contiguous GPU memory regions

* Implement MultiRange physical addresses, only used with a single range for now

* Actually use non-contiguous ranges

* GetPhysicalRegions fixes

* Documentation and remove Address property from TextureInfo

* Finish implementing GetWritableRegion

* Fix typo
This commit is contained in:
gdkchan 2021-01-17 15:44:34 -03:00 committed by GitHub
parent 3bad321d2b
commit c4f56c5704
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1141 additions and 167 deletions

View file

@ -1,7 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using Ryujinx.Graphics.Texture.Astc;
using Ryujinx.Memory.Range;
@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Represents a cached GPU texture.
/// </summary>
class Texture : IRange, IDisposable
class Texture : IMultiRangeItem, IDisposable
{
// How many updates we need before switching to the byte-by-byte comparison
// modification check method.
@ -95,21 +95,16 @@ namespace Ryujinx.Graphics.Gpu.Image
public event Action<Texture> Disposed;
/// <summary>
/// Start address of the texture in guest memory.
/// Physical memory ranges where the texture data is located.
/// </summary>
public ulong Address => Info.Address;
/// <summary>
/// End address of the texture in guest memory.
/// </summary>
public ulong EndAddress => Info.Address + Size;
public MultiRange Range { get; private set; }
/// <summary>
/// Texture size in bytes.
/// </summary>
public ulong Size => (ulong)_sizeInfo.TotalSize;
private CpuRegionHandle _memoryTracking;
private GpuRegionHandle _memoryTracking;
private int _referenceCount;
@ -119,6 +114,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="context">GPU context that the texture belongs to</param>
/// <param name="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</param>
/// <param name="range">Physical memory ranges where the texture data is located</param>
/// <param name="firstLayer">The first layer of the texture, or 0 if the texture has no parent</param>
/// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param>
/// <param name="scaleFactor">The floating point scale factor to initialize with</param>
@ -127,12 +123,13 @@ namespace Ryujinx.Graphics.Gpu.Image
GpuContext context,
TextureInfo info,
SizeInfo sizeInfo,
MultiRange range,
int firstLayer,
int firstLevel,
float scaleFactor,
TextureScaleMode scaleMode)
{
InitializeTexture(context, info, sizeInfo);
InitializeTexture(context, info, sizeInfo, range);
_firstLayer = firstLayer;
_firstLevel = firstLevel;
@ -149,13 +146,14 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="context">GPU context that the texture belongs to</param>
/// <param name="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</param>
/// <param name="range">Physical memory ranges where the texture data is located</param>
/// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, TextureScaleMode scaleMode)
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, MultiRange range, TextureScaleMode scaleMode)
{
ScaleFactor = 1f; // Texture is first loaded at scale 1x.
ScaleMode = scaleMode;
InitializeTexture(context, info, sizeInfo);
InitializeTexture(context, info, sizeInfo, range);
}
/// <summary>
@ -166,10 +164,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="context">GPU context that the texture belongs to</param>
/// <param name="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</param>
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
/// <param name="range">Physical memory ranges where the texture data is located</param>
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, MultiRange range)
{
_context = context;
_sizeInfo = sizeInfo;
Range = range;
SetInfo(info);
@ -186,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="withData">True if the texture is to be initialized with data</param>
public void InitializeData(bool isView, bool withData = false)
{
_memoryTracking = _context.PhysicalMemory.BeginTracking(Address, Size);
_memoryTracking = _context.PhysicalMemory.BeginTracking(Range);
if (withData)
{
@ -229,15 +229,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="info">Child texture information</param>
/// <param name="sizeInfo">Child texture size information</param>
/// <param name="range">Physical memory ranges where the texture data is located</param>
/// <param name="firstLayer">Start layer of the child texture on the parent texture</param>
/// <param name="firstLevel">Start mipmap level of the child texture on the parent texture</param>
/// <returns>The child texture</returns>
public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel)
public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, MultiRange range, int firstLayer, int firstLevel)
{
Texture texture = new Texture(
_context,
info,
sizeInfo,
range,
_firstLayer + firstLayer,
_firstLevel + firstLevel,
ScaleFactor,
@ -367,7 +369,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ChangedSize = true;
SetInfo(new TextureInfo(
Info.Address,
Info.GpuAddress,
width,
height,
depthOrLayers,
@ -554,7 +556,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_memoryTracking?.Reprotect();
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Address, (int)Size);
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Range);
IsModified = false;
@ -586,6 +588,10 @@ namespace Ryujinx.Graphics.Gpu.Image
_hasData = true;
}
/// <summary>
/// Uploads new texture data to the host GPU.
/// </summary>
/// <param name="data">New data</param>
public void SetData(ReadOnlySpan<byte> data)
{
BlacklistScale();
@ -653,7 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.Address:X} ({texInfo}).");
Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
}
data = decoded;
@ -689,15 +695,14 @@ namespace Ryujinx.Graphics.Gpu.Image
if (tracked)
{
_context.PhysicalMemory.Write(Address, GetTextureDataFromGpu(tracked));
_context.PhysicalMemory.Write(Range, GetTextureDataFromGpu(tracked));
}
else
{
_context.PhysicalMemory.WriteUntracked(Address, GetTextureDataFromGpu(tracked));
_context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(tracked));
}
}
/// <summary>
/// Flushes the texture data, to be called from an external thread.
/// The host backend must ensure that we have shared access to the resource from this thread.
@ -725,7 +730,7 @@ namespace Ryujinx.Graphics.Gpu.Image
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
}
_context.PhysicalMemory.WriteUntracked(Address, GetTextureDataFromGpu(false, texture));
_context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture));
});
}
@ -847,25 +852,23 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureMatchQuality.NoMatch;
}
return Info.Address == info.Address && Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch;
return Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch;
}
/// <summary>
/// Check if it's possible to create a view, with the given parameters, from this texture.
/// </summary>
/// <param name="info">Texture view information</param>
/// <param name="size">Texture view size</param>
/// <param name="range">Texture view physical memory ranges</param>
/// <param name="firstLayer">Texture view initial layer on this texture</param>
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
public TextureViewCompatibility IsViewCompatible(
TextureInfo info,
ulong size,
out int firstLayer,
out int firstLevel)
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, out int firstLayer, out int firstLevel)
{
int offset = Range.FindOffset(range);
// Out of range.
if (info.Address < Address || info.Address + size > EndAddress)
if (offset < 0)
{
firstLayer = 0;
firstLevel = 0;
@ -873,9 +876,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureViewCompatibility.Incompatible;
}
int offset = (int)(info.Address - Address);
if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel))
if (!_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
{
return TextureViewCompatibility.Incompatible;
}
@ -1045,17 +1046,6 @@ namespace Ryujinx.Graphics.Gpu.Image
HostTexture = hostTexture;
}
/// <summary>
/// Checks if the texture overlaps with a memory range.
/// </summary>
/// <param name="address">Start address of the range</param>
/// <param name="size">Size of the range</param>
/// <returns>True if the texture overlaps with the range, false otherwise</returns>
public bool OverlapsWith(ulong address, ulong size)
{
return Address < address + size && address < EndAddress;
}
/// <summary>
/// Determine if any of our child textures are compaible as views of the given texture.
/// </summary>
@ -1070,7 +1060,7 @@ namespace Ryujinx.Graphics.Gpu.Image
foreach (Texture view in _views)
{
if (texture.IsViewCompatible(view.Info, view.Size, out int _, out int _) != TextureViewCompatibility.Incompatible)
if (texture.IsViewCompatible(view.Info, view.Range, out _, out _) != TextureViewCompatibility.Incompatible)
{
return true;
}
@ -1153,7 +1143,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
IsModified = false; // We shouldn't flush this texture, as its memory is no longer mapped.
CpuRegionHandle tracking = _memoryTracking;
var tracking = _memoryTracking;
tracking?.Reprotect();
tracking?.RegisterAction(null);
}