diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index abacdcfa6..7a1f44b6b 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.GAL
public readonly bool HasFrontFacingBug;
public readonly bool HasVectorIndexingBug;
+ public readonly bool NeedsFragmentOutputSpecialization;
+ public readonly bool ReduceShaderPrecision;
public readonly bool SupportsAstcCompression;
public readonly bool SupportsBc123Compression;
@@ -49,6 +51,8 @@ namespace Ryujinx.Graphics.GAL
string vendorName,
bool hasFrontFacingBug,
bool hasVectorIndexingBug,
+ bool needsFragmentOutputSpecialization,
+ bool reduceShaderPrecision,
bool supportsAstcCompression,
bool supportsBc123Compression,
bool supportsBc45Compression,
@@ -85,6 +89,8 @@ namespace Ryujinx.Graphics.GAL
VendorName = vendorName;
HasFrontFacingBug = hasFrontFacingBug;
HasVectorIndexingBug = hasVectorIndexingBug;
+ NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
+ ReduceShaderPrecision = reduceShaderPrecision;
SupportsAstcCompression = supportsAstcCompression;
SupportsBc123Compression = supportsBc123Compression;
SupportsBc45Compression = supportsBc45Compression;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
index 13b332f43..62df15e79 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
@@ -1,5 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader;
@@ -10,6 +11,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
internal class SpecializationStateUpdater
{
+ private readonly GpuContext _context;
private GpuChannelGraphicsState _graphics;
private GpuChannelPoolState _pool;
@@ -18,6 +20,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private bool _changed;
+ ///
+ /// Creates a new instance of the specialization state updater class.
+ ///
+ /// GPU context
+ public SpecializationStateUpdater(GpuContext context)
+ {
+ _context = context;
+ }
+
///
/// Signal that the specialization state has changed.
///
@@ -232,6 +243,42 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
}
+ ///
+ /// Updates the type of the outputs produced by the fragment shader based on the current render target state.
+ ///
+ /// The render target control register
+ /// The color attachment state
+ public void SetFragmentOutputTypes(RtControl rtControl, ref Array8 state)
+ {
+ bool changed = false;
+ int count = rtControl.UnpackCount();
+
+ for (int index = 0; index < Constants.TotalRenderTargets; index++)
+ {
+ int rtIndex = rtControl.UnpackPermutationIndex(index);
+
+ var colorState = state[rtIndex];
+
+ if (index < count && StateUpdater.IsRtEnabled(colorState))
+ {
+ Format format = colorState.Format.Convert().Format;
+
+ AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float;
+
+ if (type != _graphics.FragmentOutputTypes[index])
+ {
+ _graphics.FragmentOutputTypes[index] = type;
+ changed = true;
+ }
+ }
+ }
+
+ if (changed && _context.Capabilities.NeedsFragmentOutputSpecialization)
+ {
+ Signal();
+ }
+ }
+
///
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
///
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
index 3ed5607a4..7c7309676 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
@@ -138,6 +138,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length);
}
+ ///
+ /// Check if the given register group is dirty without clearing it.
+ ///
+ /// Index of the group to check
+ /// True if dirty, false otherwise
+ public bool IsDirty(int groupIndex)
+ {
+ return (_dirtyMask & (1UL << groupIndex)) != 0;
+ }
+
///
/// Check all the groups specified by for modification, and update if modified.
///
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index 64fa1735f..9b59009cf 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public const int ScissorStateIndex = 16;
public const int VertexBufferStateIndex = 0;
public const int PrimitiveRestartStateIndex = 12;
+ public const int RenderTargetStateIndex = 27;
private readonly GpuContext _context;
private readonly GpuChannel _channel;
@@ -264,6 +265,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_prevTfEnable = false;
}
+ if (_updateTracker.IsDirty(RenderTargetStateIndex))
+ {
+ UpdateRenderTargetSpecialization();
+ }
+
_updateTracker.Update(ulong.MaxValue);
// If any state that the shader depends on changed,
@@ -526,12 +532,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
}
+ ///
+ /// Updates specialization state based on render target state.
+ ///
+ public void UpdateRenderTargetSpecialization()
+ {
+ _currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, ref _state.State.RtColorState);
+ }
+
///
/// Checks if a render target color buffer is used.
///
/// Color buffer information
/// True if the specified buffer is enabled/used, false otherwise
- private static bool IsRtEnabled(RtColorState colorState)
+ internal static bool IsRtEnabled(RtColorState colorState)
{
// Colors are disabled by writing 0 to the format.
return colorState.Format != 0 && colorState.WidthOrStride != 0;
@@ -893,7 +907,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
- format = Format.R32G32B32A32Float;
+ format = vertexAttrib.UnpackType() switch
+ {
+ VertexAttribType.Sint => Format.R32G32B32A32Sint,
+ VertexAttribType.Uint => Format.R32G32B32A32Uint,
+ _ => Format.R32G32B32A32Float
+ };
}
vertexAttribs[index] = new VertexAttribDescriptor(
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
index a38c09875..19eb8b46e 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
- var spec = new SpecializationStateUpdater();
+ var spec = new SpecializationStateUpdater(context);
var drawState = new DrawState();
_drawManager = new DrawManager(context, channel, _state, drawState, spec);
diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs
index 57c632dac..fe8587629 100644
--- a/Ryujinx.Graphics.Gpu/GpuChannel.cs
+++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs
@@ -1,4 +1,5 @@
-using Ryujinx.Graphics.Gpu.Engine.GPFifo;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using System;
@@ -31,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu
///
internal MemoryManager MemoryManager => _memoryManager;
+ ///
+ /// Host hardware capabilities from the GPU context.
+ ///
+ internal ref Capabilities Capabilities => ref _context.Capabilities;
+
///
/// Creates a new instance of a GPU channel.
///
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
index c567c2c06..97173c964 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
@@ -107,6 +107,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.AttributeTypes[location];
}
+ ///
+ public AttributeType QueryFragmentOutputType(int location)
+ {
+ return _oldSpecState.GraphicsState.FragmentOutputTypes[location];
+ }
+
///
public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX;
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 28ea430cd..05631a210 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -113,6 +113,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.AttributeTypes[location];
}
+ ///
+ public AttributeType QueryFragmentOutputType(int location)
+ {
+ return _state.GraphicsState.FragmentOutputTypes[location];
+ }
+
///
public int QueryComputeLocalSizeX() => _state.ComputeState.LocalSizeX;
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index 33f06b6e9..d36ffd70f 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -112,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
};
}
+ public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
+
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
index e5e486260..70ac50170 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
@@ -87,6 +87,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
///
public bool HasUnalignedStorageBuffer;
+ ///
+ /// Type of the fragment shader outputs.
+ ///
+ public Array8 FragmentOutputTypes;
+
///
/// Creates a new GPU graphics state.
///
@@ -105,6 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Type of the vertex attributes consumed by the shader
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0
/// Indicates that any storage buffer use is unaligned
+ /// Type of the fragment shader outputs
public GpuChannelGraphicsState(
bool earlyZForce,
PrimitiveTopology topology,
@@ -120,7 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
float alphaTestReference,
ref Array32 attributeTypes,
bool hasConstantBufferDrawParameters,
- bool hasUnalignedStorageBuffer)
+ bool hasUnalignedStorageBuffer,
+ ref Array8 fragmentOutputTypes)
{
EarlyZForce = earlyZForce;
Topology = topology;
@@ -137,6 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
AttributeTypes = attributeTypes;
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
+ FragmentOutputTypes = fragmentOutputTypes;
}
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index b0d77d8ae..a4bf81363 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -530,6 +530,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false;
}
+ if (channel.Capabilities.NeedsFragmentOutputSpecialization && !graphicsState.FragmentOutputTypes.AsSpan().SequenceEqual(GraphicsState.FragmentOutputTypes.AsSpan()))
+ {
+ return false;
+ }
+
return Matches(channel, ref poolState, checkTextures, isCompute: false);
}
diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
index 1733c6f21..30ed942d3 100644
--- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
@@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.OpenGL
vendorName: GpuVendor,
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
+ needsFragmentOutputSpecialization: false,
+ reduceShaderPrecision: false,
supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc,
supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc,
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 4da21cb72..996d312b7 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -346,12 +346,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
string name = context.OperandManager.DeclareLocal(decl);
- context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";");
+ context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";");
}
}
- public static string GetVarTypeName(AggregateType type, bool precise = true)
+ public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool precise = true)
{
+ if (context.Config.GpuAccessor.QueryHostReducedPrecision())
+ {
+ precise = false;
+ }
+
return type switch
{
AggregateType.Void => "void",
@@ -666,7 +671,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
else
{
- context.AppendLine($"layout (location = {attr}) out vec4 {name};");
+ string type = context.Config.Stage != ShaderStage.Fragment ? "vec4" :
+ context.Config.GpuAccessor.QueryFragmentOutputType(attr) switch
+ {
+ AttributeType.Sint => "ivec4",
+ AttributeType.Uint => "uvec4",
+ _ => "vec4"
+ };
+
+ if (context.Config.GpuAccessor.QueryHostReducedPrecision() && context.Config.Stage == ShaderStage.Vertex && attr == 0)
+ {
+ context.AppendLine($"layout (location = {attr}) invariant out {type} {name};");
+ }
+ else
+ {
+ context.AppendLine($"layout (location = {attr}) out {type} {name};");
+ }
}
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
index 90826a154..907275583 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
for (int i = 1; i < info.Functions.Count; i++)
{
- context.AppendLine($"{GetFunctionSignature(info.Functions[i])};");
+ context.AppendLine($"{GetFunctionSignature(context, info.Functions[i])};");
}
context.AppendLine();
@@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
context.CurrentFunction = function;
- context.AppendLine(GetFunctionSignature(function, funcName));
+ context.AppendLine(GetFunctionSignature(context, function, funcName));
context.EnterScope();
Declarations.DeclareLocals(context, function);
@@ -54,23 +54,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.LeaveScope();
}
- private static string GetFunctionSignature(StructuredFunction function, string funcName = null)
+ private static string GetFunctionSignature(CodeGenContext context, StructuredFunction function, string funcName = null)
{
string[] args = new string[function.InArguments.Length + function.OutArguments.Length];
for (int i = 0; i < function.InArguments.Length; i++)
{
- args[i] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}";
+ args[i] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}";
}
for (int i = 0; i < function.OutArguments.Length; i++)
{
int j = i + function.InArguments.Length;
- args[j] = $"out {Declarations.GetVarTypeName(function.OutArguments[i])} {OperandManager.GetArgumentName(j)}";
+ args[j] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}";
}
- return $"{Declarations.GetVarTypeName(function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})";
+ return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})";
}
private static void PrintBlock(CodeGenContext context, AstBlock block)
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index f667d0808..3dbd73b27 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if ((outputType & AggregateType.ElementCountMask) != 0)
{
- return $"{Declarations.GetVarTypeName(outputType, precise: false)}({imageConst})";
+ return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({imageConst})";
}
return imageConst;
@@ -513,7 +513,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if ((outputType & AggregateType.ElementCountMask) != 0)
{
- return $"{Declarations.GetVarTypeName(outputType, precise: false)}({scalarValue})";
+ return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({scalarValue})";
}
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 3da72b402..fab1667ce 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -577,6 +577,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(spvVar, Decoration.Patch);
}
+ if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
+ {
+ context.Decorate(spvVar, Decoration.Invariant);
+ }
+
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
index a02c4c22a..61abf3341 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
@@ -2194,13 +2194,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (operation.Inst.HasFlag(Instruction.FP64))
{
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2));
- context.Decorate(result, Decoration.NoContraction);
+
+ if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
+ {
+ context.Decorate(result, Decoration.NoContraction);
+ }
+
return new OperationResult(AggregateType.FP64, result);
}
else if (operation.Inst.HasFlag(Instruction.FP32))
{
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2));
- context.Decorate(result, Decoration.NoContraction);
+
+ if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
+ {
+ context.Decorate(result, Decoration.NoContraction);
+ }
+
return new OperationResult(AggregateType.FP32, result);
}
else
@@ -2255,13 +2265,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (operation.Inst.HasFlag(Instruction.FP64))
{
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3));
- context.Decorate(result, Decoration.NoContraction);
+
+ if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
+ {
+ context.Decorate(result, Decoration.NoContraction);
+ }
+
return new OperationResult(AggregateType.FP64, result);
}
else if (operation.Inst.HasFlag(Instruction.FP32))
{
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3));
- context.Decorate(result, Decoration.NoContraction);
+
+ if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
+ {
+ context.Decorate(result, Decoration.NoContraction);
+ }
+
return new OperationResult(AggregateType.FP32, result);
}
else
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 337bd314a..55df8dc31 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -114,6 +114,16 @@ namespace Ryujinx.Graphics.Shader
return index;
}
+ ///
+ /// Queries output type for fragment shaders.
+ ///
+ /// Location of the framgent output
+ /// Output location
+ AttributeType QueryFragmentOutputType(int location)
+ {
+ return AttributeType.Float;
+ }
+
///
/// Queries Local Size X for compute shaders.
///
@@ -186,6 +196,15 @@ namespace Ryujinx.Graphics.Shader
return false;
}
+ ///
+ /// Queries host about whether to reduce precision to improve performance.
+ ///
+ /// True if precision is limited to vertex position, false otherwise
+ bool QueryHostReducedPrecision()
+ {
+ return false;
+ }
+
///
/// Queries host about the presence of the FrontFacing built-in variable bug.
///
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
index fa1ae17f6..b671429a8 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
@@ -128,7 +128,15 @@ namespace Ryujinx.Graphics.Shader.Translation
}
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
{
- return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | AggregateType.FP32, false);
+ int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
+ var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
+ {
+ AttributeType.Sint => AggregateType.S32,
+ AttributeType.Uint => AggregateType.U32,
+ _ => AggregateType.FP32
+ };
+
+ return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
}
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
{
diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index 1b99a609c..751ef5ebf 100644
--- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -140,6 +140,25 @@ namespace Ryujinx.Graphics.Vulkan
return _attachments[index];
}
+ public ComponentType GetAttachmentComponentType(int index)
+ {
+ if (_colors != null && (uint)index < _colors.Length)
+ {
+ var format = _colors[index].Info.Format;
+
+ if (format.IsSint())
+ {
+ return ComponentType.SignedInteger;
+ }
+ else if (format.IsUint())
+ {
+ return ComponentType.UnsignedInteger;
+ }
+ }
+
+ return ComponentType.Float;
+ }
+
public bool IsValidColorAttachment(int bindIndex)
{
return (uint)bindIndex < Constants.MaxRenderTargets && (_validColorAttachments & (1u << bindIndex)) != 0;
diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
index 31acfc9b6..0a4d365f2 100644
--- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
+++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
@@ -1,7 +1,20 @@
using Silk.NET.Vulkan;
+using System;
namespace Ryujinx.Graphics.Vulkan
{
+ [Flags]
+ enum PortabilitySubsetFlags
+ {
+ None = 0,
+
+ VertexBufferAlignment4B = 1,
+ NoTriangleFans = 1 << 1,
+ NoPointMode = 1 << 2,
+ No3DImageView = 1 << 3,
+ NoLodBias = 1 << 4
+ }
+
readonly struct HardwareCapabilities
{
public readonly bool SupportsIndexTypeUint8;
@@ -23,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly uint MaxSubgroupSize;
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
public readonly SampleCountFlags SupportedSampleCounts;
+ public readonly PortabilitySubsetFlags PortabilitySubset;
public HardwareCapabilities(
bool supportsIndexTypeUint8,
@@ -43,7 +57,8 @@ namespace Ryujinx.Graphics.Vulkan
uint minSubgroupSize,
uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages,
- SampleCountFlags supportedSampleCounts)
+ SampleCountFlags supportedSampleCounts,
+ PortabilitySubsetFlags portabilitySubset)
{
SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor;
@@ -64,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
MaxSubgroupSize = maxSubgroupSize;
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
SupportedSampleCounts = supportedSampleCounts;
+ PortabilitySubset = portabilitySubset;
}
}
}
diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs
index 14a636151..223bcc71e 100644
--- a/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -9,6 +9,13 @@ using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan
{
+ enum ComponentType
+ {
+ Float,
+ SignedInteger,
+ UnsignedInteger
+ }
+
class HelperShader : IDisposable
{
private const int UniformBufferAlignment = 256;
@@ -18,7 +25,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly ISampler _samplerNearest;
private readonly IProgram _programColorBlit;
private readonly IProgram _programColorBlitClearAlpha;
- private readonly IProgram _programColorClear;
+ private readonly IProgram _programColorClearF;
+ private readonly IProgram _programColorClearSI;
+ private readonly IProgram _programColorClearUI;
private readonly IProgram _programStrideChange;
private readonly IProgram _programConvertIndexBuffer;
private readonly IProgram _programConvertIndirectData;
@@ -63,10 +72,22 @@ namespace Ryujinx.Graphics.Vulkan
Array.Empty(),
Array.Empty());
- _programColorClear = gd.CreateProgramWithMinimalLayout(new[]
+ _programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
+ });
+
+ _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
+ {
+ new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
+ });
+
+ _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
+ {
+ new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
var strideChangeBindings = new ShaderBindings(
@@ -242,6 +263,7 @@ namespace Ryujinx.Graphics.Vulkan
int dstWidth,
int dstHeight,
VkFormat dstFormat,
+ ComponentType type,
Rectangle scissor)
{
const int ClearColorBufferSize = 16;
@@ -273,7 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
scissors[0] = scissor;
- _pipeline.SetProgram(_programColorClear);
+ IProgram program;
+
+ if (type == ComponentType.SignedInteger)
+ {
+ program = _programColorClearSI;
+ }
+ else if (type == ComponentType.UnsignedInteger)
+ {
+ program = _programColorClearUI;
+ }
+ else
+ {
+ program = _programColorClearF;
+ }
+
+ _pipeline.SetProgram(program);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
_pipeline.SetRenderTargetColorMasks(new uint[] { componentMask });
_pipeline.SetViewports(viewports, false);
@@ -948,7 +985,9 @@ namespace Ryujinx.Graphics.Vulkan
{
_programColorBlitClearAlpha.Dispose();
_programColorBlit.Dispose();
- _programColorClear.Dispose();
+ _programColorClearF.Dispose();
+ _programColorClearSI.Dispose();
+ _programColorClearUI.Dispose();
_programStrideChange.Dispose();
_programConvertIndexBuffer.Dispose();
_programConvertIndirectData.Dispose();
diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs
new file mode 100644
index 000000000..4fbae86e7
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Vulkan.MoltenVK
+{
+ enum MVKConfigLogLevel : int
+ {
+ None = 0,
+ Error = 1,
+ Warning = 2,
+ Info = 3,
+ Debug = 4
+ }
+
+ enum MVKConfigTraceVulkanCalls : int
+ {
+ None = 0,
+ Enter = 1,
+ EnterExit = 2,
+ Duration = 3
+ }
+
+ enum MVKConfigAutoGPUCaptureScope : int
+ {
+ None = 0,
+ Device = 1,
+ Frame = 2
+ }
+
+ [Flags]
+ enum MVKConfigAdvertiseExtensions : int
+ {
+ All = 0x00000001,
+ MoltenVK = 0x00000002,
+ WSI = 0x00000004,
+ Portability = 0x00000008
+ }
+
+ enum MVKVkSemaphoreSupportStyle : int
+ {
+ MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE = 0,
+ MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS_WHERE_SAFE = 1,
+ MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS = 2,
+ MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_CALLBACK = 3,
+ MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_MAX_ENUM = 0x7FFFFFFF
+ }
+
+ readonly struct Bool32
+ {
+ uint Value { get; }
+
+ public Bool32(uint value)
+ {
+ Value = value;
+ }
+
+ public Bool32(bool value)
+ {
+ Value = value ? 1u : 0u;
+ }
+
+ public static implicit operator bool(Bool32 val) => val.Value == 1;
+ public static implicit operator Bool32(bool val) => new Bool32(val);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct MVKConfiguration
+ {
+ public Bool32 DebugMode;
+ public Bool32 ShaderConversionFlipVertexY;
+ public Bool32 SynchronousQueueSubmits;
+ public Bool32 PrefillMetalCommandBuffers;
+ public uint MaxActiveMetalCommandBuffersPerQueue;
+ public Bool32 SupportLargeQueryPools;
+ public Bool32 PresentWithCommandBuffer;
+ public Bool32 SwapchainMagFilterUseNearest;
+ public ulong MetalCompileTimeout;
+ public Bool32 PerformanceTracking;
+ public uint PerformanceLoggingFrameCount;
+ public Bool32 DisplayWatermark;
+ public Bool32 SpecializedQueueFamilies;
+ public Bool32 SwitchSystemGPU;
+ public Bool32 FullImageViewSwizzle;
+ public uint DefaultGPUCaptureScopeQueueFamilyIndex;
+ public uint DefaultGPUCaptureScopeQueueIndex;
+ public Bool32 FastMathEnabled;
+ public MVKConfigLogLevel LogLevel;
+ public MVKConfigTraceVulkanCalls TraceVulkanCalls;
+ public Bool32 ForceLowPowerGPU;
+ public Bool32 SemaphoreUseMTLFence;
+ public MVKVkSemaphoreSupportStyle SemaphoreSupportStyle;
+ public MVKConfigAutoGPUCaptureScope AutoGPUCaptureScope;
+ public IntPtr AutoGPUCaptureOutputFilepath;
+ public Bool32 Texture1DAs2D;
+ public Bool32 PreallocateDescriptors;
+ public Bool32 UseCommandPooling;
+ public Bool32 UseMTLHeap;
+ public Bool32 LogActivityPerformanceInline;
+ public uint ApiVersionToAdvertise;
+ public MVKConfigAdvertiseExtensions AdvertiseExtensions;
+ public Bool32 ResumeLostDevice;
+ public Bool32 UseMetalArgumentBuffers;
+ }
+}
diff --git a/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
new file mode 100644
index 000000000..ca2fbfb94
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
@@ -0,0 +1,31 @@
+using Silk.NET.Vulkan;
+using System;
+using System.Runtime.Versioning;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Vulkan.MoltenVK
+{
+ [SupportedOSPlatform("macos")]
+ public static partial class MVKInitialization
+ {
+ [LibraryImport("libMoltenVK.dylib")]
+ private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize);
+
+ [LibraryImport("libMoltenVK.dylib")]
+ private static partial Result vkSetMoltenVKConfigurationMVK(IntPtr unusedInstance, in MVKConfiguration config, in IntPtr configSize);
+
+ public static void Initialize()
+ {
+ var configSize = (IntPtr)Marshal.SizeOf();
+
+ vkGetMoltenVKConfigurationMVK(IntPtr.Zero, out MVKConfiguration config, configSize);
+
+ config.UseMetalArgumentBuffers = true;
+
+ config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE;
+ config.SynchronousQueueSubmits = false;
+
+ vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index dfcb32d40..1c0c836bb 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -2,6 +2,7 @@
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
+using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -50,6 +51,11 @@ namespace Ryujinx.Graphics.Vulkan
private Auto _renderPass;
private int _writtenAttachmentCount;
+ private bool _framebufferUsingColorWriteMask;
+
+ private ITexture[] _preMaskColors;
+ private ITexture _preMaskDepthStencil;
+
private readonly DescriptorSetUpdater _descriptorSetUpdater;
private IndexBufferState _indexBuffer;
@@ -905,22 +911,35 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- SignalStateChange();
-
- if (writtenAttachments != _writtenAttachmentCount)
+ if (_framebufferUsingColorWriteMask)
{
- SignalAttachmentChange();
- _writtenAttachmentCount = writtenAttachments;
+ SetRenderTargetsInternal(_preMaskColors, _preMaskDepthStencil, true);
}
+ else
+ {
+ SignalStateChange();
+
+ if (writtenAttachments != _writtenAttachmentCount)
+ {
+ SignalAttachmentChange();
+ _writtenAttachmentCount = writtenAttachments;
+ }
+ }
+ }
+
+ private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
+ {
+ FramebufferParams?.UpdateModifications();
+ CreateFramebuffer(colors, depthStencil, filterWriteMasked);
+ CreateRenderPass();
+ SignalStateChange();
+ SignalAttachmentChange();
}
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{
- FramebufferParams?.UpdateModifications();
- CreateFramebuffer(colors, depthStencil);
- CreateRenderPass();
- SignalStateChange();
- SignalAttachmentChange();
+ _framebufferUsingColorWriteMask = false;
+ SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
}
public void SetRenderTargetScale(float scale)
@@ -1102,7 +1121,7 @@ namespace Ryujinx.Graphics.Vulkan
int vbSize = vertexBuffer.Buffer.Size;
- if (Gd.Vendor == Vendor.Amd && vertexBuffer.Stride > 0)
+ if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0)
{
// AMD has a bug where if offset + stride * count is greater than
// the size, then the last attribute will have the wrong value.
@@ -1119,7 +1138,8 @@ namespace Ryujinx.Graphics.Vulkan
buffer.Dispose();
- if ((vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
+ if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) &&
+ (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{
buffer = new VertexBufferState(
vb,
@@ -1259,8 +1279,62 @@ namespace Ryujinx.Graphics.Vulkan
_currentPipelineHandle = 0;
}
- private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil)
+ private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{
+ if (filterWriteMasked)
+ {
+ // TBDR GPUs don't work properly if the same attachment is bound to multiple targets,
+ // due to each attachment being a copy of the real attachment, rather than a direct write.
+
+ // Just try to remove duplicate attachments.
+ // Save a copy of the array to rebind when mask changes.
+
+ void maskOut()
+ {
+ if (!_framebufferUsingColorWriteMask)
+ {
+ _preMaskColors = colors.ToArray();
+ _preMaskDepthStencil = depthStencil;
+ }
+
+ // If true, then the framebuffer must be recreated when the mask changes.
+ _framebufferUsingColorWriteMask = true;
+ }
+
+ // Look for textures that are masked out.
+
+ for (int i = 0; i < colors.Length; i++)
+ {
+ if (colors[i] == null)
+ {
+ continue;
+ }
+
+ ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i];
+
+ for (int j = 0; j < i; j++)
+ {
+ // Check each binding for a duplicate binding before it.
+
+ if (colors[i] == colors[j])
+ {
+ // Prefer the binding with no write mask.
+ ref var vkBlend2 = ref _newState.Internal.ColorBlendAttachmentState[j];
+ if (vkBlend.ColorWriteMask == 0)
+ {
+ colors[i] = null;
+ maskOut();
+ }
+ else if (vkBlend2.ColorWriteMask == 0)
+ {
+ colors[j] = null;
+ maskOut();
+ }
+ }
+ }
+ }
+ }
+
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
UpdatePipelineAttachmentFormats();
}
diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index 2256c5422..e4bf4fffa 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -79,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
(int)FramebufferParams.Width,
(int)FramebufferParams.Height,
FramebufferParams.AttachmentFormats[index],
+ FramebufferParams.GetAttachmentComponentType(index),
ClearScissor);
}
else
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag
similarity index 100%
rename from Ryujinx.Graphics.Vulkan/Shaders/ColorClearFragmentShaderSource.frag
rename to Ryujinx.Graphics.Vulkan/Shaders/ColorClearFFragmentShaderSource.frag
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag
new file mode 100644
index 000000000..4254f4f86
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearSIFragmentShaderSource.frag
@@ -0,0 +1,9 @@
+#version 450 core
+
+layout (location = 0) in vec4 clear_colour;
+layout (location = 0) out ivec4 colour;
+
+void main()
+{
+ colour = floatBitsToInt(clear_colour);
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag
new file mode 100644
index 000000000..08a6b8648
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/Shaders/ColorClearUIFragmentShaderSource.frag
@@ -0,0 +1,9 @@
+#version 450 core
+
+layout (location = 0) in vec4 clear_colour;
+layout (location = 0) out uvec4 colour;
+
+void main()
+{
+ colour = floatBitsToUint(clear_colour);
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs
index 992f1e692..667e5a8b4 100644
--- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs
+++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs
@@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x3C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
- public static readonly byte[] ColorClearFragmentShaderSource = new byte[]
+ public static readonly byte[] ColorClearFFragmentShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -459,6 +459,68 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x0C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
+ public static readonly byte[] ColorClearSIFragmentShaderSource = new byte[]
+ {
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F,
+ 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61,
+ 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
+ };
+
+ public static readonly byte[] ColorClearUIFragmentShaderSource = new byte[]
+ {
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F,
+ 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61,
+ 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
+ };
+
public static readonly byte[] ColorClearVertexShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00,
diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index f4feecbcd..28fabb4fa 100644
--- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Vulkan
flags |= ImageCreateFlags.CreateCubeCompatibleBit;
}
- if (type == ImageType.Type3D)
+ if (type == ImageType.Type3D && !gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView))
{
flags |= ImageCreateFlags.Create2DArrayCompatibleBit;
}
diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs
index c58c9fc5d..a9e1ed361 100644
--- a/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -94,8 +94,14 @@ namespace Ryujinx.Graphics.Vulkan
var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
- unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType)
+ unsafe Auto CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags = 0)
{
+ var usage = new ImageViewUsageCreateInfo()
+ {
+ SType = StructureType.ImageViewUsageCreateInfo,
+ Usage = usageFlags
+ };
+
var imageCreateInfo = new ImageViewCreateInfo()
{
SType = StructureType.ImageViewCreateInfo,
@@ -103,7 +109,8 @@ namespace Ryujinx.Graphics.Vulkan
ViewType = viewType,
Format = format,
Components = cm,
- SubresourceRange = sr
+ SubresourceRange = sr,
+ PNext = usageFlags == 0 ? null : &usage
};
gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
@@ -124,9 +131,21 @@ namespace Ryujinx.Graphics.Vulkan
// Framebuffer attachments also require 3D textures to be bound as 2D array.
if (info.Target == Target.Texture3D)
{
- subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
+ if (gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView))
+ {
+ if (levels == 1 && (info.Format.IsRtColorCompatible() || info.Format.IsDepthOrStencil()))
+ {
+ subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, 1);
- _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray);
+ _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2D, ImageUsageFlags.ColorAttachmentBit);
+ }
+ }
+ else
+ {
+ subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
+
+ _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray);
+ }
}
Valid = true;
@@ -353,7 +372,7 @@ namespace Ryujinx.Graphics.Vulkan
}
if (VulkanConfiguration.UseSlowSafeBlitOnAmd &&
- _gd.Vendor == Vendor.Amd &&
+ (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) &&
src.Info.Target == Target.Texture2D &&
dst.Info.Target == Target.Texture2D &&
!dst.Info.Format.IsDepthOrStencil())
diff --git a/Ryujinx.Graphics.Vulkan/Vendor.cs b/Ryujinx.Graphics.Vulkan/Vendor.cs
index 7a6e0e600..087d6e9dc 100644
--- a/Ryujinx.Graphics.Vulkan/Vendor.cs
+++ b/Ryujinx.Graphics.Vulkan/Vendor.cs
@@ -5,9 +5,12 @@ namespace Ryujinx.Graphics.Vulkan
enum Vendor
{
Amd,
+ ImgTec,
Intel,
Nvidia,
+ ARM,
Qualcomm,
+ Apple,
Unknown
}
@@ -21,7 +24,10 @@ namespace Ryujinx.Graphics.Vulkan
return id switch
{
0x1002 => Vendor.Amd,
+ 0x1010 => Vendor.ImgTec,
+ 0x106B => Vendor.Apple,
0x10DE => Vendor.Nvidia,
+ 0x13B5 => Vendor.ARM,
0x8086 => Vendor.Intel,
0x5143 => Vendor.Qualcomm,
_ => Vendor.Unknown
@@ -34,6 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
{
0x1002 => "AMD",
0x1010 => "ImgTec",
+ 0x106B => "Apple",
0x10DE => "NVIDIA",
0x13B5 => "ARM",
0x1AE0 => "Google",
diff --git a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
index 661bb7747..7a0220108 100644
--- a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
@@ -82,9 +82,9 @@ namespace Ryujinx.Graphics.Vulkan
}
_buffer = autoBuffer;
- }
- state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
+ state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
+ }
return;
}
diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index 68462825f..fe9462aa3 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -389,6 +389,18 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresCustomBorderColorSupported;
}
+ PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
+ {
+ SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
+ };
+
+ if (supportedExtensions.Contains("VK_EXT_robustness2"))
+ {
+ supportedFeaturesRobustness2.PNext = features2.PNext;
+
+ features2.PNext = &supportedFeaturesRobustness2;
+ }
+
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2);
var supportedFeatures = features2.Features;
@@ -428,14 +440,17 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresTransformFeedback;
- var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
+ if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
- SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
- PNext = pExtendedFeatures,
- NullDescriptor = true
- };
+ var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
+ {
+ SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
+ PNext = pExtendedFeatures,
+ NullDescriptor = supportedFeaturesRobustness2.NullDescriptor
+ };
- pExtendedFeatures = &featuresRobustness2;
+ pExtendedFeatures = &featuresRobustness2;
+ }
var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT()
{
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 5c77cb001..f1922efed 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -3,6 +3,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Graphics.Vulkan.Queries;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT;
@@ -77,6 +78,8 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsAmdWindows { get; private set; }
internal bool IsIntelWindows { get; private set; }
internal bool IsAmdGcn { get; private set; }
+ internal bool IsMoltenVk { get; private set; }
+ internal bool IsTBDR { get; private set; }
public string GpuVendor { get; private set; }
public string GpuRenderer { get; private set; }
public string GpuVersion { get; private set; }
@@ -93,6 +96,14 @@ namespace Ryujinx.Graphics.Vulkan
Shaders = new HashSet();
Textures = new HashSet();
Samplers = new HashSet();
+
+ if (OperatingSystem.IsMacOS())
+ {
+ MVKInitialization.Initialize();
+
+ // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
+ IsMoltenVk = true;
+ }
}
private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex)
@@ -161,7 +172,10 @@ namespace Ryujinx.Graphics.Vulkan
properties2.PNext = &propertiesTransformFeedback;
}
- Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2);
+ PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new PhysicalDevicePortabilitySubsetPropertiesKHR()
+ {
+ SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr
+ };
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
{
@@ -183,6 +197,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
};
+ PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR()
+ {
+ SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
+ };
+
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
features2.PNext = &featuresRobustness2;
@@ -200,8 +219,31 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresCustomBorderColor;
}
+ bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset");
+
+ if (usePortability)
+ {
+ propertiesPortabilitySubset.PNext = properties2.PNext;
+ properties2.PNext = &propertiesPortabilitySubset;
+
+ featuresPortabilitySubset.PNext = features2.PNext;
+ features2.PNext = &featuresPortabilitySubset;
+ }
+
+ Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2);
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
+ var portabilityFlags = PortabilitySubsetFlags.None;
+
+ if (usePortability)
+ {
+ portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0;
+ portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
+ portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
+ portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
+ portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
+ }
+
bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat;
@@ -224,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport,
- featuresRobustness2.NullDescriptor,
+ featuresRobustness2.NullDescriptor || IsMoltenVk,
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries,
@@ -232,7 +274,8 @@ namespace Ryujinx.Graphics.Vulkan
propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
- supportedSampleCounts);
+ supportedSampleCounts,
+ portabilityFlags);
MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount);
@@ -413,6 +456,36 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.R4G4B4A4Unorm);
+ bool supportsAstcFormats = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
+ GAL.Format.Astc4x4Unorm,
+ GAL.Format.Astc5x4Unorm,
+ GAL.Format.Astc5x5Unorm,
+ GAL.Format.Astc6x5Unorm,
+ GAL.Format.Astc6x6Unorm,
+ GAL.Format.Astc8x5Unorm,
+ GAL.Format.Astc8x6Unorm,
+ GAL.Format.Astc8x8Unorm,
+ GAL.Format.Astc10x5Unorm,
+ GAL.Format.Astc10x6Unorm,
+ GAL.Format.Astc10x8Unorm,
+ GAL.Format.Astc10x10Unorm,
+ GAL.Format.Astc12x10Unorm,
+ GAL.Format.Astc12x12Unorm,
+ GAL.Format.Astc4x4Srgb,
+ GAL.Format.Astc5x4Srgb,
+ GAL.Format.Astc5x5Srgb,
+ GAL.Format.Astc6x5Srgb,
+ GAL.Format.Astc6x6Srgb,
+ GAL.Format.Astc8x5Srgb,
+ GAL.Format.Astc8x6Srgb,
+ GAL.Format.Astc8x8Srgb,
+ GAL.Format.Astc10x5Srgb,
+ GAL.Format.Astc10x6Srgb,
+ GAL.Format.Astc10x8Srgb,
+ GAL.Format.Astc10x10Srgb,
+ GAL.Format.Astc12x10Srgb,
+ GAL.Format.Astc12x12Srgb);
+
PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
{
SType = StructureType.PhysicalDeviceVulkan12Features
@@ -434,7 +507,9 @@ namespace Ryujinx.Graphics.Vulkan
GpuVendor,
hasFrontFacingBug: IsIntelWindows,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
- supportsAstcCompression: features2.Features.TextureCompressionAstcLdr,
+ needsFragmentOutputSpecialization: IsMoltenVk,
+ reduceShaderPrecision: IsMoltenVk,
+ supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats,
supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat,
supportsBc67Compression: supportsBc67CompressionFormat,
@@ -515,12 +590,13 @@ namespace Ryujinx.Graphics.Vulkan
IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec;
GpuVendor = vendorName;
GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
- IsAmdGcn = Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
+ IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
}
@@ -531,6 +607,7 @@ namespace Ryujinx.Graphics.Vulkan
{
GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
+ GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans) ? GAL.PrimitiveTopology.Triangles : topology,
_ => topology
};
}
@@ -540,6 +617,7 @@ namespace Ryujinx.Graphics.Vulkan
return topology switch
{
GAL.PrimitiveTopology.Quads => true,
+ GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans),
_ => false
};
}
@@ -553,7 +631,13 @@ namespace Ryujinx.Graphics.Vulkan
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{
- if (Vendor != Vendor.Nvidia)
+ if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B))
+ {
+ alignment = 4;
+
+ return true;
+ }
+ else if (Vendor != Vendor.Nvidia)
{
// Vulkan requires that vertex attributes are globally aligned by their component size,
// so buffer strides that don't divide by the largest scalar element are invalid.