Geometry shader passthrough emulation

This commit is contained in:
gdk 2022-04-14 22:30:54 -03:00 committed by riperiperi
parent 2713703d45
commit dd54eb4be1
13 changed files with 93 additions and 53 deletions

View file

@ -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;

View file

@ -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";

View file

@ -105,64 +105,37 @@ namespace Ryujinx.Graphics.Gpu.Shader
};
}
/// <summary>
/// Queries host about the presence of the FrontFacing built-in variable bug.
/// </summary>
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
/// <summary>
/// Queries host about the presence of the vector indexing bug.
/// </summary>
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
/// <summary>
/// Queries host storage buffer alignment required.
/// </summary>
/// <returns>Host storage buffer alignment in bytes</returns>
/// <inheritdoc/>
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
/// <summary>
/// Queries host support for texture formats with BGRA component order (such as BGRA8).
/// </summary>
/// <returns>True if BGRA formats are supported, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
/// <summary>
/// Queries host support for fragment shader ordering critical sections on the shader code.
/// </summary>
/// <returns>True if fragment shader interlock is supported, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
/// <summary>
/// Queries host support for fragment shader ordering scoped critical sections on the shader code.
/// </summary>
/// <returns>True if fragment shader ordering is supported, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
/// <summary>
/// Queries host support for readable images without a explicit format declaration on the shader.
/// </summary>
/// <returns>True if formatted image load is supported, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
/// <inheritdoc/>
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
/// <summary>
/// Queries host GPU non-constant texture offset support.
/// </summary>
/// <returns>True if the GPU and driver supports non-constant texture offsets, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
/// <summary>
/// Queries host GPU shader ballot support.
/// </summary>
/// <returns>True if the GPU and driver supports shader ballot, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
/// <summary>
/// Queries host GPU texture shadow LOD support.
/// </summary>
/// <returns>True if the GPU and driver supports texture shadow LOD, false otherwise</returns>
/// <inheritdoc/>
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
/// <summary>

View file

@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.OpenGL
private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture"));
private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock"));
private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering"));
private static readonly Lazy<bool> _supportsGeometryShaderPassthrough = new Lazy<bool>(() => HasExtension("GL_NV_geometry_shader_passthrough"));
private static readonly Lazy<bool> _supportsImageLoadFormatted = new Lazy<bool>(() => HasExtension("GL_EXT_shader_image_load_formatted"));
private static readonly Lazy<bool> _supportsIndirectParameters = new Lazy<bool>(() => HasExtension("GL_ARB_indirect_parameters"));
private static readonly Lazy<bool> _supportsParallelShaderCompile = new Lazy<bool>(() => 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;

View file

@ -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,

View file

@ -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)

View file

@ -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);
}

View file

@ -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)
{

View file

@ -197,6 +197,15 @@ namespace Ryujinx.Graphics.Shader
return false;
}
/// <summary>
/// Queries host GPU geometry shader passthrough support.
/// </summary>
/// <returns>True if the GPU and driver supports geometry shader passthrough, false otherwise</returns>
bool QueryHostSupportsGeometryShaderPassthrough()
{
return true;
}
/// <summary>
/// Queries host support for readable images without a explicit format declaration on the shader.
/// </summary>

View file

@ -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
{

View file

@ -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();

View file

@ -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; }

View file

@ -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,