Use vector transform feedback outputs if possible (#3832)

This commit is contained in:
gdkchan 2022-11-12 20:20:40 -03:00 committed by GitHub
parent 51a27032f0
commit 9daf029f35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 180 additions and 82 deletions

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3833; private const uint CodeGenVersion = 3831;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View file

@ -10,12 +10,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public StructuredFunction CurrentFunction { get; set; } public StructuredFunction CurrentFunction { get; set; }
public StructuredProgramInfo Info { get; }
public ShaderConfig Config { get; } public ShaderConfig Config { get; }
public OperandManager OperandManager { get; } public OperandManager OperandManager { get; }
private readonly StructuredProgramInfo _info;
private readonly StringBuilder _sb; private readonly StringBuilder _sb;
private int _level; private int _level;
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) public CodeGenContext(StructuredProgramInfo info, ShaderConfig config)
{ {
_info = info; Info = info;
Config = config; Config = config;
OperandManager = new OperandManager(); OperandManager = new OperandManager();
@ -72,19 +72,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public StructuredFunction GetFunction(int id) public StructuredFunction GetFunction(int id)
{ {
return _info.Functions[id]; return Info.Functions[id];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
{
int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component;
return _info.TransformFeedbackOutputs[index];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location)
{
int index = location / 4;
return _info.TransformFeedbackOutputs[index];
} }
private void UpdateIndentation() private void UpdateIndentation()

View file

@ -210,7 +210,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
{ {
var tfOutput = context.GetTransformFeedbackOutput(AttributeConsts.PositionX); var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex");
@ -604,19 +604,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
{ {
for (int c = 0; c < 4; c++) int attrOffset = AttributeConsts.UserAttributeBase + attr * 16;
int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
if (components > 1)
{ {
char swzMask = "xyzw"[c]; string type = components switch
{
2 => "vec2",
3 => "vec3",
4 => "vec4",
_ => "float"
};
string xfb = string.Empty; string xfb = string.Empty;
var tfOutput = context.GetTransformFeedbackOutput(attr, c); var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
} }
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};");
}
else
{
for (int c = 0; c < 4; c++)
{
char swzMask = "xyzw"[c];
string xfb = string.Empty;
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4);
if (tfOutput.Valid)
{
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
}
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
}
} }
} }
else else

View file

@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute()) if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute())
{ {
bool perPatch = operand.Type == OperandType.AttributePerPatch; bool perPatch = operand.Type == OperandType.AttributePerPatch;
dest = OperandManager.GetOutAttributeName(operand.Value, context.Config, perPatch); dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch);
} }
else else
{ {

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
} }
else if (node is AstOperand operand) else if (node is AstOperand operand)
{ {
return context.OperandManager.GetExpression(operand, context.Config); return context.OperandManager.GetExpression(context, operand);
} }
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");

View file

