diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index 33c828d19..bbced2dde 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsR4G4Format;
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel;
+ public readonly bool SupportsGeometryShaderPassthrough;
public readonly bool SupportsImageLoadFormatted;
public readonly bool SupportsMismatchingViewFormat;
public readonly bool SupportsNonConstantTextureOffset;
@@ -44,6 +45,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsR4G4Format,
bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel,
+ bool supportsGeometryShaderPassthrough,
bool supportsImageLoadFormatted,
bool supportsMismatchingViewFormat,
bool supportsNonConstantTextureOffset,
@@ -69,6 +71,7 @@ namespace Ryujinx.Graphics.GAL
SupportsR4G4Format = supportsR4G4Format;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
+ SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
SupportsImageLoadFormatted = supportsImageLoadFormatted;
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index 799838ec1..bed5b774c 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
- private const uint CodeGenVersion = 1;
+ private const uint CodeGenVersion = 2;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index 7cf7b6e99..8c28af5b8 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -105,64 +105,37 @@ namespace Ryujinx.Graphics.Gpu.Shader
};
}
- ///
- /// Queries host about the presence of the FrontFacing built-in variable bug.
- ///
- /// True if the bug is present on the host device used, false otherwise
+ ///
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
- ///
- /// Queries host about the presence of the vector indexing bug.
- ///
- /// True if the bug is present on the host device used, false otherwise
+ ///
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
- ///
- /// Queries host storage buffer alignment required.
- ///
- /// Host storage buffer alignment in bytes
+ ///
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
- ///
- /// Queries host support for texture formats with BGRA component order (such as BGRA8).
- ///
- /// True if BGRA formats are supported, false otherwise
+ ///
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
- ///
- /// Queries host support for fragment shader ordering critical sections on the shader code.
- ///
- /// True if fragment shader interlock is supported, false otherwise
+ ///
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
- ///
- /// Queries host support for fragment shader ordering scoped critical sections on the shader code.
- ///
- /// True if fragment shader ordering is supported, false otherwise
+ ///
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
- ///
- /// Queries host support for readable images without a explicit format declaration on the shader.
- ///
- /// True if formatted image load is supported, false otherwise
+ ///
+ public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
+
+ ///
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
- ///
- /// Queries host GPU non-constant texture offset support.
- ///
- /// True if the GPU and driver supports non-constant texture offsets, false otherwise
+ ///
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
- ///
- /// Queries host GPU shader ballot support.
- ///
- /// True if the GPU and driver supports shader ballot, false otherwise
+ ///
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
- ///
- /// Queries host GPU texture shadow LOD support.
- ///
- /// True if the GPU and driver supports texture shadow LOD, false otherwise
+ ///
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
///
diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
index 773c9f634..c20eccf8f 100644
--- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
+++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.OpenGL
private static readonly Lazy _supportsDrawTexture = new Lazy(() => HasExtension("GL_NV_draw_texture"));
private static readonly Lazy _supportsFragmentShaderInterlock = new Lazy(() => HasExtension("GL_ARB_fragment_shader_interlock"));
private static readonly Lazy _supportsFragmentShaderOrdering = new Lazy(() => HasExtension("GL_INTEL_fragment_shader_ordering"));
+ private static readonly Lazy _supportsGeometryShaderPassthrough = new Lazy(() => HasExtension("GL_NV_geometry_shader_passthrough"));
private static readonly Lazy _supportsImageLoadFormatted = new Lazy(() => HasExtension("GL_EXT_shader_image_load_formatted"));
private static readonly Lazy _supportsIndirectParameters = new Lazy(() => HasExtension("GL_ARB_indirect_parameters"));
private static readonly Lazy _supportsParallelShaderCompile = new Lazy(() => HasExtension("GL_ARB_parallel_shader_compile"));
@@ -47,6 +48,7 @@ namespace Ryujinx.Graphics.OpenGL
public static bool SupportsDrawTexture => _supportsDrawTexture.Value;
public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value;
public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value;
+ public static bool SupportsGeometryShaderPassthrough => _supportsGeometryShaderPassthrough.Value;
public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value;
public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value;
public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value;
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index f670fee07..78335a133 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -110,6 +110,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsR4G4Format: false,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
+ supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index c3402c582..d64c74520 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable");
}
- if (context.Config.GpPassthrough)
+ if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable");
}
@@ -127,11 +127,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
if (context.Config.Stage == ShaderStage.Geometry)
{
- string inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology().ToGlslString();
+ InputTopology inputTopology = context.Config.GpuAccessor.QueryPrimitiveTopology();
+ string inPrimitive = inputTopology.ToGlslString();
context.AppendLine($"layout ({inPrimitive}) in;");
- if (context.Config.GpPassthrough)
+ if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.AppendLine($"layout (passthrough) in gl_PerVertex");
context.EnterScope();
@@ -144,7 +145,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
string outPrimitive = context.Config.OutputTopology.ToGlslString();
- int maxOutputVertices = context.Config.MaxOutputVertices;
+ int maxOutputVertices = context.Config.GpPassthrough
+ ? inputTopology.ToInputVertices()
+ : context.Config.MaxOutputVertices;
context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;");
}
@@ -563,7 +566,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
};
}
- string pass = (context.Config.PassthroughAttributes & (1 << attr)) != 0 ? "passthrough, " : string.Empty;
+ bool passthrough = (context.Config.PassthroughAttributes & (1 << attr)) != 0;
+ string pass = passthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough() ? "passthrough, " : string.Empty;
string name = $"{DefaultNames.IAttributePrefix}{attr}";
if (context.Config.TransformFeedbackEnabled && context.Config.Stage != ShaderStage.Vertex)
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 44f8a5bd2..911137649 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -425,7 +425,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var spvType = context.TypePointer(StorageClass.Input, attrType);
var spvVar = context.Variable(spvType, StorageClass.Input);
- if (context.Config.PassthroughAttributes != 0)
+ if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
@@ -534,7 +534,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
- if (context.Config.GpPassthrough)
+ if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
builtInPassthrough = true;
}
@@ -581,7 +581,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (!isOutAttr)
{
- if (!perPatch && (context.Config.PassthroughAttributes & (1 << location)) != 0)
+ if (!perPatch &&
+ (context.Config.PassthroughAttributes & (1 << location)) != 0 &&
+ context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
@@ -646,7 +648,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
else
{
- if ((context.Config.PassthroughAttributes & (1 << location)) != 0)
+ if ((context.Config.PassthroughAttributes & (1 << location)) != 0 &&
+ context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
index 666fcc42e..2755d3ab0 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
@@ -70,7 +70,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
context.AddCapability(Capability.Geometry);
- if (config.GpPassthrough)
+ if (config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.AddExtension("SPV_NV_geometry_shader_passthrough");
context.AddCapability(Capability.GeometryShaderPassthroughNV);
@@ -230,7 +230,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
_ => throw new InvalidOperationException($"Invalid output topology \"{context.Config.OutputTopology}\".")
});
- context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.MaxOutputVertices);
+ int maxOutputVertices = context.Config.GpPassthrough ? context.InputVertices : context.Config.MaxOutputVertices;
+
+ context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)maxOutputVertices);
}
else if (context.Config.Stage == ShaderStage.Fragment)
{
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 87288b2e6..e6a62302a 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -197,6 +197,15 @@ namespace Ryujinx.Graphics.Shader
return false;
}
+ ///
+ /// Queries host GPU geometry shader passthrough support.
+ ///
+ /// True if the GPU and driver supports geometry shader passthrough, false otherwise
+ bool QueryHostSupportsGeometryShaderPassthrough()
+ {
+ return true;
+ }
+
///
/// Queries host support for readable images without a explicit format declaration on the shader.
///
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
index 8bf98963d..ffe9d9e75 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Translation
AggregateType elemType;
- if (!isOutAttr)
+ if (config.Stage == ShaderStage.Vertex && !isOutAttr)
{
elemType = config.GpuAccessor.QueryAttributeType(location) switch
{
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index dbf4eb125..38c503e67 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Numerics;
using System.Runtime.CompilerServices;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -232,6 +233,44 @@ namespace Ryujinx.Graphics.Shader.Translation
{
PrepareForVertexReturn();
}
+ else if (Config.Stage == ShaderStage.Geometry)
+ {
+ void WriteOutput(int index, int primIndex)
+ {
+ Operand x = this.LoadAttribute(Const(index), Const(0), Const(primIndex));
+ Operand y = this.LoadAttribute(Const(index + 4), Const(0), Const(primIndex));
+ Operand z = this.LoadAttribute(Const(index + 8), Const(0), Const(primIndex));
+ Operand w = this.LoadAttribute(Const(index + 12), Const(0), Const(primIndex));
+
+ this.Copy(Attribute(index), x);
+ this.Copy(Attribute(index + 4), y);
+ this.Copy(Attribute(index + 8), z);
+ this.Copy(Attribute(index + 12), w);
+ }
+
+ if (Config.GpPassthrough)
+ {
+ int inputVertices = Config.GpuAccessor.QueryPrimitiveTopology().ToInputVertices();
+
+ for (int primIndex = 0; primIndex < inputVertices; primIndex++)
+ {
+ WriteOutput(AttributeConsts.PositionX, primIndex);
+
+ int passthroughAttributes = Config.PassthroughAttributes;
+ while (passthroughAttributes != 0)
+ {
+ int index = BitOperations.TrailingZeroCount(passthroughAttributes);
+ WriteOutput(AttributeConsts.UserAttributeBase + index * 16, primIndex);
+ Config.SetOutputUserAttribute(index, perPatch: false);
+ passthroughAttributes &= ~(1 << index);
+ }
+
+ this.EmitVertex();
+ }
+
+ this.EndPrimitive();
+ }
+ }
else if (Config.Stage == ShaderStage.Fragment)
{
bool supportsBgra = Config.GpuAccessor.QueryHostSupportsBgraFormat();
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index e6e95240c..c5e138879 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderStage Stage { get; }
public bool GpPassthrough { get; }
+ public bool GpPassthroughWithHostSupport => GpPassthrough && GpuAccessor.QueryHostSupportsGeometryShaderPassthrough();
public bool LastInVertexPipeline { get; private set; }
public int ThreadsPerInputPrimitive { get; }
diff --git a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs
index 4153edcaf..177bb0824 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
internal bool SupportsCustomBorderColor { get; private set; }
internal bool SupportsIndirectParameters { get; private set; }
internal bool SupportsFragmentShaderInterlock { get; private set; }
+ internal bool SupportsGeometryShaderPassthrough { get; private set; }
internal bool SupportsSubgroupSizeControl { get; private set; }
internal uint QueueFamilyIndex { get; private set; }
@@ -123,6 +124,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color");
SupportsIndirectParameters = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName);
SupportsFragmentShaderInterlock = supportedExtensions.Contains("VK_EXT_fragment_shader_interlock");
+ SupportsGeometryShaderPassthrough = supportedExtensions.Contains("VK_NV_geometry_shader_passthrough");
SupportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control");
if (api.TryGetDeviceExtension(_instance, _device, out KhrSwapchain swapchainApi))
@@ -311,6 +313,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsR4G4Format: false,
supportsFragmentShaderInterlock: SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false,
+ supportsGeometryShaderPassthrough: SupportsGeometryShaderPassthrough,
supportsImageLoadFormatted: features.ShaderStorageImageReadWithoutFormat,
supportsMismatchingViewFormat: true,
supportsNonConstantTextureOffset: false,