Texture Sync, incompatible overlap handling, data flush improvements. (#2971)
* Initial test for texture sync * WIP new texture flushing setup * Improve rules for incompatible overlaps Fixes a lot of issues with Unreal Engine games. Still a few minor issues (some caused by dma fast path?) Needs docs and cleanup. * Cleanup, improvements Improve rules for fast DMA * Small tweak to group together flushes of overlapping handles. * Fixes, flush overlapping texture data for ASTC and BC4/5 compressed textures. Fixes the new Life is Strange game. * Flush overlaps before init data, fix 3d texture size/overlap stuff * Fix 3D Textures, faster single layer flush Note: nosy people can no longer merge this with Vulkan. (unless they are nosy enough to implement the new backend methods) * Remove unused method * Minor cleanup * More cleanup * Use the More Fun and Hopefully No Driver Bugs method for getting compressed tex too This one's for metro * Address feedback, ASTC+ETC to FormatClass * Change offset to use Span slice rather than IntPtr Add * Fix this too
This commit is contained in:
parent
4864648e72
commit
cda659955c
26 changed files with 1453 additions and 329 deletions
|
@ -72,11 +72,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
public TextureGroup Group { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set when a texture has been modified by the Host GPU since it was last flushed.
|
||||
/// </summary>
|
||||
public bool IsModified { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set when a texture has been changed size. This indicates that it may need to be
|
||||
/// changed again when obtained as a sampler.
|
||||
|
@ -89,6 +84,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
public bool ChangedMapping { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the data for this texture must always be flushed when an overlap appears.
|
||||
/// This is useful if SetData is called directly on this texture, but the data is meant for a future texture.
|
||||
/// </summary>
|
||||
public bool AlwaysFlushOnOverlap { get; private set; }
|
||||
|
||||
private int _depth;
|
||||
private int _layers;
|
||||
public int FirstLayer { get; private set; }
|
||||
|
@ -99,6 +100,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
private int _updateCount;
|
||||
private byte[] _currentData;
|
||||
|
||||
private bool _modifiedStale = true;
|
||||
|
||||
private ITexture _arrayViewTexture;
|
||||
private Target _arrayViewTarget;
|
||||
|
||||
|
@ -241,6 +244,8 @@ 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)
|
||||
{
|
||||
withData |= Group != null && Group.FlushIncompatibleOverlapsIfNeeded();
|
||||
|
||||
if (withData)
|
||||
{
|
||||
Debug.Assert(!isView);
|
||||
|
@ -280,9 +285,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
/// <param name="hasLayerViews">True if the texture will have layer views</param>
|
||||
/// <param name="hasMipViews">True if the texture will have mip views</param>
|
||||
public void InitializeGroup(bool hasLayerViews, bool hasMipViews)
|
||||
/// <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);
|
||||
Group = new TextureGroup(_context, _physicalMemory, this, incompatibleOverlaps);
|
||||
|
||||
Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
|
||||
}
|
||||
|
@ -657,6 +663,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
_dirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signal that the modified state is dirty, indicating that the texture group should be notified when it changes.
|
||||
/// </summary>
|
||||
public void SignalModifiedDirty()
|
||||
{
|
||||
_modifiedStale = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fully synchronizes guest and host memory.
|
||||
/// This will replace the entire texture with the data present in guest memory.
|
||||
|
@ -670,8 +684,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
|
||||
|
||||
IsModified = false;
|
||||
|
||||
// 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.
|
||||
// This does a byte-by-byte check and skips the update if the data is equal in this case.
|
||||
|
@ -710,7 +722,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
Group.CheckDirty(this, true);
|
||||
|
||||
IsModified = false;
|
||||
AlwaysFlushOnOverlap = true;
|
||||
|
||||
HostTexture.SetData(data);
|
||||
|
||||
|
@ -738,15 +750,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// Converts texture data to a format and layout that is supported by the host GPU.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to be converted</param>
|
||||
/// <param name="level">Mip level to convert</param>
|
||||
/// <param name="single">True to convert a single slice</param>
|
||||
/// <returns>Converted data</returns>
|
||||
public ReadOnlySpan<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||
{
|
||||
int width = Info.Width;
|
||||
int height = Info.Height;
|
||||
|
||||
int depth = single ? 1 : _depth;
|
||||
int depth = _depth;
|
||||
int layers = single ? 1 : _layers;
|
||||
int levels = single ? 1 : Info.Levels;
|
||||
int levels = single ? 1 : (Info.Levels - level);
|
||||
|
||||
width = Math.Max(width >> level, 1);
|
||||
height = Math.Max(height >> level, 1);
|
||||
|
@ -770,6 +784,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
width,
|
||||
height,
|
||||
depth,
|
||||
single ? 1 : depth,
|
||||
levels,
|
||||
layers,
|
||||
Info.FormatInfo.BlockWidth,
|
||||
|
@ -821,6 +836,65 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts texture data from a format and layout that is supported by the host GPU, back into the intended format on the guest GPU.
|
||||
/// </summary>
|
||||
/// <param name="output">Optional output span to convert into</param>
|
||||
/// <param name="data">Data to be converted</param>
|
||||
/// <param name="level">Mip level to convert</param>
|
||||
/// <param name="single">True to convert a single slice</param>
|
||||
/// <returns>Converted data</returns>
|
||||
public ReadOnlySpan<byte> ConvertFromHostCompatibleFormat(Span<byte> output, ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||
{
|
||||
if (Target != Target.TextureBuffer)
|
||||
{
|
||||
int width = Info.Width;
|
||||
int height = Info.Height;
|
||||
|
||||
int depth = _depth;
|
||||
int layers = single ? 1 : _layers;
|
||||
int levels = single ? 1 : (Info.Levels - level);
|
||||
|
||||
width = Math.Max(width >> level, 1);
|
||||
height = Math.Max(height >> level, 1);
|
||||
depth = Math.Max(depth >> level, 1);
|
||||
|
||||
if (Info.IsLinear)
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearToLinearStrided(
|
||||
output,
|
||||
Info.Width,
|
||||
Info.Height,
|
||||
Info.FormatInfo.BlockWidth,
|
||||
Info.FormatInfo.BlockHeight,
|
||||
Info.Stride,
|
||||
Info.FormatInfo.BytesPerPixel,
|
||||
data);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearToBlockLinear(
|
||||
output,
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
single ? 1 : depth,
|
||||
levels,
|
||||
layers,
|
||||
Info.FormatInfo.BlockWidth,
|
||||
Info.FormatInfo.BlockHeight,
|
||||
Info.FormatInfo.BytesPerPixel,
|
||||
Info.GobBlocksInY,
|
||||
Info.GobBlocksInZ,
|
||||
Info.GobBlocksInTileX,
|
||||
_sizeInfo,
|
||||
data);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the texture data.
|
||||
/// This causes the texture data to be written back to guest memory.
|
||||
|
@ -830,56 +904,48 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// This may cause data corruption if the memory is already being used for something else on the CPU side.
|
||||
/// </summary>
|
||||
/// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
|
||||
public void Flush(bool tracked = true)
|
||||
/// <returns>True if data was flushed, false otherwise</returns>
|
||||
public bool FlushModified(bool tracked = true)
|
||||
{
|
||||
IsModified = false;
|
||||
if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities))
|
||||
{
|
||||
return; // Flushing this format is not supported, as it may have been converted to another host format.
|
||||
}
|
||||
|
||||
FlushTextureDataToGuest(tracked);
|
||||
return TextureCompatibility.CanTextureFlush(Info, _context.Capabilities) && Group.FlushModified(this, 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.
|
||||
/// This is used when flushing from memory access handlers.
|
||||
/// Flushes the texture data.
|
||||
/// This causes the texture data to be written back to guest memory.
|
||||
/// If the texture was written by the GPU, this includes all modification made by the GPU
|
||||
/// up to this point.
|
||||
/// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
|
||||
/// This may cause data corruption if the memory is already being used for something else on the CPU side.
|
||||
/// </summary>
|
||||
public void ExternalFlush(ulong address, ulong size)
|
||||
/// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
|
||||
public void Flush(bool tracked)
|
||||
{
|
||||
if (!IsModified)
|
||||
if (TextureCompatibility.CanTextureFlush(Info, _context.Capabilities))
|
||||
{
|
||||
return;
|
||||
FlushTextureDataToGuest(tracked);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a host texture to use for flushing the texture, at 1x resolution.
|
||||
/// If the HostTexture is already at 1x resolution, it is returned directly.
|
||||
/// </summary>
|
||||
/// <returns>The host texture to flush</returns>
|
||||
public ITexture GetFlushTexture()
|
||||
{
|
||||
ITexture texture = HostTexture;
|
||||
if (ScaleFactor != 1f)
|
||||
{
|
||||
// If needed, create a texture to flush back to host at 1x scale.
|
||||
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
|
||||
}
|
||||
|
||||
_context.Renderer.BackgroundContextAction(() =>
|
||||
{
|
||||
IsModified = false;
|
||||
if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities))
|
||||
{
|
||||
return; // Flushing this format is not supported, as it may have been converted to another host format.
|
||||
}
|
||||
|
||||
if (Info.Target == Target.Texture2DMultisample ||
|
||||
Info.Target == Target.Texture2DMultisampleArray)
|
||||
{
|
||||
return; // Flushing multisample textures is not supported, the host does not allow getting their data.
|
||||
}
|
||||
|
||||
ITexture texture = HostTexture;
|
||||
if (ScaleFactor != 1f)
|
||||
{
|
||||
// If needed, create a texture to flush back to host at 1x scale.
|
||||
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
|
||||
}
|
||||
|
||||
FlushTextureDataToGuest(false, texture);
|
||||
});
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets data from the host GPU, and flushes it to guest memory.
|
||||
/// Gets data from the host GPU, and flushes it all to guest memory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method should be used to retrieve data that was modified by the host GPU.
|
||||
|
@ -888,28 +954,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </remarks>
|
||||
/// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
|
||||
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
||||
private void FlushTextureDataToGuest(bool tracked, ITexture texture = null)
|
||||
public void FlushTextureDataToGuest(bool tracked, ITexture texture = null)
|
||||
{
|
||||
if (Range.Count == 1)
|
||||
{
|
||||
MemoryRange subrange = Range.GetSubRange(0);
|
||||
using WritableRegion region = _physicalMemory.GetWritableRegion(Range, tracked);
|
||||
|
||||
using (WritableRegion region = _physicalMemory.GetWritableRegion(subrange.Address, (int)subrange.Size, tracked))
|
||||
{
|
||||
GetTextureDataFromGpu(region.Memory.Span, tracked, texture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tracked)
|
||||
{
|
||||
_physicalMemory.Write(Range, GetTextureDataFromGpu(Span<byte>.Empty, true, texture));
|
||||
}
|
||||
else
|
||||
{
|
||||
_physicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(Span<byte>.Empty, false, texture));
|
||||
}
|
||||
}
|
||||
GetTextureDataFromGpu(region.Memory.Span, tracked, texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -951,40 +1000,54 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
if (Target != Target.TextureBuffer)
|
||||
data = ConvertFromHostCompatibleFormat(output, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets data from the host GPU for a single slice.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method should be used to retrieve data that was modified by the host GPU.
|
||||
/// This is not cheap, avoid doing that unless strictly needed.
|
||||
/// </remarks>
|
||||
/// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
|
||||
/// <param name="layer">The layer of the texture to flush</param>
|
||||
/// <param name="level">The level of the texture to flush</param>
|
||||
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
||||
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
||||
/// <returns>The span containing the texture data</returns>
|
||||
public ReadOnlySpan<byte> GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
|
||||
{
|
||||
ReadOnlySpan<byte> data;
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
if (Info.IsLinear)
|
||||
data = texture.GetData(layer, level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (blacklist)
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearToLinearStrided(
|
||||
output,
|
||||
Info.Width,
|
||||
Info.Height,
|
||||
Info.FormatInfo.BlockWidth,
|
||||
Info.FormatInfo.BlockHeight,
|
||||
Info.Stride,
|
||||
Info.FormatInfo.BytesPerPixel,
|
||||
data);
|
||||
BlacklistScale();
|
||||
data = HostTexture.GetData(layer, level);
|
||||
}
|
||||
else if (ScaleFactor != 1f)
|
||||
{
|
||||
float scale = ScaleFactor;
|
||||
SetScale(1f);
|
||||
data = HostTexture.GetData(layer, level);
|
||||
SetScale(scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearToBlockLinear(
|
||||
output,
|
||||
Info.Width,
|
||||
Info.Height,
|
||||
_depth,
|
||||
Info.Levels,
|
||||
_layers,
|
||||
Info.FormatInfo.BlockWidth,
|
||||
Info.FormatInfo.BlockHeight,
|
||||
Info.FormatInfo.BytesPerPixel,
|
||||
Info.GobBlocksInY,
|
||||
Info.GobBlocksInZ,
|
||||
Info.GobBlocksInTileX,
|
||||
_sizeInfo,
|
||||
data);
|
||||
data = HostTexture.GetData(layer, level);
|
||||
}
|
||||
}
|
||||
|
||||
data = ConvertFromHostCompatibleFormat(output, data, level, true);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -1043,55 +1106,64 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
/// <param name="info">Texture view information</param>
|
||||
/// <param name="range">Texture view physical memory ranges</param>
|
||||
/// <param name="layerSize">Layer size on the given texture</param>
|
||||
/// <param name="caps">Host GPU capabilities</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, MultiRange range, int layerSize, out int firstLayer, out int firstLevel)
|
||||
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
|
||||
{
|
||||
int offset = Range.FindOffset(range);
|
||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||
|
||||
// Out of range.
|
||||
if (offset < 0)
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
|
||||
if (result != TextureViewCompatibility.Incompatible)
|
||||
{
|
||||
firstLayer = 0;
|
||||
firstLevel = 0;
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
|
||||
|
||||
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
|
||||
{
|
||||
// AMD and Intel have a bug where the view format is always ignored;
|
||||
// they use the parent format instead.
|
||||
// Create a copy dependency to avoid this issue.
|
||||
|
||||
result = TextureViewCompatibility.CopyOnly;
|
||||
}
|
||||
|
||||
if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)
|
||||
{
|
||||
result = TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
}
|
||||
|
||||
firstLayer = 0;
|
||||
firstLevel = 0;
|
||||
|
||||
if (result == TextureViewCompatibility.Incompatible)
|
||||
{
|
||||
return TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
|
||||
if (!_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
|
||||
int offset = Range.FindOffset(range);
|
||||
|
||||
if (offset < 0 || !_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
|
||||
{
|
||||
return TextureViewCompatibility.Incompatible;
|
||||
return TextureViewCompatibility.LayoutIncompatible;
|
||||
}
|
||||
|
||||
if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
|
||||
{
|
||||
return TextureViewCompatibility.Incompatible;
|
||||
return TextureViewCompatibility.LayoutIncompatible;
|
||||
}
|
||||
|
||||
if (info.GetSlices() > 1 && LayerSize != layerSize)
|
||||
{
|
||||
return TextureViewCompatibility.Incompatible;
|
||||
return TextureViewCompatibility.LayoutIncompatible;
|
||||
}
|
||||
|
||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info));
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel));
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
|
||||
|
||||
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
|
||||
{
|
||||
// AMD and Intel have a bug where the view format is always ignored;
|
||||
// they use the parent format instead.
|
||||
// Create a copy dependency to avoid this issue.
|
||||
|
||||
result = TextureViewCompatibility.CopyOnly;
|
||||
}
|
||||
|
||||
return (Info.SamplesInX == info.SamplesInX &&
|
||||
Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1261,11 +1333,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
public void SignalModified()
|
||||
{
|
||||
bool wasModified = IsModified;
|
||||
if (!wasModified || Group.HasCopyDependencies)
|
||||
if (_modifiedStale || Group.HasCopyDependencies)
|
||||
{
|
||||
IsModified = true;
|
||||
Group.SignalModified(this, !wasModified);
|
||||
_modifiedStale = false;
|
||||
Group.SignalModified(this);
|
||||
}
|
||||
|
||||
_physicalMemory.TextureCache.Lift(this);
|
||||
|
@ -1278,12 +1349,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
|
||||
public void SignalModifying(bool bound)
|
||||
{
|
||||
bool wasModified = IsModified;
|
||||
|
||||
if (!wasModified || Group.HasCopyDependencies)
|
||||
if (_modifiedStale || Group.HasCopyDependencies)
|
||||
{
|
||||
IsModified = true;
|
||||
Group.SignalModifying(this, bound, !wasModified);
|
||||
_modifiedStale = false;
|
||||
Group.SignalModifying(this, bound);
|
||||
}
|
||||
|
||||
_physicalMemory.TextureCache.Lift(this);
|
||||
|
@ -1309,29 +1378,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
HostTexture = hostTexture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if any of our child textures are compaible as views of the given texture.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture to check against</param>
|
||||
/// <returns>True if any child is view compatible, false otherwise</returns>
|
||||
public bool HasViewCompatibleChild(Texture texture)
|
||||
{
|
||||
if (_viewStorage != this || _views.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Texture view in _views)
|
||||
{
|
||||
if (texture.IsViewCompatible(view.Info, view.Range, view.LayerSize, out _, out _) != TextureViewCompatibility.Incompatible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if any of this texture's data overlaps with another.
|
||||
/// </summary>
|
||||
|
@ -1489,11 +1535,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// Called when the memory for this texture has been unmapped.
|
||||
/// Calls are from non-gpu threads.
|
||||
/// </summary>
|
||||
public void Unmapped()
|
||||
/// <param name="unmapRange">The range of memory being unmapped</param>
|
||||
public void Unmapped(MultiRange unmapRange)
|
||||
{
|
||||
ChangedMapping = true;
|
||||
|
||||
IsModified = false; // We shouldn't flush this texture, as its memory is no longer mapped.
|
||||
if (Group.Storage == this)
|
||||
{
|
||||
Group.ClearModified(unmapRange);
|
||||
}
|
||||
|
||||
RemoveFromPools(true);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue