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);
}