diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs
index bab62b952..ccaabf70f 100644
--- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs
+++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs
@@ -98,6 +98,8 @@ namespace Ryujinx.Graphics.Gpu
private Thread _gpuThread;
private bool _pendingSync;
+ private long _modifiedSequence;
+
///
/// Creates a new instance of the GPU emulation context.
///
@@ -200,6 +202,15 @@ namespace Ryujinx.Graphics.Gpu
return divided * NsToTicksFractionNumerator + errorBias;
}
+ ///
+ /// Gets a sequence number for resource modification ordering. This increments on each call.
+ ///
+ /// A sequence number for resource modification ordering
+ public long GetModifiedSequence()
+ {
+ return _modifiedSequence++;
+ }
+
///
/// Gets the value of the GPU timer.
///
diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 0427d09b2..a7af1aad7 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -1170,6 +1170,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Host GPU capabilities
/// Texture view initial layer on this texture
/// Texture view first mipmap level on this texture
+ /// Texture search flags
/// The level of compatiblilty a view with the given parameters created from this texture has
public TextureViewCompatibility IsViewCompatible(
TextureInfo info,
@@ -1178,11 +1179,12 @@ namespace Ryujinx.Graphics.Gpu.Image
int layerSize,
Capabilities caps,
out int firstLayer,
- out int firstLevel)
+ out int firstLevel,
+ TextureSearchFlags flags = TextureSearchFlags.None)
{
TextureViewCompatibility result = TextureViewCompatibility.Full;
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
+ result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps, flags));
if (result != TextureViewCompatibility.Incompatible)
{
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 49191239f..bccd3fd72 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -569,7 +569,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture texture = null;
- TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch;
+ long bestSequence = 0;
for (int index = 0; index < sameAddressOverlapsCount; index++)
{
@@ -601,17 +601,12 @@ namespace Ryujinx.Graphics.Gpu.Image
continue;
}
}
- }
- if (matchQuality == TextureMatchQuality.Perfect)
- {
- texture = overlap;
- break;
- }
- else if (matchQuality > bestQuality)
- {
- texture = overlap;
- bestQuality = matchQuality;
+ if (texture == null || overlap.Group.ModifiedSequence - bestSequence > 0)
+ {
+ texture = overlap;
+ bestSequence = overlap.Group.ModifiedSequence;
+ }
}
}
@@ -664,6 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int fullyCompatible = 0;
// Evaluate compatibility of overlaps, add temporary references
+ int preferredOverlap = -1;
for (int index = 0; index < overlapsCount; index++)
{
@@ -675,17 +671,26 @@ namespace Ryujinx.Graphics.Gpu.Image
sizeInfo.LayerSize,
_context.Capabilities,
out int firstLayer,
- out int firstLevel);
+ out int firstLevel,
+ flags);
- if (overlapCompatibility == TextureViewCompatibility.Full)
+ if (overlapCompatibility >= TextureViewCompatibility.FormatAlias)
{
if (overlap.IsView)
{
- overlapCompatibility = TextureViewCompatibility.CopyOnly;
+ overlapCompatibility = overlapCompatibility == TextureViewCompatibility.FormatAlias ?
+ TextureViewCompatibility.Incompatible :
+ TextureViewCompatibility.CopyOnly;
}
else
{
fullyCompatible++;
+
+ if (preferredOverlap == -1 || overlap.Group.ModifiedSequence - bestSequence > 0)
+ {
+ preferredOverlap = index;
+ bestSequence = overlap.Group.ModifiedSequence;
+ }
}
}
@@ -695,37 +700,50 @@ namespace Ryujinx.Graphics.Gpu.Image
// Search through the overlaps to find a compatible view and establish any copy dependencies.
- for (int index = 0; index < overlapsCount; index++)
+ if (preferredOverlap != -1)
{
- Texture overlap = _textureOverlaps[index];
- OverlapInfo oInfo = _overlapInfo[index];
+ Texture overlap = _textureOverlaps[preferredOverlap];
+ OverlapInfo oInfo = _overlapInfo[preferredOverlap];
- if (oInfo.Compatibility == TextureViewCompatibility.Full)
+ bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias;
+
+ if (!isSamplerTexture)
{
- if (!isSamplerTexture)
- {
- // If this is not a sampler texture, the size might be different from the requested size,
- // so we need to make sure the texture information has the correct size for this base texture,
- // before creating the view.
- info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
- }
+ // If this is not a sampler texture, the size might be different from the requested size,
+ // so we need to make sure the texture information has the correct size for this base texture,
+ // before creating the view.
- texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
- texture.SynchronizeMemory();
- break;
+ info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased);
}
- else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
+ else if (aliased)
{
- // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
+ // The format must be changed to match the parent.
+ info = info.CreateInfoWithFormat(overlap.Info.FormatInfo);
+ }
- texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
+ texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
+ texture.SynchronizeMemory();
+ }
+ else
+ {
+ for (int index = 0; index < overlapsCount; index++)
+ {
+ Texture overlap = _textureOverlaps[index];
+ OverlapInfo oInfo = _overlapInfo[index];
- texture.InitializeGroup(true, true, new List());
- texture.InitializeData(false, false);
+ if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
+ {
+ // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
- overlap.SynchronizeMemory();
- overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
- break;
+ texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
+
+ texture.InitializeGroup(true, true, new List());
+ texture.InitializeData(false, false);
+
+ overlap.SynchronizeMemory();
+ overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
+ break;
+ }
}
}
@@ -740,7 +758,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index];
- if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible)
+ if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible || oInfo.Compatibility == TextureViewCompatibility.FormatAlias)
{
if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
{
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index 5d8462220..85ad0bb01 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -291,22 +291,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// The minimum compatibility level of two provided view compatibility results
public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second)
{
- if (first == TextureViewCompatibility.Incompatible || second == TextureViewCompatibility.Incompatible)
- {
- return TextureViewCompatibility.Incompatible;
- }
- else if (first == TextureViewCompatibility.LayoutIncompatible || second == TextureViewCompatibility.LayoutIncompatible)
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
- else if (first == TextureViewCompatibility.CopyOnly || second == TextureViewCompatibility.CopyOnly)
- {
- return TextureViewCompatibility.CopyOnly;
- }
- else
- {
- return TextureViewCompatibility.Full;
- }
+ return (TextureViewCompatibility)Math.Min((int)first, (int)second);
}
///
@@ -628,15 +613,21 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Texture information of the texture view
/// Texture information of the texture view
/// Host GPU capabilities
+ /// Texture search flags
/// The view compatibility level of the texture formats
- public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps)
+ public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps, TextureSearchFlags flags)
{
FormatInfo lhsFormat = lhs.FormatInfo;
FormatInfo rhsFormat = rhs.FormatInfo;
if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
{
- return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
+ return FormatMatches(lhs, rhs, flags.HasFlag(TextureSearchFlags.ForSampler), flags.HasFlag(TextureSearchFlags.DepthAlias)) switch
+ {
+ TextureMatchQuality.Perfect => TextureViewCompatibility.Full,
+ TextureMatchQuality.FormatAlias => TextureViewCompatibility.FormatAlias,
+ _ => TextureViewCompatibility.Incompatible
+ };
}
if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
@@ -754,49 +745,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
}
- ///
- /// Checks if a swizzle component in two textures functionally match, taking into account if the components are defined.
- ///
- /// Texture information to compare
- /// Texture information to compare with
- /// Swizzle component for the first texture
- /// Swizzle component for the second texture
- /// Component index, starting at 0 for red
- /// True if the swizzle components functionally match, false othersize
- private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, SwizzleComponent swizzleLhs, SwizzleComponent swizzleRhs, int component)
- {
- int lhsComponents = lhs.FormatInfo.Components;
- int rhsComponents = rhs.FormatInfo.Components;
-
- if (lhsComponents == 4 && rhsComponents == 4)
- {
- return swizzleLhs == swizzleRhs;
- }
-
- // Swizzles after the number of components a format defines are "undefined".
- // We allow these to not be equal under certain circumstances.
- // This can only happen when there are less than 4 components in a format.
- // It tends to happen when float depth textures are sampled.
-
- bool lhsDefined = (swizzleLhs - SwizzleComponent.Red) < lhsComponents;
- bool rhsDefined = (swizzleRhs - SwizzleComponent.Red) < rhsComponents;
-
- if (lhsDefined == rhsDefined)
- {
- // If both are undefined, return true. Otherwise just check if they're equal.
- return lhsDefined ? swizzleLhs == swizzleRhs : true;
- }
- else
- {
- SwizzleComponent defined = lhsDefined ? swizzleLhs : swizzleRhs;
- SwizzleComponent undefined = lhsDefined ? swizzleRhs : swizzleLhs;
-
- // Undefined swizzle can be matched by a forced value (0, 1), exact equality, or expected value.
- // For example, R___ matches R001, RGBA but not RBGA.
- return defined == undefined || defined < SwizzleComponent.Red || defined == SwizzleComponent.Red + component;
- }
- }
-
///
/// Checks if the texture shader sampling parameters of two texture informations match.
///
@@ -806,10 +754,10 @@ namespace Ryujinx.Graphics.Gpu.Image
public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs)
{
return lhs.DepthStencilMode == rhs.DepthStencilMode &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleR, rhs.SwizzleR, 0) &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleG, rhs.SwizzleG, 1) &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleB, rhs.SwizzleB, 2) &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleA, rhs.SwizzleA, 3);
+ lhs.SwizzleR == rhs.SwizzleR &&
+ lhs.SwizzleG == rhs.SwizzleG &&
+ lhs.SwizzleB == rhs.SwizzleB &&
+ lhs.SwizzleA == rhs.SwizzleA;
}
///
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
index b36b16e94..2fa1e79e5 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
@@ -68,6 +68,11 @@ namespace Ryujinx.Graphics.Gpu.Image
///
public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
+ ///
+ /// Number indicating the order this texture group was modified relative to others.
+ ///
+ public long ModifiedSequence { get; private set; }
+
private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
@@ -664,6 +669,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// The texture that has been modified
public void SignalModified(Texture texture)
{
+ ModifiedSequence = _context.GetModifiedSequence();
+
ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
@@ -684,6 +691,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// True if this texture is being bound, false if unbound
public void SignalModifying(Texture texture, bool bound)
{
+ ModifiedSequence = _context.GetModifiedSequence();
+
ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
index a7ee12bcc..1994d2263 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
@@ -300,8 +300,9 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// The parent texture
/// The first level of the texture view
+ /// True if the parent format should be inherited
/// The adjusted texture information with the new size
- public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel)
+ public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel, bool parentFormat)
{
// When the texture is used as view of another texture, we must
// ensure that the sizes are valid, otherwise data uploads would fail
@@ -370,7 +371,36 @@ namespace Ryujinx.Graphics.Gpu.Image
GobBlocksInZ,
GobBlocksInTileX,
target,
- FormatInfo,
+ parentFormat ? parent.Info.FormatInfo : FormatInfo,
+ DepthStencilMode,
+ SwizzleR,
+ SwizzleG,
+ SwizzleB,
+ SwizzleA);
+ }
+
+ ///
+ /// Creates texture information for a given format and this information.
+ ///
+ /// Format for the new texture info
+ /// New info with the specified format
+ public TextureInfo CreateInfoWithFormat(FormatInfo formatInfo)
+ {
+ return new TextureInfo(
+ GpuAddress,
+ Width,
+ Height,
+ DepthOrLayers,
+ Levels,
+ SamplesInX,
+ SamplesInY,
+ Stride,
+ IsLinear,
+ GobBlocksInY,
+ GobBlocksInZ,
+ GobBlocksInTileX,
+ Target,
+ formatInfo,
DepthStencilMode,
SwizzleR,
SwizzleG,
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs
index b89936ebd..dfa688c45 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs
@@ -9,6 +9,7 @@
Incompatible = 0,
LayoutIncompatible,
CopyOnly,
+ FormatAlias,
Full
}
}