diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Texture.cs | 29 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs | 96 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs | 76 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs | 1 |
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; |