diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index bab62b95..ccaabf70 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 0427d09b..a7af1aad 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 49191239..bccd3fd7 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 5d846222..85ad0bb0 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 b36b16e9..2fa1e79e 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 a7ee12bc..1994d226 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 b89936eb..dfa688c4 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 } }