aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2022-01-09 16:28:48 +0000
committerGitHub <noreply@github.com>2022-01-09 13:28:48 -0300
commitcda659955ced1b16839cdd1e7fea1ef6f8d99041 (patch)
tree41d0d7a705f9feb7b43c9e5bac14dafee7878220 /Ryujinx.Graphics.Gpu/Image/TextureCache.cs
parent4864648e727c6f526e3b65478f222c15468f6074 (diff)
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
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/TextureCache.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCache.cs113
1 files changed, 78 insertions, 35 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 1aa09b90..fab38e14 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -7,8 +7,10 @@ using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
+using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;
+using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
@@ -72,14 +74,26 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture[] overlaps = new Texture[10];
int overlapCount;
+ MultiRange unmapped;
+
+ try
+ {
+ unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
+ }
+ catch (InvalidMemoryRegionException)
+ {
+ // This event fires on Map in case any mappings are overwritten. In that case, there may not be an existing mapping.
+ return;
+ }
+
lock (_textures)
{
- overlapCount = _textures.FindOverlaps(((MemoryManager)sender).Translate(e.Address), e.Size, ref overlaps);
+ overlapCount = _textures.FindOverlaps(unmapped, ref overlaps);
}
for (int i = 0; i < overlapCount; i++)
{
- overlaps[i].Unmapped();
+ overlaps[i].Unmapped(unmapped);
}
}
@@ -494,12 +508,12 @@ namespace Ryujinx.Graphics.Gpu.Image
int fullyCompatible = 0;
- // Evaluate compatibility of overlaps
+ // Evaluate compatibility of overlaps, add temporary references
for (int index = 0; index < overlapsCount; index++)
{
Texture overlap = _textureOverlaps[index];
- TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(info, range.Value, sizeInfo.LayerSize, out int firstLayer, out int firstLevel);
+ TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(info, range.Value, sizeInfo.LayerSize, _context.Capabilities, out int firstLayer, out int firstLevel);
if (overlapCompatibility == TextureViewCompatibility.Full)
{
@@ -514,6 +528,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
_overlapInfo[index] = new OverlapInfo(overlapCompatibility, firstLayer, firstLevel);
+ overlap.IncrementReferenceCount();
}
// Search through the overlaps to find a compatible view and establish any copy dependencies.
@@ -544,7 +559,8 @@ namespace Ryujinx.Graphics.Gpu.Image
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
- texture.InitializeGroup(true, true);
+
+ texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>());
texture.InitializeData(false, false);
overlap.SynchronizeMemory();
@@ -564,7 +580,14 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index];
- if (oInfo.Compatibility != TextureViewCompatibility.Incompatible && overlap.Group != texture.Group)
+ if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible)
+ {
+ if (!overlap.IsView && texture.DataOverlaps(overlap))
+ {
+ texture.Group.RegisterIncompatibleOverlap(new TextureIncompatibleOverlap(overlap.Group, oInfo.Compatibility), true);
+ }
+ }
+ else if (overlap.Group != texture.Group)
{
overlap.SynchronizeMemory();
overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
@@ -591,78 +614,82 @@ namespace Ryujinx.Graphics.Gpu.Image
bool hasLayerViews = false;
bool hasMipViews = false;
+ var incompatibleOverlaps = new List<TextureIncompatibleOverlap>();
+
for (int index = 0; index < overlapsCount; index++)
{
Texture overlap = _textureOverlaps[index];
bool overlapInCache = overlap.CacheNode != null;
- TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Range, overlap.LayerSize, out int firstLayer, out int firstLevel);
+ TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Range, overlap.LayerSize, _context.Capabilities, out int firstLayer, out int firstLevel);
if (overlap.IsView && compatibility == TextureViewCompatibility.Full)
{
compatibility = TextureViewCompatibility.CopyOnly;
}
- if (compatibility != TextureViewCompatibility.Incompatible)
+ if (compatibility > TextureViewCompatibility.LayoutIncompatible)
{
+ _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
+ _textureOverlaps[index] = _textureOverlaps[viewCompatible];
+ _textureOverlaps[viewCompatible] = overlap;
+
if (compatibility == TextureViewCompatibility.Full)
{
- if (viewCompatible == fullyCompatible)
- {
- _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
- _textureOverlaps[viewCompatible++] = overlap;
- }
- else
+ if (viewCompatible != fullyCompatible)
{
// Swap overlaps so that the fully compatible views have priority.
_overlapInfo[viewCompatible] = _overlapInfo[fullyCompatible];
- _textureOverlaps[viewCompatible++] = _textureOverlaps[fullyCompatible];
+ _textureOverlaps[viewCompatible] = _textureOverlaps[fullyCompatible];
_overlapInfo[fullyCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
_textureOverlaps[fullyCompatible] = overlap;
}
+
fullyCompatible++;
}
- else
- {
- _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
- _textureOverlaps[viewCompatible++] = overlap;
- }
+
+ viewCompatible++;
hasLayerViews |= overlap.Info.GetSlices() < texture.Info.GetSlices();
hasMipViews |= overlap.Info.Levels < texture.Info.Levels;
}
else
{
+ bool dataOverlaps = texture.DataOverlaps(overlap);
+
+ if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group))
+ {
+ incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
+ }
+
bool removeOverlap;
bool modified = overlap.CheckModified(false);
if (overlapInCache || !setData)
{
- if (info.GobBlocksInZ > 1 && info.GobBlocksInZ == overlap.Info.GobBlocksInZ)
+ if (!dataOverlaps)
{
- // Allow overlapping slices of 3D textures. Could be improved in future by making sure the textures don't overlap.
+ // Allow textures to overlap if their data does not actually overlap.
+ // This typically happens when mip level subranges of a layered texture are used. (each texture fills the gaps of the others)
continue;
}
- if (!texture.DataOverlaps(overlap))
+ if (info.GobBlocksInZ > 1 && info.GobBlocksInZ == overlap.Info.GobBlocksInZ)
{
- // Allow textures to overlap if their data does not actually overlap.
- // This typically happens when mip level subranges of a layered texture are used. (each texture fills the gaps of the others)
+ // Allow overlapping slices of 3D textures. Could be improved in future by making sure the textures don't overlap.
continue;
}
// The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
- // If the texture cannot be entirely contained in the new address space, and one of its view children is compatible with us,
- // it must be flushed before removal, so that the data is not lost.
+ // The texture group will obtain copy dependencies for any subresources that are compatible between the two textures,
+ // but sometimes its data must be flushed regardless.
// If the texture was modified since its last use, then that data is probably meant to go into this texture.
// If the data has been modified by the CPU, then it also shouldn't be flushed.
- bool viewCompatibleChild = overlap.HasViewCompatibleChild(texture);
-
- bool flush = overlapInCache && !modified && !texture.Range.Contains(overlap.Range) && viewCompatibleChild;
+ bool flush = overlapInCache && !modified && overlap.AlwaysFlushOnOverlap;
setData |= modified || flush;
@@ -671,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_cache.Remove(overlap, flush);
}
- removeOverlap = modified && !viewCompatibleChild;
+ removeOverlap = modified;
}
else
{
@@ -687,12 +714,14 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
- texture.InitializeGroup(hasLayerViews, hasMipViews);
+ texture.InitializeGroup(hasLayerViews, hasMipViews, incompatibleOverlaps);
// We need to synchronize before copying the old view data to the texture,
// otherwise the copied data would be overwritten by a future synchronization.
texture.InitializeData(false, setData);
+ texture.Group.InitializeOverlaps();
+
for (int index = 0; index < viewCompatible; index++)
{
Texture overlap = _textureOverlaps[index];
@@ -753,6 +782,11 @@ namespace Ryujinx.Graphics.Gpu.Image
ShrinkOverlapsBufferIfNeeded();
+ for (int i = 0; i < overlapsCount; i++)
+ {
+ _textureOverlaps[i].DecrementReferenceCount();
+ }
+
return texture;
}
@@ -824,14 +858,16 @@ namespace Ryujinx.Graphics.Gpu.Image
}
int addressMatches = _textures.FindOverlaps(address, ref _textureOverlaps);
+ Texture textureMatch = null;
for (int i = 0; i < addressMatches; i++)
{
Texture texture = _textureOverlaps[i];
FormatInfo format = texture.Info.FormatInfo;
- if (texture.Info.DepthOrLayers > 1)
+ if (texture.Info.DepthOrLayers > 1 || texture.Info.Levels > 1 || texture.Info.FormatInfo.IsCompressed)
{
+ // Don't support direct buffer copies to anything that isn't a single 2D image, uncompressed.
continue;
}
@@ -859,11 +895,18 @@ namespace Ryujinx.Graphics.Gpu.Image
if (match)
{
- return texture;
+ if (textureMatch == null)
+ {
+ textureMatch = texture;
+ }
+ else if (texture.Group != textureMatch.Group)
+ {
+ return null; // It's ambiguous which texture should match between multiple choices, so leave it up to the slow path.
+ }
}
}
- return null;
+ return textureMatch;
}
/// <summary>