@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (src2 is AstOperand operand && operand.Type == OperandType.Constant) if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{ {
int attrOffset = baseAttr.Value + (operand.Value << 2); int attrOffset = baseAttr.Value + (operand.Value << 2);
return OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: false, indexExpr); return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr);
} }
else else
{ {
@ -332,7 +332,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (src2 is AstOperand operand && operand.Type == OperandType.Constant) if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{ {
int attrOffset = baseAttr.Value + (operand.Value << 2); int attrOffset = baseAttr.Value + (operand.Value << 2);
attrName = OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: true); attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true);
} }
else else
{ {

View file

@ -103,15 +103,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return name; return name;
} }
public string GetExpression(AstOperand operand, ShaderConfig config) public string GetExpression(CodeGenContext context, AstOperand operand)
{ {
return operand.Type switch return operand.Type switch
{ {
OperandType.Argument => GetArgumentName(operand.Value), OperandType.Argument => GetArgumentName(operand.Value),
OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false), OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false),
OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true), OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true),
OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
OperandType.ConstantBuffer => GetConstantBufferName(operand, config), OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
OperandType.LocalVariable => _locals[operand], OperandType.LocalVariable => _locals[operand],
OperandType.Undefined => DefaultNames.UndefinedName, OperandType.Undefined => DefaultNames.UndefinedName,
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
@ -153,13 +153,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
} }
public static string GetOutAttributeName(int value, ShaderConfig config, bool perPatch) public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch)
{ {
return GetAttributeName(value, config, perPatch, isOutAttr: true); return GetAttributeName(context, value, perPatch, isOutAttr: true);
} }
public static string GetAttributeName(int value, ShaderConfig config, bool perPatch, bool isOutAttr = false, string indexExpr = "0") public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
{ {
ShaderConfig config = context.Config;
if ((value & AttributeConsts.LoadOutputMask) != 0) if ((value & AttributeConsts.LoadOutputMask) != 0)
{ {
isOutAttr = true; isOutAttr = true;
@ -192,6 +194,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
{ {
int attrOffset = value;
value -= AttributeConsts.UserAttributeBase; value -= AttributeConsts.UserAttributeBase;
string prefix = isOutAttr string prefix = isOutAttr
@ -215,14 +218,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
((config.LastInVertexPipeline && isOutAttr) || ((config.LastInVertexPipeline && isOutAttr) ||
(config.Stage == ShaderStage.Fragment && !isOutAttr))) (config.Stage == ShaderStage.Fragment && !isOutAttr)))
{ {
string name = $"{prefix}{(value >> 4)}_{swzMask}"; int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}";
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
{ {
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
} }
return name; return components > 1 ? name + '.' + swzMask : name;
} }
else else
{ {

View file

@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private const uint SpirvVersionRevision = 0; private const uint SpirvVersionRevision = 0;
private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision; private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision;
private readonly StructuredProgramInfo _info; public StructuredProgramInfo Info { get; }
public ShaderConfig Config { get; } public ShaderConfig Config { get; }
@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
GeneratorPool<Instruction> instPool, GeneratorPool<Instruction> instPool,
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool) GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
{ {
_info = info; Info = info;
Config = config; Config = config;
if (config.Stage == ShaderStage.Geometry) if (config.Stage == ShaderStage.Geometry)
@ -317,6 +317,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
attrOffset = attr; attrOffset = attr;
type = elemType; type = elemType;
if (Config.LastInPipeline && isOutAttr)
{
int components = Info.GetTransformFeedbackOutputComponents(attr);
if (components > 1)
{
attrOffset &= ~0xf;
type = AggregateType.Vector | AggregateType.FP32;
attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false);
}
}
} }
ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset]; ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset];
@ -536,18 +548,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return _functions[funcIndex]; return _functions[funcIndex];
} }
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
{
int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component;
return _info.TransformFeedbackOutputs[index];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location)
{
int index = location / 4;
return _info.TransformFeedbackOutputs[index];
}
public Instruction GetType(AggregateType type, int length = 1) public Instruction GetType(AggregateType type, int length = 1)
{ {
if (type.HasFlag(AggregateType.Array)) if (type.HasFlag(AggregateType.Array))

View file

@ -440,11 +440,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
PixelImap iq = PixelImap.Unused; PixelImap iq = PixelImap.Unused;
if (context.Config.Stage == ShaderStage.Fragment && if (context.Config.Stage == ShaderStage.Fragment)
attr >= AttributeConsts.UserAttributeBase &&
attr < AttributeConsts.UserAttributeEnd)
{ {
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
{
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
}
else
{
AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
{
iq = PixelImap.Constant;
}
}
} }
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
@ -516,7 +527,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
((isOutAttr && context.Config.LastInVertexPipeline) || ((isOutAttr && context.Config.LastInVertexPipeline) ||
(!isOutAttr && context.Config.Stage == ShaderStage.Fragment))) (!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
{ {
DeclareInputOrOutput(context, attr, (attr >> 2) & 3, isOutAttr, iq); DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
return; return;
} }
@ -572,7 +583,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
{ {
var tfOutput = context.GetTransformFeedbackOutput(attrInfo.BaseValue); var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
@ -595,24 +606,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
if (!isOutAttr) if (!isOutAttr &&
!perPatch &&
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{ {
if (!perPatch && context.Decorate(spvVar, Decoration.PassthroughNV);
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
switch (iq)
{
case PixelImap.Constant:
context.Decorate(spvVar, Decoration.Flat);
break;
case PixelImap.ScreenLinear:
context.Decorate(spvVar, Decoration.NoPerspective);
break;
}
} }
} }
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
@ -621,22 +620,52 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
} }
if (!isOutAttr)
{
switch (iq)
{
case PixelImap.Constant:
context.Decorate(spvVar, Decoration.Flat);
break;
case PixelImap.ScreenLinear:
context.Decorate(spvVar, Decoration.NoPerspective);
break;
}
}
context.AddGlobalVariable(spvVar); context.AddGlobalVariable(spvVar);
dict.Add(attrInfo.BaseValue, spvVar); dict.Add(attrInfo.BaseValue, spvVar);
} }
private static void DeclareInputOrOutput(CodeGenContext context, int attr, int component, bool isOutAttr, PixelImap iq = PixelImap.Unused) private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
{ {
var dict = isOutAttr ? context.Outputs : context.Inputs; var dict = isOutAttr ? context.Outputs : context.Inputs;
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
bool hasComponent = true;
int component = (attr >> 2) & 3;
int components = 1;
var type = attrInfo.Type & AggregateType.ElementTypeMask;
if (context.Config.LastInPipeline && isOutAttr)
{
components = context.Info.GetTransformFeedbackOutputComponents(attr);
if (components > 1)
{
attr &= ~0xf;
type = AggregateType.Vector | AggregateType.FP32;
hasComponent = false;
}
}
if (dict.ContainsKey(attr)) if (dict.ContainsKey(attr))
{ {
return; return;
} }
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
var attrType = context.GetType(attrInfo.Type & AggregateType.ElementTypeMask); var attrType = context.GetType(type, components);
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
{ {
@ -656,11 +685,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
int location = (attr - AttributeConsts.UserAttributeBase) / 16; int location = (attr - AttributeConsts.UserAttributeBase) / 16;
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
if (hasComponent)
{
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
}
if (isOutAttr) if (isOutAttr)
{ {
var tfOutput = context.GetTransformFeedbackOutput(location, component); var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);

View file

@ -62,10 +62,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddCapability(Capability.TransformFeedback); context.AddCapability(Capability.TransformFeedback);
} }
if (config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) if (config.Stage == ShaderStage.Fragment)
{ {
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); if (context.Info.Inputs.Contains(AttributeConsts.Layer))
context.AddExtension("SPV_EXT_fragment_shader_interlock"); {
context.AddCapability(Capability.Geometry);
}
if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock())
{
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
context.AddExtension("SPV_EXT_fragment_shader_interlock");
}
} }
else if (config.Stage == ShaderStage.Geometry) else if (config.Stage == ShaderStage.Geometry)
{ {

View file

@ -71,12 +71,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
for (int j = 0; j < locations.Length; j++) for (int i = 0; i < locations.Length; i++)
{ {
byte location = locations[j]; byte location = locations[i];
if (location < 0xc0) if (location < 0xc0)
{ {
context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, j * 4, stride); context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
} }
} }
} }

View file

@ -42,5 +42,40 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
} }
public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
{
int index = attr / 4;
return TransformFeedbackOutputs[index];
}
public int GetTransformFeedbackOutputComponents(int attr)
{
int index = attr / 4;
int baseIndex = index & ~3;
int count = 1;
for (; count < 4; count++)
{
ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1];
ref var curr = ref TransformFeedbackOutputs[baseIndex + count];
int prevOffset = prev.Offset;
int currOffset = curr.Offset;
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
{
break;
}
}
if (baseIndex + count <= index)
{
return 1;
}
return count;
}
} }
} }

