mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-05 17:18:54 +02:00
Initial texture replacement implementation
Unmerged PR #5632, originally by @gdkchan * Add mod manager support - @KeatonTheBot * Remove redundant code - @KeatonTheBot * Optimize, modernize code to newer C# standards where appropriate - @KeatonTheBot Co-authored-by: KeatonTheBot <keaton@ryujinx.app>
This commit is contained in:
committed by
KeatonTheBot
co-authored by
KeatonTheBot
parent
d00bda7e53
commit
812cecd713
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public int Height { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture depth, or 1 if the texture is not a 3D texture.
|
||||
/// </summary>
|
||||
public int Depth { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Numer of texture layers, or 1 if the texture is not a array texture.
|
||||
/// </summary>
|
||||
public int Layers { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture information.
|
||||
/// </summary>
|
||||
@@ -106,11 +116,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public int InvalidatedSequence { get; private set; }
|
||||
|
||||
private int _depth;
|
||||
private int _layers;
|
||||
public int FirstLayer { get; private set; }
|
||||
public int FirstLevel { get; private set; }
|
||||
|
||||
private TextureInfoOverride? _importOverride;
|
||||
private bool _forceReimport;
|
||||
private readonly bool _forRender;
|
||||
|
||||
private bool _hasData;
|
||||
private bool _dirty = true;
|
||||
private int _updateCount;
|
||||
@@ -189,6 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param>
|
||||
/// <param name="scaleFactor">The floating point scale factor to initialize with</param>
|
||||
/// <param name="scaleMode">The scale mode to initialize with</param>
|
||||
/// <param name="forRender">Indicates that the texture will be modified by a draw or blit operation</param>
|
||||
private Texture(
|
||||
GpuContext context,
|
||||
PhysicalMemory physicalMemory,
|
||||
@@ -198,7 +211,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
int firstLayer,
|
||||
int firstLevel,
|
||||
float scaleFactor,
|
||||
TextureScaleMode scaleMode)
|
||||
TextureScaleMode scaleMode,
|
||||
bool forRender)
|
||||
{
|
||||
InitializeTexture(context, physicalMemory, info, sizeInfo, range);
|
||||
|
||||
@@ -208,6 +222,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
ScaleFactor = scaleFactor;
|
||||
ScaleMode = scaleMode;
|
||||
|
||||
_forRender = forRender;
|
||||
|
||||
InitializeData(true);
|
||||
}
|
||||
|
||||
@@ -220,17 +236,21 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="sizeInfo">Size information of the texture</param>
|
||||
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
||||
/// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
|
||||
/// <param name="forRender">Indicates that the texture will be modified by a draw or blit operation</param>
|
||||
public Texture(
|
||||
GpuContext context,
|
||||
PhysicalMemory physicalMemory,
|
||||
TextureInfo info,
|
||||
SizeInfo sizeInfo,
|
||||
MultiRange range,
|
||||
TextureScaleMode scaleMode)
|
||||
TextureScaleMode scaleMode,
|
||||
bool forRender)
|
||||
{
|
||||
ScaleFactor = 1f; // Texture is first loaded at scale 1x.
|
||||
ScaleMode = scaleMode;
|
||||
|
||||
_forRender = forRender;
|
||||
|
||||
InitializeTexture(context, physicalMemory, info, sizeInfo, range);
|
||||
}
|
||||
|
||||
@@ -278,7 +298,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
Debug.Assert(!isView);
|
||||
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor, _importOverride);
|
||||
HostTexture = _context.Renderer.CreateTexture(createInfo);
|
||||
|
||||
SynchronizeMemory(); // Load the data.
|
||||
@@ -302,7 +322,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
ScaleFactor = GraphicsConfig.ResScale;
|
||||
}
|
||||
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor, _importOverride);
|
||||
HostTexture = _context.Renderer.CreateTexture(createInfo);
|
||||
}
|
||||
}
|
||||
@@ -344,9 +364,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
FirstLayer + firstLayer,
|
||||
FirstLevel + firstLevel,
|
||||
ScaleFactor,
|
||||
ScaleMode);
|
||||
ScaleMode,
|
||||
_forRender);
|
||||
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(info, _context.Capabilities, ScaleFactor);
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(info, _context.Capabilities, ScaleFactor, null);
|
||||
texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
|
||||
|
||||
_viewStorage.AddView(texture);
|
||||
@@ -490,7 +511,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
if (storage == null)
|
||||
{
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, scale);
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, scale, _importOverride);
|
||||
storage = _context.Renderer.CreateTexture(createInfo);
|
||||
}
|
||||
|
||||
@@ -541,7 +562,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format}.");
|
||||
view.ScaleFactor = scale;
|
||||
|
||||
TextureCreateInfo viewCreateInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, scale);
|
||||
TextureCreateInfo viewCreateInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, scale, _importOverride);
|
||||
ITexture newView = HostTexture.CreateView(viewCreateInfo, view.FirstLayer - FirstLayer, view.FirstLevel - FirstLevel);
|
||||
|
||||
view.ReplaceStorage(newView);
|
||||
@@ -571,6 +592,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return Group.CheckDirty(this, consume);
|
||||
}
|
||||
|
||||
public void ForceReimport()
|
||||
{
|
||||
_forceReimport = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discards all data for this texture.
|
||||
/// This clears all dirty flags and pending copies from other textures.
|
||||
@@ -597,6 +623,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return;
|
||||
}
|
||||
|
||||
if (_forceReimport)
|
||||
{
|
||||
SynchronizeFull();
|
||||
|
||||
_forceReimport = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_dirty)
|
||||
{
|
||||
return;
|
||||
@@ -643,7 +678,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
// The decompression is slow, so we want to avoid it as much as possible.
|
||||
// This does a byte-by-byte check and skips the update if the data is equal in this case.
|
||||
// This improves the speed on applications that overwrites ASTC data without changing anything.
|
||||
if (Info.FormatInfo.Format.IsAstc() && !_context.Capabilities.SupportsAstcCompression)
|
||||
if (Info.FormatInfo.Format.IsAstc() && !_context.Capabilities.SupportsAstcCompression && !_forceReimport)
|
||||
{
|
||||
if (_updateCount < ByteComparisonSwitchThreshold)
|
||||
{
|
||||
@@ -688,6 +723,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
BlacklistScale();
|
||||
|
||||
if (HasImportOverride())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Group.CheckDirty(this, true);
|
||||
|
||||
AlwaysFlushOnOverlap = true;
|
||||
@@ -725,6 +765,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
BlacklistScale();
|
||||
|
||||
if (HasImportOverride())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HostTexture.SetData(data, layer, level, region);
|
||||
|
||||
_currentData = null;
|
||||
@@ -744,8 +789,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
int width = Info.Width;
|
||||
int height = Info.Height;
|
||||
|
||||
int depth = _depth;
|
||||
int layers = single ? 1 : _layers;
|
||||
int depth = Depth;
|
||||
int layers = single ? 1 : Layers;
|
||||
int levels = single ? 1 : (Info.Levels - level);
|
||||
|
||||
width = Math.Max(width >> level, 1);
|
||||
@@ -788,11 +833,55 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
|
||||
MemoryOwner<byte> result = linear;
|
||||
FormatInfo formatInfo = Info.FormatInfo;
|
||||
|
||||
if (_context.DiskTextureStorage.IsActive && !_forRender)
|
||||
{
|
||||
TextureInfoOverride? importOverride = _context.DiskTextureStorage.ImportTexture(out var importedTexture, this, result.Memory.ToArray());
|
||||
|
||||
if (importOverride.HasValue)
|
||||
{
|
||||
if (!_importOverride.HasValue || !_importOverride.Equals(importOverride))
|
||||
{
|
||||
bool hadImportOverride = HasImportOverride();
|
||||
|
||||
_importOverride = importOverride;
|
||||
|
||||
if (hadImportOverride || HasImportOverride())
|
||||
{
|
||||
InvalidatedSequence++;
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor, importOverride);
|
||||
ReplaceStorage(_context.Renderer.CreateTexture(createInfo));
|
||||
|
||||
if (_viewStorage != this)
|
||||
{
|
||||
_viewStorage.RemoveView(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextureInfoOverride infoOverride = importOverride.Value;
|
||||
|
||||
width = infoOverride.Width;
|
||||
height = infoOverride.Height;
|
||||
sliceDepth = Target == Target.Texture3D ? infoOverride.DepthOrLayers : 1;
|
||||
layers = Target != Target.Texture3D ? Info.DepthOrLayers : 1;
|
||||
levels = infoOverride.Levels;
|
||||
formatInfo = infoOverride.FormatInfo;
|
||||
|
||||
result.Dispose();
|
||||
result = importedTexture;
|
||||
}
|
||||
else if (!_hasData)
|
||||
{
|
||||
_context.DiskTextureStorage.EnqueueTextureDataForExport(this, result.Memory.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
// Handle compressed cases not supported by the host:
|
||||
// - ASTC is usually not supported on desktop cards.
|
||||
// - BC4/BC5 is not supported on 3D textures.
|
||||
if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
|
||||
if (!_context.Capabilities.SupportsAstcCompression && formatInfo.Format.IsAstc())
|
||||
{
|
||||
using (result)
|
||||
{
|
||||
@@ -823,9 +912,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return decoded;
|
||||
}
|
||||
}
|
||||
else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
|
||||
else if (!_context.Capabilities.SupportsEtc2Compression && formatInfo.Format.IsEtc2())
|
||||
{
|
||||
switch (Format)
|
||||
switch (formatInfo.Format)
|
||||
{
|
||||
case Format.Etc2RgbaSrgb:
|
||||
case Format.Etc2RgbaUnorm:
|
||||
@@ -847,9 +936,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
|
||||
else if (!TextureCompatibility.HostSupportsBcFormat(formatInfo.Format, Target, _context.Capabilities))
|
||||
{
|
||||
switch (Format)
|
||||
switch (formatInfo.Format)
|
||||
{
|
||||
case Format.Bc1RgbaSrgb:
|
||||
case Format.Bc1RgbaUnorm:
|
||||
@@ -895,7 +984,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
|
||||
else if (!_context.Capabilities.SupportsR4G4Format && formatInfo.Format == Format.R4G4Unorm)
|
||||
{
|
||||
using (result)
|
||||
{
|
||||
@@ -914,7 +1003,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Format == Format.R4G4B4A4Unorm)
|
||||
else if (formatInfo.Format == Format.R4G4B4A4Unorm)
|
||||
{
|
||||
if (!_context.Capabilities.SupportsR4G4B4A4Format)
|
||||
{
|
||||
@@ -924,9 +1013,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
|
||||
else if (!_context.Capabilities.Supports5BitComponentFormat && formatInfo.Format.Is16BitPacked())
|
||||
{
|
||||
switch (Format)
|
||||
switch (formatInfo.Format)
|
||||
{
|
||||
case Format.B5G6R5Unorm:
|
||||
case Format.R5G6B5Unorm:
|
||||
@@ -972,8 +1061,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
int width = Info.Width;
|
||||
int height = Info.Height;
|
||||
|
||||
int depth = _depth;
|
||||
int layers = single ? 1 : _layers;
|
||||
int depth = Depth;
|
||||
int layers = single ? 1 : Layers;
|
||||
int levels = single ? 1 : (Info.Levels - level);
|
||||
|
||||
width = Math.Max(width >> level, 1);
|
||||
@@ -1028,7 +1117,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <returns>True if data was flushed, false otherwise</returns>
|
||||
public bool FlushModified(bool tracked = true)
|
||||
{
|
||||
return TextureCompatibility.CanTextureFlush(Info, _context.Capabilities) && Group.FlushModified(this, tracked);
|
||||
return TextureCompatibility.CanTextureFlush(this, _context.Capabilities) && Group.FlushModified(this, tracked);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1042,7 +1131,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
|
||||
public void Flush(bool tracked)
|
||||
{
|
||||
if (TextureCompatibility.CanTextureFlush(Info, _context.Capabilities))
|
||||
if (TextureCompatibility.CanTextureFlush(this, _context.Capabilities))
|
||||
{
|
||||
FlushTextureDataToGuest(tracked);
|
||||
}
|
||||
@@ -1286,6 +1375,22 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool HasImportOverride()
|
||||
{
|
||||
if (_importOverride.HasValue)
|
||||
{
|
||||
TextureInfoOverride importOverride = _importOverride.Value;
|
||||
|
||||
return importOverride.Width != Info.Width ||
|
||||
importOverride.Height != Info.Height ||
|
||||
importOverride.DepthOrLayers != Info.GetDepthOrLayers() ||
|
||||
importOverride.Levels != Info.Levels ||
|
||||
importOverride.FormatInfo.Format != Info.FormatInfo.Format;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a texture of the specified target type from this texture.
|
||||
/// This can be used to get an array texture from a non-array texture and vice-versa.
|
||||
@@ -1302,7 +1407,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
|
||||
{
|
||||
FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
|
||||
FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info.FormatInfo, Info.Target, _context.Capabilities);
|
||||
|
||||
TextureCreateInfo createInfo = new(
|
||||
Info.Width,
|
||||
@@ -1405,7 +1510,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
foreach (Texture view in viewCopy)
|
||||
{
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, ScaleFactor);
|
||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, ScaleFactor, null);
|
||||
|
||||
ITexture newView = parent.HostTexture.CreateView(createInfo, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
|
||||
|
||||
@@ -1440,8 +1545,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
Height = info.Height;
|
||||
CanForceAnisotropy = CanTextureForceAnisotropy();
|
||||
|
||||
_depth = info.GetDepth();
|
||||
_layers = info.GetLayers();
|
||||
Depth = info.GetDepth();
|
||||
Layers = info.GetLayers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -828,6 +828,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
out int firstLevel,
|
||||
flags);
|
||||
|
||||
if (overlap.HasImportOverride())
|
||||
{
|
||||
overlapCompatibility = TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
|
||||
if (overlapCompatibility >= TextureViewCompatibility.FormatAlias)
|
||||
{
|
||||
if (overlap.IsView)
|
||||
@@ -887,7 +892,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
|
||||
|
||||
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
|
||||
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode, flags.HasFlag(TextureSearchFlags.WithUpscale));
|
||||
|
||||
// If the new texture is larger than the existing one, we need to fill the remaining space with CPU data,
|
||||
// otherwise we only need the data that is copied from the existing texture, without loading the CPU data.
|
||||
@@ -936,7 +941,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
// No match, create a new texture.
|
||||
if (texture == null)
|
||||
{
|
||||
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
|
||||
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode, flags.HasFlag(TextureSearchFlags.WithUpscale));
|
||||
|
||||
// Step 1: Find textures that are view compatible with the new texture.
|
||||
// Any textures that are incompatible will contain garbage data, so they should be removed where possible.
|
||||
@@ -964,6 +969,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
out int firstLayer,
|
||||
out int firstLevel);
|
||||
|
||||
if (overlap.HasImportOverride())
|
||||
{
|
||||
compatibility = TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
|
||||
if (overlap.IsView && compatibility == TextureViewCompatibility.Full)
|
||||
{
|
||||
compatibility = TextureViewCompatibility.CopyOnly;
|
||||
@@ -1079,6 +1089,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
continue;
|
||||
}
|
||||
|
||||
if (texture.HasImportOverride())
|
||||
{
|
||||
// Replaced textures with different parameters are not considered compatible.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: If we allow different sizes for those overlaps,
|
||||
// we need to make sure that the "info" has the correct size for the parent texture here.
|
||||
// Since this is not allowed right now, we don't need to do it.
|
||||
@@ -1102,14 +1118,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
else
|
||||
{
|
||||
TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor);
|
||||
|
||||
TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor, null);
|
||||
ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);
|
||||
|
||||
overlap.SynchronizeMemory();
|
||||
|
||||
overlap.HostTexture.CopyTo(newView, 0, 0);
|
||||
|
||||
overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
|
||||
}
|
||||
}
|
||||
@@ -1278,10 +1291,30 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="info">Texture information</param>
|
||||
/// <param name="caps">GPU capabilities</param>
|
||||
/// <param name="scale">Texture scale factor, to be applied to the texture size</param>
|
||||
/// <param name="infoOverride">Optional parameters to override the <paramref name="info"/> parameter</param>
|
||||
/// <returns>The texture creation information</returns>
|
||||
public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps, float scale)
|
||||
public static TextureCreateInfo GetCreateInfo(TextureInfo info, in Capabilities caps, float scale, TextureInfoOverride? infoOverride)
|
||||
{
|
||||
FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps);
|
||||
int width = info.Width / info.SamplesInX;
|
||||
int height = info.Height / info.SamplesInY;
|
||||
|
||||
int depth = info.GetDepth() * info.GetLayers();
|
||||
int levels = info.Levels;
|
||||
|
||||
FormatInfo formatInfo = info.FormatInfo;
|
||||
|
||||
if (infoOverride.HasValue)
|
||||
{
|
||||
TextureInfoOverride overrideValue = infoOverride.Value;
|
||||
|
||||
width = overrideValue.Width;
|
||||
height = overrideValue.Height;
|
||||
depth = overrideValue.DepthOrLayers;
|
||||
levels = overrideValue.Levels;
|
||||
formatInfo = overrideValue.FormatInfo;
|
||||
}
|
||||
|
||||
formatInfo = TextureCompatibility.ToHostCompatibleFormat(formatInfo, info.Target, caps);
|
||||
|
||||
if (info.Target == Target.TextureBuffer && !caps.SupportsSnormBufferTextureFormat)
|
||||
{
|
||||
@@ -1310,11 +1343,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
int width = info.Width / info.SamplesInX;
|
||||
int height = info.Height / info.SamplesInY;
|
||||
|
||||
int depth = info.GetDepth() * info.GetLayers();
|
||||
|
||||
if (scale != 1f)
|
||||
{
|
||||
width = (int)MathF.Ceiling(width * scale);
|
||||
@@ -1325,7 +1353,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
info.Levels,
|
||||
levels,
|
||||
info.Samples,
|
||||
formatInfo.BlockWidth,
|
||||
formatInfo.BlockHeight,
|
||||
|
||||
@@ -50,10 +50,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="info">Texture information</param>
|
||||
/// <param name="caps">Host GPU capabilities</param>
|
||||
/// <returns>True if the format is incompatible, false otherwise</returns>
|
||||
public static bool IsFormatHostIncompatible(TextureInfo info, Capabilities caps)
|
||||
public static bool IsFormatHostIncompatible(TextureInfo info, in Capabilities caps)
|
||||
{
|
||||
Format originalFormat = info.FormatInfo.Format;
|
||||
return ToHostCompatibleFormat(info, caps).Format != originalFormat;
|
||||
return ToHostCompatibleFormat(info.FormatInfo, info.Target, caps).Format != originalFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,10 +64,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// This can be used to convert a incompatible compressed format to the decompressor
|
||||
/// output format.
|
||||
/// </remarks>
|
||||
/// <param name="info">Texture information</param>
|
||||
/// <param name="formatInfo">Texture format information</param>
|
||||
/// <param name="target">Texture dimensions</param>
|
||||
/// <param name="caps">Host GPU capabilities</param>
|
||||
/// <returns>A host compatible format</returns>
|
||||
public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps)
|
||||
public static FormatInfo ToHostCompatibleFormat(FormatInfo formatInfo, Target target, in Capabilities caps)
|
||||
{
|
||||
// The host API does not support those compressed formats.
|
||||
// We assume software decompression will be done for those textures,
|
||||
@@ -75,13 +76,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (!caps.SupportsAstcCompression)
|
||||
{
|
||||
if (info.FormatInfo.Format.IsAstcUnorm())
|
||||
if (formatInfo.Format.IsAstcUnorm())
|
||||
{
|
||||
return GraphicsConfig.EnableTextureRecompression
|
||||
? new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4)
|
||||
: new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
|
||||
}
|
||||
else if (info.FormatInfo.Format.IsAstcSrgb())
|
||||
else if (formatInfo.Format.IsAstcSrgb())
|
||||
{
|
||||
return GraphicsConfig.EnableTextureRecompression
|
||||
? new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4)
|
||||
@@ -89,9 +90,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
|
||||
if (!HostSupportsBcFormat(formatInfo.Format, target, caps))
|
||||
{
|
||||
switch (info.FormatInfo.Format)
|
||||
switch (formatInfo.Format)
|
||||
{
|
||||
case Format.Bc1RgbaSrgb:
|
||||
case Format.Bc2Srgb:
|
||||
@@ -119,7 +120,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (!caps.SupportsEtc2Compression)
|
||||
{
|
||||
switch (info.FormatInfo.Format)
|
||||
switch (formatInfo.Format)
|
||||
{
|
||||
case Format.Etc2RgbaSrgb:
|
||||
case Format.Etc2RgbPtaSrgb:
|
||||
@@ -132,7 +133,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
|
||||
if (!caps.SupportsR4G4Format && formatInfo.Format == Format.R4G4Unorm)
|
||||
{
|
||||
if (caps.SupportsR4G4B4A4Format)
|
||||
{
|
||||
@@ -144,19 +145,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
if (info.FormatInfo.Format == Format.R4G4B4A4Unorm)
|
||||
if (formatInfo.Format == Format.R4G4B4A4Unorm)
|
||||
{
|
||||
if (!caps.SupportsR4G4B4A4Format)
|
||||
{
|
||||
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
|
||||
}
|
||||
}
|
||||
else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked())
|
||||
else if (!caps.Supports5BitComponentFormat && formatInfo.Format.Is16BitPacked())
|
||||
{
|
||||
return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4);
|
||||
return new FormatInfo(formatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4);
|
||||
}
|
||||
|
||||
return info.FormatInfo;
|
||||
return formatInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -166,7 +167,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="target">Target usage of the texture</param>
|
||||
/// <param name="caps">Host GPU Capabilities</param>
|
||||
/// <returns>True if the texture host supports the format with the given target usage, false otherwise</returns>
|
||||
public static bool HostSupportsBcFormat(Format format, Target target, Capabilities caps)
|
||||
public static bool HostSupportsBcFormat(Format format, Target target, in Capabilities caps)
|
||||
{
|
||||
bool not3DOr3DCompressionSupported = target != Target.Texture3D || caps.Supports3DTextureCompression;
|
||||
|
||||
@@ -194,15 +195,26 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a texture can flush its data back to guest memory.
|
||||
/// </summary>
|
||||
/// <param name="info">Texture that will have its data flushed</param>
|
||||
/// <param name="caps">Host GPU Capabilities</param>
|
||||
/// <returns>True if the texture can flush, false otherwise</returns>
|
||||
public static bool CanTextureFlush(Texture texture, in Capabilities caps)
|
||||
{
|
||||
return !texture.HasImportOverride() && CanTextureFlush(texture.Info, caps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a texture can flush its data back to guest memory.
|
||||
/// </summary>
|
||||
/// <param name="info">Texture information</param>
|
||||
/// <param name="caps">Host GPU Capabilities</param>
|
||||
/// <returns>True if the texture can flush, false otherwise</returns>
|
||||
public static bool CanTextureFlush(TextureInfo info, Capabilities caps)
|
||||
private static bool CanTextureFlush(TextureInfo info, in Capabilities caps)
|
||||
{
|
||||
if (IsFormatHostIncompatible(info, caps))
|
||||
if (IsFormatHostIncompatible(info, in caps))
|
||||
{
|
||||
return false; // Flushing this format is not supported, as it may have been converted to another host format.
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
if (_flushBuffer == BufferHandle.Null)
|
||||
{
|
||||
if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
|
||||
if (!TextureCompatibility.CanTextureFlush(Storage, _context.Capabilities))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1700,7 +1700,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported))
|
||||
if (TextureCompatibility.CanTextureFlush(Storage, _context.Capabilities) && !(inBuffer && _flushBufferImported))
|
||||
{
|
||||
FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture());
|
||||
}
|
||||
|
||||
@@ -207,6 +207,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return GetLayers(Target, DepthOrLayers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of layers or depth of the texture.
|
||||
/// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
|
||||
/// </summary>
|
||||
/// <returns>The number of texture layers or depth</returns>
|
||||
public int GetDepthOrLayers()
|
||||
{
|
||||
return GetDepthOrLayers(Target, DepthOrLayers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of layers of the texture.
|
||||
/// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
|
||||
@@ -234,6 +244,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of layers or depth of the texture.
|
||||
/// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
|
||||
/// </summary>
|
||||
/// <param name="target">Texture target</param>
|
||||
/// <param name="depthOrLayers">Texture layers if the is a array texture, depth for 3D textures, ignored otherwise</param>
|
||||
/// <returns>The number of texture layers or depth</returns>
|
||||
public static int GetDepthOrLayers(Target target, int depthOrLayers)
|
||||
{
|
||||
if (target is Target.Texture2DArray or Target.Texture2DMultisampleArray or Target.Texture3D)
|
||||
{
|
||||
return depthOrLayers;
|
||||
}
|
||||
else if (target == Target.CubemapArray)
|
||||
{
|
||||
return depthOrLayers * 6;
|
||||
}
|
||||
else if (target == Target.Cubemap)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of 2D slices of the texture.
|
||||
/// Returns 6 for cubemap textures, layer faces for cubemap array textures, and DepthOrLayers for everything else.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Values that should override <see cref="TextureInfo"/> parameters.
|
||||
/// </summary>
|
||||
readonly struct TextureInfoOverride
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture width override.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture height override.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture depth (for 3D textures), or layers count override.
|
||||
/// </summary>
|
||||
public int DepthOrLayers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Mipmap levels override.
|
||||
/// </summary>
|
||||
public int Levels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture format override.
|
||||
/// </summary>
|
||||
public FormatInfo FormatInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the texture override structure.
|
||||
/// </summary>
|
||||
/// <param name="width">Texture width override</param>
|
||||
/// <param name="height">Texture height override</param>
|
||||
/// <param name="depthOrLayers">Texture depth (for 3D textures), or layers count override</param>
|
||||
/// <param name="levels">Mipmap levels override</param>
|
||||
/// <param name="formatInfo">Texture format override</param>
|
||||
public TextureInfoOverride(int width, int height, int depthOrLayers, int levels, FormatInfo formatInfo)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
DepthOrLayers = depthOrLayers;
|
||||
Levels = levels;
|
||||
FormatInfo = formatInfo;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TextureInfoOverride other &&
|
||||
other.Width == Width &&
|
||||
other.Height == Height &&
|
||||
other.DepthOrLayers == DepthOrLayers &&
|
||||
other.Levels == Levels &&
|
||||
other.FormatInfo.Format == FormatInfo.Format;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Width, Height, DepthOrLayers, Levels, FormatInfo.Format);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags controlling which parameters of the texture should be overriden.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
enum TextureInfoOverrideFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing should be overriden.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The texture size (width, height, depth and levels) should be overriden.
|
||||
/// </summary>
|
||||
OverrideSize = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// The texture format should be overriden.
|
||||
/// </summary>
|
||||
OverrideFormat = 1 << 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user