aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs29
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs96
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs76
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs1
4 files changed, 156 insertions, 46 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index cb10f456..a598f212 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -826,20 +826,25 @@ namespace Ryujinx.Graphics.Gpu.Image
depth,
levels,
layers,
- out Span<byte> decoded))
+ out byte[] decoded))
{
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
}
+ if (GraphicsConfig.EnableTextureRecompression)
+ {
+ decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers);
+ }
+
data = decoded;
}
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
{
data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
}
- else if (!_context.Capabilities.Supports3DTextureCompression && Target == Target.Texture3D)
+ else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
{
switch (Format)
{
@@ -863,6 +868,14 @@ namespace Ryujinx.Graphics.Gpu.Image
case Format.Bc5Unorm:
data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
break;
+ case Format.Bc6HSfloat:
+ case Format.Bc6HUfloat:
+ data = BCnDecoder.DecodeBC6(data, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
+ break;
+ case Format.Bc7Srgb:
+ case Format.Bc7Unorm:
+ data = BCnDecoder.DecodeBC7(data, width, height, depth, levels, layers);
+ break;
}
}
@@ -1151,7 +1164,7 @@ namespace Ryujinx.Graphics.Gpu.Image
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
if (result != TextureViewCompatibility.Incompatible)
{
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
+ result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
@@ -1216,16 +1229,18 @@ namespace Ryujinx.Graphics.Gpu.Image
if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
{
+ FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
+
TextureCreateInfo createInfo = new TextureCreateInfo(
Info.Width,
Info.Height,
target == Target.CubemapArray ? 6 : 1,
Info.Levels,
Info.Samples,
- Info.FormatInfo.BlockWidth,
- Info.FormatInfo.BlockHeight,
- Info.FormatInfo.BytesPerPixel,
- Info.FormatInfo.Format,
+ formatInfo.BlockWidth,
+ formatInfo.BlockHeight,
+ formatInfo.BytesPerPixel,
+ formatInfo.Format,
Info.DepthStencilMode,
target,
Info.SwizzleR,
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index 067a1f9f..6c122124 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -281,6 +281,30 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Determines if the vertex stage requires a scale value.
+ /// </summary>
+ private bool VertexRequiresScale()
+ {
+ for (int i = 0; i < _textureBindingsCount[0]; i++)
+ {
+ if ((_textureBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
+ {
+ return true;
+ }
+ }
+
+ for (int i = 0; i < _imageBindingsCount[0]; i++)
+ {
+ if ((_imageBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
/// Uploads texture and image scales to the backend when they are used.
/// </summary>
private void CommitRenderScale()
@@ -291,10 +315,10 @@ namespace Ryujinx.Graphics.Gpu.Image
int fragmentIndex = (int)ShaderStage.Fragment - 1;
int fragmentTotal = _isCompute ? 0 : (_textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex]);
- if (total != 0 && fragmentTotal != _lastFragmentTotal)
+ if (total != 0 && fragmentTotal != _lastFragmentTotal && VertexRequiresScale())
{
// Must update scales in the support buffer if:
- // - Vertex stage has bindings.
+ // - Vertex stage has bindings that require scale.
// - Fragment stage binding count has been updated since last render scale update.
_scaleChanged = true;
@@ -421,6 +445,25 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Counts the total number of texture bindings used by all shader stages.
+ /// </summary>
+ /// <returns>The total amount of textures used</returns>
+ private int GetTextureBindingsCount()
+ {
+ int count = 0;
+
+ for (int i = 0; i < _textureBindings.Length; i++)
+ {
+ if (_textureBindings[i] != null)
+ {
+ count += _textureBindings[i].Length;
+ }
+ }
+
+ return count;
+ }
+
+ /// <summary>
/// Ensures that the texture bindings are visible to the host GPU.
/// Note: this actually performs the binding using the host graphics API.
/// </summary>
@@ -501,7 +544,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.ScaleIndex = index;
state.UsageFlags = usageFlags;
- _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTextureRebind);
+ _context.Renderer.Pipeline.SetTextureAndSampler(stage, bindingInfo.Binding, hostTextureRebind, state.Sampler);
}
continue;
@@ -514,44 +557,42 @@ namespace Ryujinx.Graphics.Gpu.Image
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
+ Sampler sampler = _samplerPool?.Get(samplerId);
+
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
+ ISampler hostSampler = sampler?.GetHostSampler(texture);
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
- _channel.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
+ _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
}
else
{
- if (state.Texture != hostTexture)
+ bool textureOrSamplerChanged = state.Texture != hostTexture || state.Sampler != hostSampler;
+
+ if ((state.ScaleIndex != index || state.UsageFlags != usageFlags || textureOrSamplerChanged) &&
+ UpdateScale(texture, usageFlags, index, stage))
{
- if (UpdateScale(texture, usageFlags, index, stage))
- {
- hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
- }
+ hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
+ textureOrSamplerChanged = true;
+ }
+ if (textureOrSamplerChanged)
+ {
state.Texture = hostTexture;
state.ScaleIndex = index;
state.UsageFlags = usageFlags;
- _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
- }
-
- Sampler sampler = samplerPool?.Get(samplerId);
- state.CachedSampler = sampler;
-
- ISampler hostSampler = sampler?.GetHostSampler(texture);
-
- if (state.Sampler != hostSampler)
- {
state.Sampler = hostSampler;
- _context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
+ _context.Renderer.Pipeline.SetTextureAndSampler(stage, bindingInfo.Binding, hostTexture, hostSampler);
}
state.CachedTexture = texture;
+ state.CachedSampler = sampler;
state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
}
}
@@ -625,7 +666,7 @@ namespace Ryujinx.Graphics.Gpu.Image
cachedTexture?.SignalModified();
}
- if ((state.ScaleIndex != index || state.UsageFlags != usageFlags) &&
+ if ((state.ScaleIndex != scaleIndex || state.UsageFlags != usageFlags) &&
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
{
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
@@ -663,7 +704,7 @@ namespace Ryujinx.Graphics.Gpu.Image
format = texture.Format;
}
- _channel.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
+ _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
}
else
{
@@ -672,13 +713,14 @@ namespace Ryujinx.Graphics.Gpu.Image
texture?.SignalModified();
}
- if (state.Texture != hostTexture)
+ if ((state.ScaleIndex != scaleIndex || state.UsageFlags != usageFlags || state.Texture != hostTexture) &&
+ UpdateScale(texture, usageFlags, scaleIndex, stage))
{
- if (UpdateScale(texture, usageFlags, scaleIndex, stage))
- {
- hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
- }
+ hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
+ }
+ if (state.Texture != hostTexture)
+ {
state.Texture = hostTexture;
state.ScaleIndex = scaleIndex;
state.UsageFlags = usageFlags;
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index 61b48dc4..5ea9ee2f 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -71,11 +71,15 @@ namespace Ryujinx.Graphics.Gpu.Image
{
if (info.FormatInfo.Format.IsAstcUnorm())
{
- return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
+ 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())
{
- return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
+ return GraphicsConfig.EnableTextureRecompression
+ ? new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4)
+ : new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
}
}
@@ -84,9 +88,9 @@ namespace Ryujinx.Graphics.Gpu.Image
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
- if (!caps.Supports3DTextureCompression && info.Target == Target.Texture3D)
+ if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
{
- // The host API does not support 3D compressed formats.
+ // The host API does not this compressed format.
// We assume software decompression will be done for those textures,
// and so we adjust the format here to match the decompressor output.
switch (info.FormatInfo.Format)
@@ -94,10 +98,12 @@ namespace Ryujinx.Graphics.Gpu.Image
case Format.Bc1RgbaSrgb:
case Format.Bc2Srgb:
case Format.Bc3Srgb:
+ case Format.Bc7Srgb:
return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
case Format.Bc1RgbaUnorm:
case Format.Bc2Unorm:
case Format.Bc3Unorm:
+ case Format.Bc7Unorm:
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
case Format.Bc4Unorm:
return new FormatInfo(Format.R8Unorm, 1, 1, 1, 1);
@@ -107,6 +113,9 @@ namespace Ryujinx.Graphics.Gpu.Image
return new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2);
case Format.Bc5Snorm:
return new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2);
+ case Format.Bc6HSfloat:
+ case Format.Bc6HUfloat:
+ return new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4);
}
}
@@ -114,6 +123,41 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Checks if the host API supports a given texture compression format of the BC family.
+ /// </summary>
+ /// <param name="format">BC format to be checked</param>
+ /// <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)
+ {
+ bool not3DOr3DCompressionSupported = target != Target.Texture3D || caps.Supports3DTextureCompression;
+
+ switch (format)
+ {
+ case Format.Bc1RgbaSrgb:
+ case Format.Bc1RgbaUnorm:
+ case Format.Bc2Srgb:
+ case Format.Bc2Unorm:
+ case Format.Bc3Srgb:
+ case Format.Bc3Unorm:
+ return caps.SupportsBc123Compression && not3DOr3DCompressionSupported;
+ case Format.Bc4Unorm:
+ case Format.Bc4Snorm:
+ case Format.Bc5Unorm:
+ case Format.Bc5Snorm:
+ return caps.SupportsBc45Compression && not3DOr3DCompressionSupported;
+ case Format.Bc6HSfloat:
+ case Format.Bc6HUfloat:
+ case Format.Bc7Srgb:
+ case Format.Bc7Unorm:
+ return caps.SupportsBc67Compression && not3DOr3DCompressionSupported;
+ }
+
+ return true;
+ }
+
+ /// <summary>
/// Determines whether a texture can flush its data back to guest memory.
/// </summary>
/// <param name="info">Texture information</param>
@@ -627,9 +671,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="lhs">Texture information of the texture view</param
/// <param name="rhs">Texture information of the texture view</param>
- /// <param name="isCopy">True to check for copy rather than view compatibility</param>
+ /// <param name="caps">Host GPU capabilities</param>
/// <returns>True if the targets are compatible, false otherwise</returns>
- public static TextureViewCompatibility ViewTargetCompatible(TextureInfo lhs, TextureInfo rhs)
+ public static TextureViewCompatibility ViewTargetCompatible(TextureInfo lhs, TextureInfo rhs, ref Capabilities caps)
{
bool result = false;
switch (lhs.Target)
@@ -646,14 +690,24 @@ namespace Ryujinx.Graphics.Gpu.Image
break;
case Target.Texture2DArray:
+ result = rhs.Target == Target.Texture2D ||
+ rhs.Target == Target.Texture2DArray;
+
+ if (rhs.Target == Target.Cubemap || rhs.Target == Target.CubemapArray)
+ {
+ return caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly;
+ }
+ break;
case Target.Cubemap:
case Target.CubemapArray:
- result = rhs.Target == Target.Texture2D ||
- rhs.Target == Target.Texture2DArray ||
- rhs.Target == Target.Cubemap ||
+ result = rhs.Target == Target.Cubemap ||
rhs.Target == Target.CubemapArray;
- break;
+ if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
+ {
+ return caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly;
+ }
+ break;
case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray:
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
@@ -744,7 +798,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if the texture target and samples count matches, false otherwise</returns>
public static bool TargetAndSamplesCompatible(TextureInfo lhs, TextureInfo rhs)
{
- return lhs.Target == rhs.Target &&
+ return lhs.Target == rhs.Target &&
lhs.SamplesInX == rhs.SamplesInX &&
lhs.SamplesInY == rhs.SamplesInY;
}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
index 73b1232e..52cc8ee0 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
@@ -1,4 +1,3 @@
-using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;