diff --git a/src/Ryujinx.Graphics.GAL/Target.cs b/src/Ryujinx.Graphics.GAL/Target.cs index 09a188e45..531051d02 100644 --- a/src/Ryujinx.Graphics.GAL/Target.cs +++ b/src/Ryujinx.Graphics.GAL/Target.cs @@ -16,19 +16,18 @@ namespace Ryujinx.Graphics.GAL public static class TargetExtensions { - public static bool IsMultisample(this Target target) + extension(Target target) { - return target is Target.Texture2DMultisample or Target.Texture2DMultisampleArray; - } + public bool IsMultisample => target is Target.Texture2DMultisample or Target.Texture2DMultisampleArray; - public static bool HasDepthOrLayers(this Target target) - { - return target is Target.Texture3D - or Target.Texture1DArray - or Target.Texture2DArray - or Target.Texture2DMultisampleArray - or Target.Cubemap - or Target.CubemapArray; + public bool HasDepthOrLayers => + target is + Target.Texture3D or + Target.Texture1DArray or + Target.Texture2DArray or + Target.Texture2DMultisampleArray or + Target.Cubemap or + Target.CubemapArray; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index e4303d031..02491864d 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1117,7 +1117,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if data was flushed, false otherwise public bool FlushModified(bool tracked = true) { - return TextureCompatibility.CanTextureFlush(this, _context.Capabilities) && Group.FlushModified(this, tracked); + return TextureCompatibility.CanTextureFlush(Info, _context.Capabilities) && Group.FlushModified(this, tracked); } /// @@ -1131,7 +1131,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either. public void Flush(bool tracked) { - if (TextureCompatibility.CanTextureFlush(this, _context.Capabilities)) + if (TextureCompatibility.CanTextureFlush(Info, _context.Capabilities)) { FlushTextureDataToGuest(tracked); } @@ -1336,7 +1336,7 @@ namespace Ryujinx.Graphics.Gpu.Image { result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps)); - bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample(); + bool bothMs = Info.Target.IsMultisample && info.Target.IsMultisample; if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)) { result = TextureViewCompatibility.Incompatible; diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 49621da84..3410d80c7 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -195,16 +195,6 @@ namespace Ryujinx.Graphics.Gpu.Image return true; } - /// - /// Determines whether a texture can flush its data back to guest memory. - /// - /// Texture that will have its data flushed - /// Host GPU Capabilities - /// True if the texture can flush, false otherwise - public static bool CanTextureFlush(Texture texture, in Capabilities caps) - { - return !texture.HasImportOverride() && CanTextureFlush(texture.Info, caps); - } /// /// Determines whether a texture can flush its data back to guest memory. @@ -212,14 +202,15 @@ namespace Ryujinx.Graphics.Gpu.Image /// Texture information /// Host GPU Capabilities /// True if the texture can flush, false otherwise - private static bool CanTextureFlush(TextureInfo info, in Capabilities caps) + public static bool CanTextureFlush(TextureInfo info, Capabilities caps) { - if (IsFormatHostIncompatible(info, in caps)) + if (IsFormatHostIncompatible(info, caps)) { return false; // Flushing this format is not supported, as it may have been converted to another host format. } - if (info.Target is Target.Texture2DMultisample or Target.Texture2DMultisampleArray) + if (info.Target is Target.Texture2DMultisample or + Target.Texture2DMultisampleArray) { return false; // Flushing multisample textures is not supported, the host does not allow getting their data. } @@ -400,7 +391,7 @@ namespace Ryujinx.Graphics.Gpu.Image return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible; } - else if (lhs.Target.IsMultisample() != rhs.Target.IsMultisample() && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height) + else if (lhs.Target.IsMultisample != rhs.Target.IsMultisample && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height) { // Copy between multisample and non-multisample textures with mismatching size is allowed, // as long aligned size matches. diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index bd7edc11e..18a67d9fe 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Image _allOffsets = size.AllOffsets; _sliceSizes = size.SliceSizes; - if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold) + if (Storage.Target.HasDepthOrLayers && Storage.Info.GetSlices() > GranularLayerThreshold) { _hasLayerViews = true; _hasMipViews = true; @@ -182,7 +182,11 @@ namespace Ryujinx.Graphics.Gpu.Image { foreach (TextureIncompatibleOverlap overlap in _incompatibleOverlaps) { - if (overlap.Compatibility <= TextureViewCompatibility.LayoutIncompatible) + // LayoutIncompatible and better may still use a regular texture copy dependency. + // Fully Incompatible pairs are not copy compatible in general, but may still qualify for an + // exact raw byte copy dependency (checked internally by CreateCopyDependency) when they map + // to exactly the same guest memory, such as differently typed/sized aliases of the same data. + if (overlap.Compatibility <= TextureViewCompatibility.Incompatible) { CreateCopyDependency(overlap.Group, false, overlap.Compatibility); } @@ -226,7 +230,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// /// Flushes incompatible overlaps if the storage format requires it, and they have been modified. /// This allows unsupported host formats to accept data written to format aliased textures. @@ -324,7 +327,7 @@ namespace Ryujinx.Graphics.Gpu.Image { FlushIncompatibleOverlapsIfNeeded(); - EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, _) => + EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) => { bool dirty = false; bool anyModified = false; @@ -479,7 +482,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture to synchronize dependents of public void SynchronizeDependents(Texture texture) { - EvaluateRelevantHandles(texture, (baseHandle, regionCount, _, _) => + EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) => { for (int i = 0; i < regionCount; i++) { @@ -571,7 +574,7 @@ namespace Ryujinx.Graphics.Gpu.Image tracked = tracked || ShouldFlushTriggerTracking(); bool flushed = false; - EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, _) => + EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) => { int startSlice = 0; int endSlice = 0; @@ -652,14 +655,14 @@ namespace Ryujinx.Graphics.Gpu.Image if (_flushBuffer == BufferHandle.Null) { - if (!TextureCompatibility.CanTextureFlush(Storage, _context.Capabilities)) + if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities)) { return; } bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel; - IntPtr hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0; + nint hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0; if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size)) { @@ -716,7 +719,7 @@ namespace Ryujinx.Graphics.Gpu.Image ClearIncompatibleOverlaps(texture); - EvaluateRelevantHandles(texture, (baseHandle, regionCount, _, _) => + EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) => { for (int i = 0; i < regionCount; i++) { @@ -1049,7 +1052,7 @@ namespace Ryujinx.Graphics.Gpu.Image int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel]; int size = endOffset - offset; - List result = new(); + List result = []; for (int i = 0; i < TextureRange.Count; i++) { @@ -1163,7 +1166,6 @@ namespace Ryujinx.Graphics.Gpu.Image SignalAllDirty(); } - /// /// Removes a view from the group, removing it from all overlap lists. /// @@ -1385,7 +1387,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (_is3D) { - List handlesList = new(); + List handlesList = []; for (int i = 0; i < levelHandles; i++) { @@ -1468,15 +1470,15 @@ namespace Ryujinx.Graphics.Gpu.Image // Get the location of each texture within its storage, so we can find the handles to apply the dependency to. // This can consist of multiple disjoint regions, for example if this is a mip slice of an array texture. - List<(int BaseHandle, int RegionCount)> targetRange = new(); - List<(int BaseHandle, int RegionCount)> otherRange = new(); + List<(int BaseHandle, int RegionCount)> targetRange = []; + List<(int BaseHandle, int RegionCount)> otherRange = []; - EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, _, _) => + EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split, specialData) => { targetRange.Add((baseHandle, regionCount)); return true; }, out _); - otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, _, _) => + otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split, specialData) => { otherRange.Add((baseHandle, regionCount)); return true; @@ -1601,7 +1603,15 @@ namespace Ryujinx.Graphics.Gpu.Image TextureInfo info = Storage.Info; TextureInfo otherInfo = other.Storage.Info; - bool textureCopy = TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) && + // ViewLayoutCompatible/CopySizeMatches only reason about textures with some genuine + // format relationship (LayoutIncompatible or better) - they are not aware of, and must + // never be used to justify, a plain texture-to-texture copy (which some backends + // implement via a reinterpreting view) between fully Incompatible aliases such as a + // depth format and an unrelated color format. For Incompatible pairs, only the strict + // raw byte copy dependency below (which explicitly excludes depth/stencil formats) may + // be used. + bool textureCopy = compatibility != TextureViewCompatibility.Incompatible && + TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) && TextureCompatibility.CopySizeMatches(info, otherInfo, level, otherLevel); if (textureCopy || rawCopy) @@ -1659,9 +1669,12 @@ namespace Ryujinx.Graphics.Gpu.Image { if (!_incompatibleOverlaps.Any(overlap => overlap.Group == other.Group)) { - if (copy && other.Compatibility <= TextureViewCompatibility.LayoutIncompatible) + if (copy && other.Compatibility <= TextureViewCompatibility.Incompatible) { // Any of the group's views may share compatibility, even if the parents do not fully. + // Fully Incompatible groups are also let through here, since CreateCopyDependency will + // fall back to an exact raw byte copy dependency for them when the strict requirements + // for one are met (see CanCreateRawCopyDependency). CreateCopyDependency(other.Group, false, other.Compatibility); } @@ -1763,7 +1776,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } - if (TextureCompatibility.CanTextureFlush(Storage, _context.Capabilities) && !(inBuffer && _flushBufferImported)) + if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported)) { FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture()); } @@ -1803,4 +1816,3 @@ namespace Ryujinx.Graphics.Gpu.Image } } } - diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index 7aef9775c..95eaf86e2 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -717,6 +717,7 @@ namespace Ryujinx.Graphics.Gpu.Image } DeferredCopy = old.DeferredCopy; + DeferredCopyRaw = old.DeferredCopyRaw; } } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index b045870a7..9f50205ca 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -116,8 +116,8 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - bool srcIsMultisample = Target.IsMultisample(); - bool dstIsMultisample = destinationView.Target.IsMultisample(); + bool srcIsMultisample = Target.IsMultisample; + bool dstIsMultisample = destinationView.Target.IsMultisample; if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) { @@ -172,8 +172,8 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - bool srcIsMultisample = Target.IsMultisample(); - bool dstIsMultisample = destinationView.Target.IsMultisample(); + bool srcIsMultisample = Target.IsMultisample; + bool dstIsMultisample = destinationView.Target.IsMultisample; if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) { @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.OpenGL.Image Extents2D srcRegion = new(0, 0, Width, Height); Extents2D dstRegion = new(0, 0, destinationView.Width, destinationView.Height); - if (destinationView.Target.IsMultisample()) + if (destinationView.Target.IsMultisample) { TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( Info.Target, diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 87d61c09e..f3ce52f8d 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -406,10 +406,10 @@ namespace Ryujinx.Graphics.Vulkan if (dstIsDepthOrStencil) { - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } - else if (src.Info.Target.IsMultisample()) + else if (src.Info.Target.IsMultisample) { _pipeline.SetProgram(_programColorBlitMs); } @@ -566,12 +566,12 @@ namespace Ryujinx.Graphics.Vulkan if (isDepth) { - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); } else { - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); + _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programStencilBlitMs : _programStencilBlit); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 005ef6219..6a3f030a6 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Vulkan _device = device; _info = info; - bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); + bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample; VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); uint levels = (uint)info.Levels; @@ -323,7 +323,7 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - if (format.IsImageCompatible() && (isMsImageStorageSupported || !target.IsMultisample())) + if (format.IsImageCompatible() && (isMsImageStorageSupported || !target.IsMultisample)) { usage |= ImageUsageFlags.StorageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 46e686e22..1005d93f0 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); - bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); + bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample; VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities, isMsImageStorageSupported) & storage.UsageFlags; @@ -126,7 +126,7 @@ namespace Ryujinx.Graphics.Vulkan ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit; - if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample())) + if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample)) { shaderUsage |= ImageUsageFlags.StorageBit; } @@ -225,12 +225,12 @@ namespace Ryujinx.Graphics.Vulkan Image srcImage = src.GetImage().Get(cbs).Value; Image dstImage = dst.GetImage().Get(cbs).Value; - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + if (!dst.Info.Target.IsMultisample && Info.Target.IsMultisample) { int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + else if (dst.Info.Target.IsMultisample && !Info.Target.IsMultisample) { int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); @@ -287,11 +287,11 @@ namespace Ryujinx.Graphics.Vulkan Image srcImage = src.GetImage().Get(cbs).Value; Image dstImage = dst.GetImage().Get(cbs).Value; - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) + if (!dst.Info.Target.IsMultisample && Info.Target.IsMultisample) { _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) + else if (dst.Info.Target.IsMultisample && !Info.Target.IsMultisample) { _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); }