Delete ShaderConfig and organize shader resources/definitions better (#5509)

* Move some properties out of ShaderConfig

* Stop using ShaderConfig on backends

* Replace ShaderConfig usages on Translator and passes

* Move remaining properties out of ShaderConfig and delete ShaderConfig

* Remove ResourceManager property from TranslatorContext

* Move Rewriter passes to separate transform pass files

* Fix TransformPasses.RunPass on cases where a node is removed

* Move remaining ClipDistancePrimitivesWritten and UsedFeatures updates to decode stage

* Reduce excessive parameter passing a bit by using structs more

* Remove binding parameter from ShaderProperties methods since it is redundant

* Replace decoder instruction checks with switch statement

* Put GLSL on the same plan as SPIR-V for input/output declaration

* Stop mutating TranslatorContext state when Translate is called

* Pass most of the graphics state using a struct instead of individual query methods

* Auto-format

* Auto-format

* Add backend logging interface

* Auto-format

* Remove unnecessary use of interpolated strings

* Remove more modifications of AttributeUsage after decode

* PR feedback

* gl_Layer is not supported on compute
This commit is contained in:
gdkchan 2023-08-13 22:26:42 -03:00 committed by GitHub
parent 8edfb2bc7b
commit b423197619
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 2653 additions and 2407 deletions

View file

@ -1,8 +1,11 @@
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
using Ryujinx.Graphics.Shader.CodeGen;
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
using Ryujinx.Graphics.Shader.CodeGen.Spirv;
using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation.Optimizations;
using Ryujinx.Graphics.Shader.Translation.Transforms;
using System;
using System.Collections.Generic;
using System.Linq;
@ -15,22 +18,47 @@ namespace Ryujinx.Graphics.Shader.Translation
public class TranslatorContext
{
private readonly DecodedProgram _program;
private readonly ShaderConfig _config;
private readonly int _localMemorySize;
public ulong Address { get; }
public int Size { get; }
public int Cb1DataSize => _program.Cb1DataSize;
public ShaderStage Stage => _config.Stage;
public int Size => _config.Size;
public int Cb1DataSize => _config.Cb1DataSize;
public bool LayerOutputWritten => _config.LayerOutputWritten;
internal bool HasLayerInputAttribute { get; private set; }
internal int GpLayerInputAttribute { get; private set; }
public IGpuAccessor GpuAccessor => _config.GpuAccessor;
internal AttributeUsage AttributeUsage => _program.AttributeUsage;
internal TranslatorContext(ulong address, DecodedProgram program, ShaderConfig config)
internal ShaderDefinitions Definitions { get; }
public ShaderStage Stage => Definitions.Stage;
internal IGpuAccessor GpuAccessor { get; }
internal TranslationOptions Options { get; }
internal FeatureFlags UsedFeatures { get; private set; }
public bool LayerOutputWritten { get; private set; }
public int LayerOutputAttribute { get; private set; }
internal TranslatorContext(
ulong address,
int size,
int localMemorySize,
ShaderDefinitions definitions,
IGpuAccessor gpuAccessor,
TranslationOptions options,
DecodedProgram program)
{
Address = address;
Size = size;
_program = program;
_config = config;
_localMemorySize = localMemorySize;
Definitions = definitions;
GpuAccessor = gpuAccessor;
Options = options;
UsedFeatures = program.UsedFeatures;
}
private static bool IsLoadUserDefined(Operation operation)
@ -131,63 +159,259 @@ namespace Ryujinx.Graphics.Shader.Translation
return output;
}
public void SetNextStage(TranslatorContext nextStage)
internal int GetDepthRegister()
{
_config.MergeFromtNextStage(nextStage._config);
// The depth register is always two registers after the last color output.
return BitOperations.PopCount((uint)Definitions.OmapTargets) + 1;
}
public void SetLayerOutputAttribute(int attr)
{
LayerOutputWritten = true;
LayerOutputAttribute = attr;
}
public void SetGeometryShaderLayerInputAttribute(int attr)
{
_config.SetGeometryShaderLayerInputAttribute(attr);
UsedFeatures |= FeatureFlags.RtLayer;
HasLayerInputAttribute = true;
GpLayerInputAttribute = attr;
}
public void SetLastInVertexPipeline()
{
_config.SetLastInVertexPipeline();
Definitions.LastInVertexPipeline = true;
}
public ShaderProgram Translate(TranslatorContext other = null)
public void SetNextStage(TranslatorContext nextStage)
{
bool usesLocalMemory = _config.UsedFeatures.HasFlag(FeatureFlags.LocalMemory);
AttributeUsage.MergeFromtNextStage(
Definitions.GpPassthrough,
nextStage.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr),
nextStage.AttributeUsage);
_config.ResourceManager.SetCurrentLocalMemory(_config.LocalMemorySize, usesLocalMemory);
if (_config.Stage == ShaderStage.Compute)
// 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,
// so the stage that comes before it is the last one that can do modifications.
if (nextStage.Definitions.Stage != ShaderStage.Fragment &&
(nextStage.Definitions.Stage != ShaderStage.Geometry || !nextStage.Definitions.GpPassthrough))
{
bool usesSharedMemory = _config.UsedFeatures.HasFlag(FeatureFlags.SharedMemory);
Definitions.LastInVertexPipeline = false;
}
}
_config.ResourceManager.SetCurrentSharedMemory(GpuAccessor.QueryComputeSharedMemorySize(), usesSharedMemory);
public ShaderProgram Translate()
{
ResourceManager resourceManager = CreateResourceManager();
bool usesLocalMemory = _program.UsedFeatures.HasFlag(FeatureFlags.LocalMemory);
resourceManager.SetCurrentLocalMemory(_localMemorySize, usesLocalMemory);
if (Stage == ShaderStage.Compute)
{
bool usesSharedMemory = _program.UsedFeatures.HasFlag(FeatureFlags.SharedMemory);
resourceManager.SetCurrentSharedMemory(GpuAccessor.QueryComputeSharedMemorySize(), usesSharedMemory);
}
FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _);
FunctionCode[] code = EmitShader(this, resourceManager, _program, initializeOutputs: true, out _);
if (other != null)
return Translate(code, resourceManager, UsedFeatures, _program.ClipDistancesWritten);
}
public ShaderProgram Translate(TranslatorContext other)
{
ResourceManager resourceManager = CreateResourceManager();
bool usesLocalMemory = _program.UsedFeatures.HasFlag(FeatureFlags.LocalMemory);
resourceManager.SetCurrentLocalMemory(_localMemorySize, usesLocalMemory);
FunctionCode[] code = EmitShader(this, resourceManager, _program, initializeOutputs: false, out _);
bool otherUsesLocalMemory = other._program.UsedFeatures.HasFlag(FeatureFlags.LocalMemory);
resourceManager.SetCurrentLocalMemory(other._localMemorySize, otherUsesLocalMemory);
FunctionCode[] otherCode = EmitShader(other, resourceManager, other._program, initializeOutputs: true, out int aStart);
code = Combine(otherCode, code, aStart);
return Translate(
code,
resourceManager,
UsedFeatures | other.UsedFeatures,
(byte)(_program.ClipDistancesWritten | other._program.ClipDistancesWritten));
}
private ShaderProgram Translate(FunctionCode[] functions, ResourceManager resourceManager, FeatureFlags usedFeatures, byte clipDistancesWritten)
{
var cfgs = new ControlFlowGraph[functions.Length];
var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length];
for (int i = 0; i < functions.Length; i++)
{
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
cfgs[i] = ControlFlowGraph.Create(functions[i].Code);
// We need to share the resource manager since both shaders accesses the same constant buffers.
other._config.ResourceManager = _config.ResourceManager;
other._config.ResourceManager.SetCurrentLocalMemory(other._config.LocalMemorySize, other._config.UsedFeatures.HasFlag(FeatureFlags.LocalMemory));
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
code = Combine(otherCode, code, aStart);
_config.InheritFrom(other._config);
if (i != 0)
{
frus[i] = RegisterUsage.RunPass(cfgs[i]);
}
}
return Translator.Translate(code, _config);
List<Function> funcs = new(functions.Length);
for (int i = 0; i < functions.Length; i++)
{
funcs.Add(null);
}
HelperFunctionManager hfm = new(funcs, Definitions.Stage);
for (int i = 0; i < functions.Length; i++)
{
var cfg = cfgs[i];
int inArgumentsCount = 0;
int outArgumentsCount = 0;
if (i != 0)
{
var fru = frus[i];
inArgumentsCount = fru.InArguments.Length;
outArgumentsCount = fru.OutArguments.Length;
}
if (cfg.Blocks.Length != 0)
{
RegisterUsage.FixupCalls(cfg.Blocks, frus);
Dominance.FindDominators(cfg);
Dominance.FindDominanceFrontiers(cfg.Blocks);
Ssa.Rename(cfg.Blocks);
TransformContext context = new(
hfm,
cfg.Blocks,
resourceManager,
GpuAccessor,
Options.TargetLanguage,
Definitions.Stage,
ref usedFeatures);
Optimizer.RunPass(context);
TransformPasses.RunPass(context);
}
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
}
var identification = ShaderIdentifier.Identify(funcs, GpuAccessor, Definitions.Stage, Definitions.InputTopology, out int layerInputAttr);
return Generate(
funcs,
AttributeUsage,
Definitions,
resourceManager,
usedFeatures,
clipDistancesWritten,
identification,
layerInputAttr);
}
private ShaderProgram Generate(
IReadOnlyList<Function> funcs,
AttributeUsage attributeUsage,
ShaderDefinitions definitions,
ResourceManager resourceManager,
FeatureFlags usedFeatures,
byte clipDistancesWritten,
ShaderIdentification identification = ShaderIdentification.None,
int layerInputAttr = 0)
{
var sInfo = StructuredProgram.MakeStructuredProgram(
funcs,
attributeUsage,
definitions,
resourceManager,
Options.Flags.HasFlag(TranslationFlags.DebugMode));
var info = new ShaderProgramInfo(
resourceManager.GetConstantBufferDescriptors(),
resourceManager.GetStorageBufferDescriptors(),
resourceManager.GetTextureDescriptors(),
resourceManager.GetImageDescriptors(),
identification,
layerInputAttr,
definitions.Stage,
usedFeatures.HasFlag(FeatureFlags.FragCoordXY),
usedFeatures.HasFlag(FeatureFlags.InstanceId),
usedFeatures.HasFlag(FeatureFlags.DrawParameters),
usedFeatures.HasFlag(FeatureFlags.RtLayer),
clipDistancesWritten,
definitions.OmapTargets);
var hostCapabilities = new HostCapabilities(
GpuAccessor.QueryHostReducedPrecision(),
GpuAccessor.QueryHostSupportsFragmentShaderInterlock(),
GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(),
GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(),
GpuAccessor.QueryHostSupportsShaderBallot(),
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
GpuAccessor.QueryHostSupportsTextureShadowLod(),
GpuAccessor.QueryHostSupportsViewportMask());
var parameters = new CodeGenParameters(attributeUsage, definitions, resourceManager.Properties, hostCapabilities, GpuAccessor, Options.TargetApi);
return Options.TargetLanguage switch
{
TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, parameters)),
TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, parameters)),
_ => throw new NotImplementedException(Options.TargetLanguage.ToString()),
};
}
private ResourceManager CreateResourceManager()
{
ResourceManager resourceManager = new(Definitions.Stage, GpuAccessor);
if (!GpuAccessor.QueryHostSupportsTransformFeedback() && GpuAccessor.QueryTransformFeedbackEnabled())
{
StructureType tfeInfoStruct = new(new StructureField[]
{
new StructureField(AggregateType.Array | AggregateType.U32, "base_offset", 4),
new StructureField(AggregateType.U32, "vertex_count")
});
BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct);
resourceManager.Properties.AddOrUpdateStorageBuffer(tfeInfoBuffer);
StructureType tfeDataStruct = new(new StructureField[]
{
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0)
});
for (int i = 0; i < Constants.TfeBuffersCount; i++)
{
int binding = Constants.TfeBufferBaseBinding + i;
BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct);
resourceManager.Properties.AddOrUpdateStorageBuffer(tfeDataBuffer);
}
}
return resourceManager;
}
public ShaderProgram GenerateGeometryPassthrough()
{
int outputAttributesMask = _config.UsedOutputAttributes;
int layerOutputAttr = _config.LayerOutputAttribute;
int outputAttributesMask = AttributeUsage.UsedOutputAttributes;
int layerOutputAttr = LayerOutputAttribute;
OutputTopology outputTopology;
int maxOutputVertices;
switch (GpuAccessor.QueryPrimitiveTopology())
switch (Definitions.InputTopology)
{
case InputTopology.Points:
outputTopology = OutputTopology.PointList;
@ -204,9 +428,10 @@ namespace Ryujinx.Graphics.Shader.Translation
break;
}
ShaderConfig config = new(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options);
var attributeUsage = new AttributeUsage(GpuAccessor);
var resourceManager = new ResourceManager(ShaderStage.Geometry, GpuAccessor);
EmitterContext context = new(default, config, false);
var context = new EmitterContext();
for (int v = 0; v < maxOutputVertices; v++)
{
@ -231,10 +456,7 @@ namespace Ryujinx.Graphics.Shader.Translation
else
{
context.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(attrIndex), Const(c), value);
config.SetOutputUserAttribute(attrIndex);
}
config.SetInputUserAttribute(attrIndex, c);
}
}
@ -254,16 +476,15 @@ namespace Ryujinx.Graphics.Shader.Translation
var cfg = ControlFlowGraph.Create(operations);
var function = new Function(cfg.Blocks, "main", false, 0, 0);
var sInfo = StructuredProgram.MakeStructuredProgram(new[] { function }, config);
var definitions = new ShaderDefinitions(
ShaderStage.Geometry,
GpuAccessor.QueryGraphicsState(),
false,
1,
outputTopology,
maxOutputVertices);
var info = config.CreateProgramInfo();
return config.Options.TargetLanguage switch
{
TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)),
TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)),
_ => throw new NotImplementedException(config.Options.TargetLanguage.ToString()),
};
return Generate(new[] { function }, attributeUsage, definitions, resourceManager, FeatureFlags.RtLayer, 0);
}
}
}