From 52c6a1890eb7a45702238d0ae388a0f9029f3f05 Mon Sep 17 00:00:00 2001 From: Neo Date: Fri, 28 Aug 2026 02:23:30 +0300 Subject: [PATCH] Improve "Add raw copy dependencies for incompatible textures" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial PR was made by Avan for Ryubing. This allows for Trails in the Sky 1st Chapter (and by extension Trails through Daybreak, Trails through Daybreak II, Trails Beyond the Horizon, and the future Trails in the Sky 2nd Chapter – tested via demo – to be fully playable. Initial PR caused issues on macOS devices, specifically crashing in certain games (such as Mario Kart 8 Deluxe and the aforementioned Trails games). It was reverted on the original Ryubing project (at the time of writing this description) for causing small rendering issues on The Legend of Zelda: Breath of the Wild. The rendering issues do not appear on macOS. This commit improves the original PR by eliminating said macOS crash. Trails in the Sky 1st writes its exposure/brightness value through an R32G32Float 1x1 texture and later reads the raw bits back through an R32Uint 2x1 texture mapped to the same guest memory. The two texture formats/dimensions are fully incompatible as texture views, so they end up as separate host textures, and nothing kept their contents coherent - the reader saw stale zeroed data, making the exposure calculation (and the rendered image) too dark. (This is the initial fix for the game) TextureGroup already had a raw-copy dependency mechanism intended to handle this class of alias (TextureDependency, TextureGroupHandle raw-copy plumbing), gated by CanCreateRawCopyDependency's strict same-guest-memory checks. This commit fixes three bugs that kept that mechanism from working correctly (and thus crashing Trails and other games, such as Mario Kart 8 Deluxe): (1) TextureGroup.InitializeOverlaps() and TextureGroup.RegisterIncompatibleOverlap() only forwarded overlaps to CreateCopyDependency() when compatibility was LayoutIncompatible or better, silently excluding the fully Incompatible case the raw-copy path exists for. Widened both guards to let Incompatible overlaps through so CanCreateRawCopyDependency actually gets a chance to run. (2) Once Incompatible overlaps were allowed through, CreateCopyDependency(TextureGroup, ...) could still fall back to a regular, non-raw textureCopy for such pairs, since ViewLayoutCompatible/CopySizeMatches only check byte size and were never meant to reason about fully incompatible pairs (e.g. a depth format aliasing a color format with the same byte size). On Vulkan running through MoltenVK on macOS, that non-raw copy path requires a pixel-format-reinterpreting texture view, and MoltenVK/Metal refuses to create any view onto a depth-format texture ("not castable"), aborting emulation. textureCopy is now forced false for Incompatible pairs, leaving raw copy (which already excludes depth/stencil formats) as the only route for that severity level. (3) TextureGroupHandle.Inherit() copied a handle's pending DeferredCopy to the new handle when a view was recreated, but did not copy DeferredCopyRaw alongside it. A handle that inherited a pending raw copy would then execute it through the regular (non-raw) CopyTo path once acknowledged, reinterpreting the source bytes with the wrong row layout and corrupting the image - visible as vertical flickering stripes whenever a view happened to be recreated with a raw copy still pending. DeferredCopyRaw is now carried over together with DeferredCopy. (As mentioned earlier, vertical stripes aren't present on macOS, so (3) doesn't negatively have an effect on macOS in any way). --- src/Ryujinx.Graphics.GAL/Target.cs | 21 ++++---- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 6 +-- .../Image/TextureCompatibility.cs | 19 ++----- .../Image/TextureGroup.cs | 52 ++++++++++++------- .../Image/TextureGroupHandle.cs | 1 + .../Image/TextureView.cs | 10 ++-- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 8 +-- src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 4 +- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 12 ++--- 9 files changed, 68 insertions(+), 65 deletions(-) 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); }