View file

@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
public bool GpPassthrough { get; } public bool GpPassthrough { get; }
public bool LastInPipeline { get; private set; }
public bool LastInVertexPipeline { get; private set; } public bool LastInVertexPipeline { get; private set; }
public int ThreadsPerInputPrimitive { get; } public int ThreadsPerInputPrimitive { get; }
@ -143,6 +144,7 @@ namespace Ryujinx.Graphics.Shader.Translation
OmapSampleMask = header.OmapSampleMask; OmapSampleMask = header.OmapSampleMask;
OmapDepth = header.OmapDepth; OmapDepth = header.OmapDepth;
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
LastInPipeline = true;
LastInVertexPipeline = header.Stage < ShaderStage.Fragment; LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
} }
@ -306,6 +308,8 @@ namespace Ryujinx.Graphics.Shader.Translation
config._perPatchAttributeLocations = locationsMap; config._perPatchAttributeLocations = locationsMap;
} }
LastInPipeline = false;
// We don't consider geometry shaders using the geometry shader passthrough feature // We don't consider geometry shaders using the geometry shader passthrough feature
// as being the last because when this feature is used, it can't actually modify any of the outputs, // as being the last because when this feature is used, it can't actually modify any of the outputs,
// so the stage that comes before it is the last one that can do modifications. // so the stage that comes before it is the last one that can do modifications.