Replace constant buffer access on shader with new Load instruction (#4646)
This commit is contained in:
parent
fb27042e01
commit
402f05b8ef
42 changed files with 788 additions and 625 deletions
|
@ -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 = 5027;
|
||||
private const uint CodeGenVersion = 4646;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed;
|
|||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
|
@ -16,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
private readonly ResourceCounts _resourceCounts;
|
||||
private readonly int _stageIndex;
|
||||
|
||||
private readonly int[] _constantBufferBindings;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU accessor.
|
||||
/// </summary>
|
||||
|
@ -25,6 +28,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
_context = context;
|
||||
_resourceCounts = resourceCounts;
|
||||
_stageIndex = stageIndex;
|
||||
|
||||
if (context.Capabilities.Api != TargetApi.Vulkan)
|
||||
{
|
||||
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
|
||||
_constantBufferBindings.AsSpan().Fill(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
|
@ -36,7 +45,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.UniformBuffersCount++;
|
||||
int binding = _constantBufferBindings[index];
|
||||
|
||||
if (binding < 0)
|
||||
{
|
||||
binding = _resourceCounts.UniformBuffersCount++;
|
||||
_constantBufferBindings[index] = binding;
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr;
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
|
@ -102,13 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
context.AppendLine();
|
||||
}
|
||||
|
||||
var cBufferDescriptors = context.Config.GetConstantBufferDescriptors();
|
||||
if (cBufferDescriptors.Length != 0)
|
||||
{
|
||||
DeclareUniforms(context, cBufferDescriptors);
|
||||
|
||||
context.AppendLine();
|
||||
}
|
||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||
|
||||
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
|
||||
if (sBufferDescriptors.Length != 0)
|
||||
|
@ -265,18 +260,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
scaleElements++; // Also includes render target scale, for gl_FragCoord.
|
||||
}
|
||||
|
||||
DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements);
|
||||
|
||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
|
||||
{
|
||||
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
|
||||
context.AppendLine();
|
||||
}
|
||||
}
|
||||
else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
DeclareSupportUniformBlock(context, context.Config.Stage, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
|
||||
|
@ -389,36 +378,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
};
|
||||
}
|
||||
|
||||
private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
||||
{
|
||||
string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]";
|
||||
|
||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
|
||||
foreach (BufferDefinition buffer in buffers)
|
||||
{
|
||||
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
||||
|
||||
ubName += "_" + DefaultNames.UniformNamePrefix;
|
||||
|
||||
string blockName = $"{ubName}_{DefaultNames.BlockSuffix}";
|
||||
|
||||
context.AppendLine($"layout (binding = {context.Config.FirstConstantBufferBinding}, std140) uniform {blockName}");
|
||||
context.EnterScope();
|
||||
context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";");
|
||||
context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var descriptor in descriptors)
|
||||
string layout = buffer.Layout switch
|
||||
{
|
||||
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
||||
BufferLayout.Std140 => "std140",
|
||||
_ => "std430"
|
||||
};
|
||||
|
||||
ubName += "_" + DefaultNames.UniformNamePrefix + descriptor.Slot;
|
||||
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
|
||||
context.EnterScope();
|
||||
|
||||
context.AppendLine($"layout (binding = {descriptor.Binding}, std140) uniform {ubName}");
|
||||
context.EnterScope();
|
||||
context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";");
|
||||
context.LeaveScope(";");
|
||||
foreach (StructureField field in buffer.Type.Fields)
|
||||
{
|
||||
if (field.Type.HasFlag(AggregateType.Array))
|
||||
{
|
||||
string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
|
||||
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
|
||||
}
|
||||
else
|
||||
{
|
||||
string typeName = GetVarTypeName(context, field.Type);
|
||||
|
||||
context.AppendLine($"{typeName} {field.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
context.LeaveScope($" {buffer.Name};");
|
||||
context.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,39 +750,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
context.AppendLine($"layout (location = {location}) patch out vec4 {name};");
|
||||
}
|
||||
|
||||
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
|
||||
{
|
||||
bool needsSupportBlock = stage == ShaderStage.Fragment ||
|
||||
(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
|
||||
|
||||
if (!needsSupportBlock && scaleElements == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
|
||||
context.EnterScope();
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Fragment:
|
||||
case ShaderStage.Vertex:
|
||||
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
|
||||
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
|
||||
context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};");
|
||||
context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
|
||||
break;
|
||||
case ShaderStage.Compute:
|
||||
context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];");
|
||||
break;
|
||||
}
|
||||
|
||||
context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];");
|
||||
|
||||
context.LeaveScope(";");
|
||||
context.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendHelperFunction(CodeGenContext context, string filename)
|
||||
{
|
||||
string code = EmbeddedResources.ReadAllText(filename);
|
||||
|
|
|
@ -15,18 +15,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
public const string DataName = "data";
|
||||
|
||||
public const string SupportBlockName = "support_block";
|
||||
public const string SupportBlockAlphaTestName = "s_alpha_test";
|
||||
public const string SupportBlockIsBgraName = "s_is_bgra";
|
||||
public const string SupportBlockViewportInverse = "s_viewport_inverse";
|
||||
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
|
||||
public const string SupportBlockRenderScaleName = "s_render_scale";
|
||||
|
||||
public const string BlockSuffix = "block";
|
||||
|
||||
public const string UniformNamePrefix = "c";
|
||||
public const string UniformNameSuffix = "data";
|
||||
|
||||
public const string LocalMemoryName = "local_mem";
|
||||
public const string SharedMemoryName = "shared_mem";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
||||
{
|
||||
float scale = s_render_scale[samplerIndex];
|
||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return inputVec;
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
||||
{
|
||||
float scale = s_render_scale[samplerIndex];
|
||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return size;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
||||
{
|
||||
float scale = s_render_scale[1 + samplerIndex];
|
||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return inputVec;
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
||||
{
|
||||
float scale = abs(s_render_scale[1 + samplerIndex]);
|
||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]);
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return size;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
||||
{
|
||||
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
|
||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return inputVec;
|
||||
|
@ -11,7 +11,7 @@
|
|||
|
||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
||||
{
|
||||
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
|
||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return size;
|
||||
|
|
|
@ -167,9 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
case Instruction.Load:
|
||||
return Load(context, operation);
|
||||
|
||||
case Instruction.LoadConstant:
|
||||
return LoadConstant(context, operation);
|
||||
|
||||
case Instruction.LoadLocal:
|
||||
return LoadLocal(context, operation);
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
Add(Instruction.ImageAtomic, InstType.Special);
|
||||
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
|
||||
Add(Instruction.Load, InstType.Special);
|
||||
Add(Instruction.LoadConstant, InstType.Special);
|
||||
Add(Instruction.LoadLocal, InstType.Special);
|
||||
Add(Instruction.LoadShared, InstType.Special);
|
||||
Add(Instruction.LoadStorage, InstType.Special);
|
||||
|
|
|
@ -215,29 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
return GenerateLoadOrStore(context, operation, isStore: false);
|
||||
}
|
||||
|
||||
public static string LoadConstant(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(0);
|
||||
IAstNode src2 = operation.GetSource(1);
|
||||
|
||||
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||
offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true);
|
||||
|
||||
var config = context.Config;
|
||||
bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
||||
|
||||
if (src1 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||
{
|
||||
bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
|
||||
return OperandManager.GetConstantBufferName(operand.Value, offsetExpr, config.Stage, cbIndexable, indexElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
|
||||
return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, config.Stage, indexElement);
|
||||
}
|
||||
}
|
||||
|
||||
public static string LoadLocal(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
|
||||
|
@ -809,9 +786,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
string varName;
|
||||
AggregateType varType;
|
||||
int srcIndex = 0;
|
||||
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
|
||||
|
||||
switch (storageKind)
|
||||
{
|
||||
case StorageKind.ConstantBuffer:
|
||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
int binding = bindingIndex.Value;
|
||||
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[binding];
|
||||
|
||||
if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||
varName = $"{buffer.Name}.{field.Name}";
|
||||
varType = field.Type;
|
||||
break;
|
||||
|
||||
case StorageKind.Input:
|
||||
case StorageKind.InputPerPatch:
|
||||
case StorageKind.Output:
|
||||
|
@ -864,40 +861,39 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
varName = $"gl_out[{expr}].{varName}";
|
||||
}
|
||||
}
|
||||
|
||||
int firstSrcIndex = srcIndex;
|
||||
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
|
||||
|
||||
for (; srcIndex < inputsCount; srcIndex++)
|
||||
{
|
||||
IAstNode src = operation.GetSource(srcIndex);
|
||||
|
||||
if ((varType & AggregateType.ElementCountMask) != 0 &&
|
||||
srcIndex == inputsCount - 1 &&
|
||||
src is AstOperand elementIndex &&
|
||||
elementIndex.Type == OperandType.Constant)
|
||||
{
|
||||
varName += "." + "xyzw"[elementIndex.Value & 3];
|
||||
}
|
||||
else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output)
|
||||
{
|
||||
// GLSL requires that for tessellation control shader outputs,
|
||||
// that the index expression must be *exactly* "gl_InvocationID",
|
||||
// otherwise the compilation fails.
|
||||
// TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR.
|
||||
varName += "[gl_InvocationID]";
|
||||
}
|
||||
else
|
||||
{
|
||||
varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
||||
}
|
||||
|
||||
int firstSrcIndex = srcIndex;
|
||||
|
||||
for (; srcIndex < inputsCount; srcIndex++)
|
||||
{
|
||||
IAstNode src = operation.GetSource(srcIndex);
|
||||
|
||||
if ((varType & AggregateType.ElementCountMask) != 0 &&
|
||||
srcIndex == inputsCount - 1 &&
|
||||
src is AstOperand elementIndex &&
|
||||
elementIndex.Type == OperandType.Constant)
|
||||
{
|
||||
varName += "." + "xyzw"[elementIndex.Value & 3];
|
||||
}
|
||||
else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output)
|
||||
{
|
||||
// GLSL requires that for tessellation control shader outputs,
|
||||
// that the index expression must be *exactly* "gl_InvocationID",
|
||||
// otherwise the compilation fails.
|
||||
// TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR.
|
||||
varName += "[gl_InvocationID]";
|
||||
}
|
||||
else
|
||||
{
|
||||
varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]";
|
||||
}
|
||||
}
|
||||
|
||||
if (isStore)
|
||||
{
|
||||
varType &= AggregateType.ElementTypeMask;
|
||||
|
|
|
@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32),
|
||||
IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location),
|
||||
IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32),
|
||||
IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool),
|
||||
IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||
IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||
IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool),
|
||||
|
@ -46,8 +45,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config),
|
||||
IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"),
|
||||
IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"),
|
||||
IoVariable.SupportBlockRenderScale => (DefaultNames.SupportBlockRenderScaleName, AggregateType.Array | AggregateType.FP32),
|
||||
IoVariable.SupportBlockViewInverse => (DefaultNames.SupportBlockViewportInverse, AggregateType.Vector2 | AggregateType.FP32),
|
||||
IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32),
|
||||
IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32),
|
||||
IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32),
|
||||
|
|
|
@ -36,63 +36,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
OperandType.Argument => GetArgumentName(operand.Value),
|
||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
|
||||
OperandType.LocalVariable => _locals[operand],
|
||||
OperandType.Undefined => DefaultNames.UndefinedName,
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetConstantBufferName(AstOperand operand, ShaderConfig config)
|
||||
{
|
||||
return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing));
|
||||
}
|
||||
|
||||
public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable)
|
||||
{
|
||||
return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}";
|
||||
}
|
||||
|
||||
private static string GetVec4Indexed(string vectorName, string indexExpr, bool indexElement)
|
||||
{
|
||||
if (indexElement)
|
||||
{
|
||||
return $"{vectorName}[{indexExpr}]";
|
||||
}
|
||||
|
||||
string result = $"{vectorName}.x";
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
result = $"(({indexExpr}) == {i}) ? ({vectorName}.{GetSwizzleMask(i)}) : ({result})";
|
||||
}
|
||||
return $"({result})";
|
||||
}
|
||||
|
||||
public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable, bool indexElement)
|
||||
{
|
||||
return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
||||
}
|
||||
|
||||
public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage, bool indexElement)
|
||||
{
|
||||
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
||||
}
|
||||
|
||||
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
||||
{
|
||||
if (cbIndexable)
|
||||
{
|
||||
return GetUbName(stage, NumberFormatter.FormatInt(slot, AggregateType.S32));
|
||||
}
|
||||
|
||||
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}";
|
||||
}
|
||||
|
||||
private static string GetUbName(ShaderStage stage, string slotExpr)
|
||||
{
|
||||
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}[{slotExpr}].{DefaultNames.DataName}";
|
||||
}
|
||||
|
||||
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
|
||||
{
|
||||
return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
|
||||
|
@ -168,6 +117,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
switch (operation.StorageKind)
|
||||
{
|
||||
case StorageKind.ConstantBuffer:
|
||||
if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
if (!(operation.GetSource(1) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
|
||||
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||
|
||||
return field.Type & AggregateType.ElementTypeMask;
|
||||
|
||||
case StorageKind.Input:
|
||||
case StorageKind.InputPerPatch:
|
||||
case StorageKind.Output:
|
||||
|
|
|
@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
public int InputVertices { get; }
|
||||
|
||||
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
|
||||
public Instruction SupportBuffer { get; set; }
|
||||
public Instruction UniformBuffersArray { get; set; }
|
||||
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
|
||||
public Instruction StorageBuffersArray { get; set; }
|
||||
public Instruction LocalMemory { get; set; }
|
||||
public Instruction SharedMemory { get; set; }
|
||||
|
@ -217,7 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
IrOperandType.Argument => GetArgument(type, operand),
|
||||
IrOperandType.Constant => GetConstant(type, operand),
|
||||
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
|
||||
IrOperandType.LocalVariable => GetLocal(type, operand),
|
||||
IrOperandType.Undefined => GetUndefined(type),
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||
|
@ -274,31 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
};
|
||||
}
|
||||
|
||||
public Instruction GetConstantBuffer(AggregateType type, AstOperand operand)
|
||||
{
|
||||
var i1 = Constant(TypeS32(), 0);
|
||||
var i2 = Constant(TypeS32(), operand.CbufOffset >> 2);
|
||||
var i3 = Constant(TypeU32(), operand.CbufOffset & 3);
|
||||
|
||||
Instruction elemPointer;
|
||||
|
||||
if (UniformBuffersArray != null)
|
||||
{
|
||||
var ubVariable = UniformBuffersArray;
|
||||
var i0 = Constant(TypeS32(), operand.CbufSlot);
|
||||
|
||||
elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i0, i1, i2, i3);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ubVariable = UniformBuffers[operand.CbufSlot];
|
||||
|
||||
elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i1, i2, i3);
|
||||
}
|
||||
|
||||
return BitcastIfNeeded(type, AggregateType.FP32, Load(TypeFP32(), elemPointer));
|
||||
}
|
||||
|
||||
public Instruction GetLocalPointer(AstOperand local)
|
||||
{
|
||||
return _locals[local];
|
||||
|
|
|
@ -98,8 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
DeclareLocalMemory(context, localMemorySize);
|
||||
}
|
||||
|
||||
DeclareSupportBuffer(context);
|
||||
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
|
||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||
|
@ -127,84 +126,63 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
return variable;
|
||||
}
|
||||
|
||||
private static void DeclareSupportBuffer(CodeGenContext context)
|
||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
||||
{
|
||||
if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
|
||||
HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
|
||||
|
||||
foreach (BufferDefinition buffer in buffers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4;
|
||||
int alignmentMask = alignment - 1;
|
||||
int offset = 0;
|
||||
|
||||
var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
|
||||
var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
|
||||
var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
|
||||
SpvInstruction[] structFieldTypes = new SpvInstruction[buffer.Type.Fields.Length];
|
||||
int[] structFieldOffsets = new int[buffer.Type.Fields.Length];
|
||||
|
||||
context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
|
||||
context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
|
||||
for (int fieldIndex = 0; fieldIndex < buffer.Type.Fields.Length; fieldIndex++)
|
||||
{
|
||||
StructureField field = buffer.Type.Fields[fieldIndex];
|
||||
int fieldSize = (field.Type.GetSizeInBytes() + alignmentMask) & ~alignmentMask;
|
||||
|
||||
var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
|
||||
structFieldTypes[fieldIndex] = context.GetType(field.Type, field.ArrayLength);
|
||||
structFieldOffsets[fieldIndex] = offset;
|
||||
|
||||
context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
|
||||
context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
|
||||
context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
|
||||
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
|
||||
context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
|
||||
context.Decorate(supportBufferStructType, Decoration.Block);
|
||||
if (field.Type.HasFlag(AggregateType.Array))
|
||||
{
|
||||
// We can't decorate the type more than once.
|
||||
if (decoratedTypes.Add(structFieldTypes[fieldIndex]))
|
||||
{
|
||||
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
|
||||
}
|
||||
|
||||
var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
|
||||
var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
|
||||
offset += fieldSize * field.ArrayLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += fieldSize;
|
||||
}
|
||||
}
|
||||
|
||||
context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
||||
context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
|
||||
var ubStructType = context.TypeStruct(false, structFieldTypes);
|
||||
|
||||
context.AddGlobalVariable(supportBufferVariable);
|
||||
if (decoratedTypes.Add(ubStructType))
|
||||
{
|
||||
context.Decorate(ubStructType, Decoration.Block);
|
||||
|
||||
context.SupportBuffer = supportBufferVariable;
|
||||
}
|
||||
for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++)
|
||||
{
|
||||
context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
{
|
||||
if (descriptors.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint ubSize = Constants.ConstantBufferSize / 16;
|
||||
|
||||
var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
|
||||
context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
|
||||
var ubStructType = context.TypeStruct(true, ubArrayType);
|
||||
context.Decorate(ubStructType, Decoration.Block);
|
||||
context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
||||
|
||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
|
||||
{
|
||||
int count = descriptors.Max(x => x.Slot) + 1;
|
||||
|
||||
var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
|
||||
context.Name(ubVariable, buffer.Name);
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
||||
context.AddGlobalVariable(ubVariable);
|
||||
|
||||
context.UniformBuffersArray = ubVariable;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
context.AddGlobalVariable(ubVariable);
|
||||
context.UniformBuffers.Add(descriptor.Slot, ubVariable);
|
||||
}
|
||||
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,25 +372,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
foreach (var ioDefinition in info.IoDefinitions)
|
||||
{
|
||||
var ioVariable = ioDefinition.IoVariable;
|
||||
|
||||
// Those are actually from constant buffer, rather than being actual inputs or outputs,
|
||||
// so we must ignore them here as they are declared as part of the support buffer.
|
||||
// TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input").
|
||||
if (ioVariable == IoVariable.FragmentOutputIsBgra ||
|
||||
ioVariable == IoVariable.SupportBlockRenderScale ||
|
||||
ioVariable == IoVariable.SupportBlockViewInverse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isOutput = ioDefinition.StorageKind.IsOutput();
|
||||
bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
|
||||
|
||||
PixelImap iq = PixelImap.Unused;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
var ioVariable = ioDefinition.IoVariable;
|
||||
if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
|
||||
|
@ -429,6 +393,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
}
|
||||
}
|
||||
|
||||
bool isOutput = ioDefinition.StorageKind.IsOutput();
|
||||
bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
|
||||
|
||||
DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
Add(Instruction.ImageStore, GenerateImageStore);
|
||||
Add(Instruction.IsNan, GenerateIsNan);
|
||||
Add(Instruction.Load, GenerateLoad);
|
||||
Add(Instruction.LoadConstant, GenerateLoadConstant);
|
||||
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
||||
Add(Instruction.LoadShared, GenerateLoadShared);
|
||||
Add(Instruction.LoadStorage, GenerateLoadStorage);
|
||||
|
@ -313,10 +312,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
var operand = (AstOperand)operation.GetSource(i + 1);
|
||||
var operand = operation.GetSource(i + 1);
|
||||
|
||||
if (i >= function.InArguments.Length)
|
||||
{
|
||||
args[i] = context.GetLocalPointer(operand);
|
||||
args[i] = context.GetLocalPointer((AstOperand)operand);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -867,68 +867,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
return GenerateLoadOrStore(context, operation, isStore: false);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
var src1 = operation.GetSource(0);
|
||||
var src2 = context.Get(AggregateType.S32, operation.GetSource(1));
|
||||
|
||||
var i1 = context.Constant(context.TypeS32(), 0);
|
||||
var i2 = context.ShiftRightArithmetic(context.TypeS32(), src2, context.Constant(context.TypeS32(), 2));
|
||||
var i3 = context.BitwiseAnd(context.TypeS32(), src2, context.Constant(context.TypeS32(), 3));
|
||||
|
||||
SpvInstruction value = null;
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostHasVectorIndexingBug())
|
||||
{
|
||||
// Test for each component individually.
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var component = context.Constant(context.TypeS32(), i);
|
||||
|
||||
SpvInstruction elemPointer;
|
||||
if (context.UniformBuffersArray != null)
|
||||
{
|
||||
var ubVariable = context.UniformBuffersArray;
|
||||
var i0 = context.Get(AggregateType.S32, src1);
|
||||
|
||||
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i0, i1, i2, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ubVariable = context.UniformBuffers[((AstOperand)src1).Value];
|
||||
|
||||
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i1, i2, component);
|
||||
}
|
||||
|
||||
SpvInstruction newValue = context.Load(context.TypeFP32(), elemPointer);
|
||||
|
||||
value = value != null ? context.Select(context.TypeFP32(), context.IEqual(context.TypeBool(), i3, component), newValue, value) : newValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpvInstruction elemPointer;
|
||||
|
||||
if (context.UniformBuffersArray != null)
|
||||
{
|
||||
var ubVariable = context.UniformBuffersArray;
|
||||
var i0 = context.Get(AggregateType.S32, src1);
|
||||
|
||||
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i0, i1, i2, i3);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ubVariable = context.UniformBuffers[((AstOperand)src1).Value];
|
||||
|
||||
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i1, i2, i3);
|
||||
}
|
||||
|
||||
value = context.Load(context.TypeFP32(), elemPointer);
|
||||
}
|
||||
|
||||
return new OperationResult(AggregateType.FP32, value);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
|
||||
|
@ -1990,12 +1928,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
StorageKind storageKind = operation.StorageKind;
|
||||
|
||||
SpvInstruction pointer;
|
||||
StorageClass storageClass;
|
||||
SpvInstruction baseObj;
|
||||
AggregateType varType;
|
||||
int srcIndex = 0;
|
||||
|
||||
switch (storageKind)
|
||||
{
|
||||
case StorageKind.ConstantBuffer:
|
||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
if (!(operation.GetSource(srcIndex) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
|
||||
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||
|
||||
storageClass = StorageClass.Uniform;
|
||||
varType = field.Type & AggregateType.ElementTypeMask;
|
||||
baseObj = context.ConstantBuffers[bindingIndex.Value];
|
||||
break;
|
||||
|
||||
case StorageKind.Input:
|
||||
case StorageKind.InputPerPatch:
|
||||
case StorageKind.Output:
|
||||
|
@ -2038,33 +1996,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
varType = context.Config.GetFragmentOutputColorType(location);
|
||||
}
|
||||
else if (ioVariable == IoVariable.FragmentOutputIsBgra)
|
||||
{
|
||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeU32());
|
||||
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 1), elemIndex);
|
||||
varType = AggregateType.U32;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (ioVariable == IoVariable.SupportBlockRenderScale)
|
||||
{
|
||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
||||
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 4), elemIndex);
|
||||
varType = AggregateType.FP32;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (ioVariable == IoVariable.SupportBlockViewInverse)
|
||||
{
|
||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
||||
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 2), elemIndex);
|
||||
varType = AggregateType.FP32;
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||
|
@ -2072,55 +2003,57 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
varType &= AggregateType.ElementTypeMask;
|
||||
|
||||
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
|
||||
var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
||||
storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
||||
|
||||
var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component);
|
||||
var dict = isPerPatch
|
||||
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||
: (isOutput ? context.Outputs : context.Inputs);
|
||||
|
||||
SpvInstruction baseObj = dict[ioDefinition];
|
||||
SpvInstruction e0, e1, e2;
|
||||
|
||||
switch (inputsCount)
|
||||
{
|
||||
case 0:
|
||||
pointer = baseObj;
|
||||
break;
|
||||
case 1:
|
||||
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0);
|
||||
break;
|
||||
case 2:
|
||||
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1);
|
||||
break;
|
||||
case 3:
|
||||
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2);
|
||||
break;
|
||||
default:
|
||||
var indexes = new SpvInstruction[inputsCount];
|
||||
int index = 0;
|
||||
|
||||
for (; index < inputsCount; srcIndex++, index++)
|
||||
{
|
||||
indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex));
|
||||
}
|
||||
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes);
|
||||
break;
|
||||
}
|
||||
baseObj = dict[ioDefinition];
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
||||
}
|
||||
|
||||
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
|
||||
SpvInstruction e0, e1, e2;
|
||||
SpvInstruction pointer;
|
||||
|
||||
switch (inputsCount)
|
||||
{
|
||||
case 0:
|
||||
pointer = baseObj;
|
||||
break;
|
||||
case 1:
|
||||
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0);
|
||||
break;
|
||||
case 2:
|
||||
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1);
|
||||
break;
|
||||
case 3:
|
||||
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2);
|
||||
break;
|
||||
default:
|
||||
var indexes = new SpvInstruction[inputsCount];
|
||||
int index = 0;
|
||||
|
||||
for (; index < inputsCount; srcIndex++, index++)
|
||||
{
|
||||
indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex));
|
||||
}
|
||||
|
||||
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isStore)
|
||||
{
|
||||
context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex)));
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
|
||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
|
||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
||||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
||||
|
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
||||
|
||||
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
|
||||
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
|
||||
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
|
||||
|
||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
||||
|
@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
|
||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
|
||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
||||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
||||
|
@ -209,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
||||
|
||||
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
|
||||
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
|
||||
var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
|
||||
|
||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
||||
|
|
|
@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
{
|
||||
// FragCoord X/Y must be divided by the render target scale, if resolution scaling is active,
|
||||
// because the shader code is not expecting scaled values.
|
||||
res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0)));
|
||||
res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0)));
|
||||
}
|
||||
else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System.Numerics;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
@ -80,7 +81,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
Operand addr = context.IAdd(srcA, Const(Imm16ToSInt(op.CbufOffset)));
|
||||
Operand wordOffset = context.ShiftRightU32(addr, Const(2));
|
||||
Operand bitOffset = GetBitOffset(context, addr);
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
|
@ -92,11 +92,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
}
|
||||
|
||||
Operand offset = context.IAdd(wordOffset, Const(index));
|
||||
Operand value = context.LoadConstant(slot, offset);
|
||||
Operand value = EmitLoadConstant(context, slot, offset);
|
||||
|
||||
if (isSmallInt)
|
||||
{
|
||||
value = ExtractSmallInt(context, (LsSize)op.LsSize, bitOffset, value);
|
||||
value = ExtractSmallInt(context, (LsSize)op.LsSize, GetBitOffset(context, addr), value);
|
||||
}
|
||||
|
||||
context.Copy(Register(dest), value);
|
||||
|
@ -154,6 +154,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
EmitStore(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
|
||||
}
|
||||
|
||||
private static Operand EmitLoadConstant(EmitterContext context, Operand slot, Operand offset)
|
||||
{
|
||||
Operand vecIndex = context.ShiftRightU32(offset, Const(2));
|
||||
Operand elemIndex = context.BitwiseAnd(offset, Const(3));
|
||||
|
||||
if (slot.Type == OperandType.Constant)
|
||||
{
|
||||
int binding = context.Config.ResourceManager.GetConstantBufferBinding(slot.Value);
|
||||
return context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand value = Const(0);
|
||||
|
||||
uint cbUseMask = context.Config.GpuAccessor.QueryConstantBufferUse();
|
||||
|
||||
while (cbUseMask != 0)
|
||||
{
|
||||
int cbIndex = BitOperations.TrailingZeroCount(cbUseMask);
|
||||
int binding = context.Config.ResourceManager.GetConstantBufferBinding(cbIndex);
|
||||
|
||||
Operand isCurrent = context.ICompareEqual(slot, Const(cbIndex));
|
||||
Operand currentValue = context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex);
|
||||
|
||||
value = context.ConditionalSelect(isCurrent, currentValue, value);
|
||||
|
||||
cbUseMask &= ~(1u << cbIndex);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitAtomicOp(
|
||||
EmitterContext context,
|
||||
StorageKind storageKind,
|
||||
|
|
|
@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
ImageAtomic,
|
||||
IsNan,
|
||||
Load,
|
||||
LoadConstant,
|
||||
LoadGlobal,
|
||||
LoadLocal,
|
||||
LoadShared,
|
||||
|
|
|
@ -15,7 +15,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
FragmentCoord,
|
||||
FragmentOutputColor,
|
||||
FragmentOutputDepth,
|
||||
FragmentOutputIsBgra, // TODO: Remove and use constant buffer access.
|
||||
FrontColorDiffuse,
|
||||
FrontColorSpecular,
|
||||
FrontFacing,
|
||||
|
@ -34,8 +33,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
SubgroupLaneId,
|
||||
SubgroupLeMask,
|
||||
SubgroupLtMask,
|
||||
SupportBlockViewInverse, // TODO: Remove and use constant buffer access.
|
||||
SupportBlockRenderScale, // TODO: Remove and use constant buffer access.
|
||||
TessellationCoord,
|
||||
TessellationLevelInner,
|
||||
TessellationLevelOuter,
|
||||
|
|
|
@ -15,9 +15,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
public int Value { get; }
|
||||
|
||||
public int CbufSlot { get; }
|
||||
public int CbufOffset { get; }
|
||||
|
||||
private AstOperand()
|
||||
{
|
||||
Defs = new HashSet<IAstNode>();
|
||||
|
@ -29,16 +26,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
public AstOperand(Operand operand) : this()
|
||||
{
|
||||
Type = operand.Type;
|
||||
|
||||
if (Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
CbufSlot = operand.GetCbufSlot();
|
||||
CbufOffset = operand.GetCbufOffset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = operand.Value;
|
||||
}
|
||||
Value = operand.Value;
|
||||
}
|
||||
|
||||
public AstOperand(OperandType type, int value = 0) : this()
|
||||
|
|
20
src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs
Normal file
20
src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
readonly struct BufferDefinition
|
||||
{
|
||||
public BufferLayout Layout { get; }
|
||||
public int Set { get; }
|
||||
public int Binding { get; }
|
||||
public string Name { get; }
|
||||
public StructureType Type { get; }
|
||||
|
||||
public BufferDefinition(BufferLayout layout, int set, int binding, string name, StructureType type)
|
||||
{
|
||||
Layout = layout;
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
Name = name;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
8
src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs
Normal file
8
src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
enum BufferLayout
|
||||
{
|
||||
Std140,
|
||||
Std430
|
||||
}
|
||||
}
|
|
@ -90,7 +90,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
Add(Instruction.ImageAtomic, AggregateType.S32);
|
||||
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
||||
Add(Instruction.Load, AggregateType.FP32);
|
||||
Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
||||
Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32);
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
OperandType.Argument => AggregateType.S32,
|
||||
OperandType.Constant => AggregateType.S32,
|
||||
OperandType.ConstantBuffer => AggregateType.FP32,
|
||||
OperandType.Undefined => AggregateType.S32,
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{type}\".")
|
||||
};
|
||||
|
|
21
src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
Normal file
21
src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
class ShaderProperties
|
||||
{
|
||||
private readonly Dictionary<int, BufferDefinition> _constantBuffers;
|
||||
|
||||
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
|
||||
|
||||
public ShaderProperties()
|
||||
{
|
||||
_constantBuffers = new Dictionary<int, BufferDefinition>();
|
||||
}
|
||||
|
||||
public void AddConstantBuffer(int binding, BufferDefinition definition)
|
||||
{
|
||||
_constantBuffers[binding] = definition;
|
||||
}
|
||||
}
|
||||
}
|
28
src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs
Normal file
28
src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
struct StructureField
|
||||
{
|
||||
public AggregateType Type { get; }
|
||||
public string Name { get; }
|
||||
public int ArrayLength { get; }
|
||||
|
||||
public StructureField(AggregateType type, string name, int arrayLength = 1)
|
||||
{
|
||||
Type = type;
|
||||
Name = name;
|
||||
ArrayLength = arrayLength;
|
||||
}
|
||||
}
|
||||
|
||||
class StructureType
|
||||
{
|
||||
public StructureField[] Fields { get; }
|
||||
|
||||
public StructureType(StructureField[] fields)
|
||||
{
|
||||
Fields = fields;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -73,27 +73,34 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
Instruction inst = operation.Inst;
|
||||
StorageKind storageKind = operation.StorageKind;
|
||||
|
||||
if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput())
|
||||
if (inst == Instruction.Load || inst == Instruction.Store)
|
||||
{
|
||||
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
|
||||
bool isOutput = storageKind.IsOutput();
|
||||
bool perPatch = storageKind.IsPerPatch();
|
||||
int location = 0;
|
||||
int component = 0;
|
||||
|
||||
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||
if (storageKind.IsInputOrOutput())
|
||||
{
|
||||
location = operation.GetSource(1).Value;
|
||||
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
|
||||
bool isOutput = storageKind.IsOutput();
|
||||
bool perPatch = storageKind.IsPerPatch();
|
||||
int location = 0;
|
||||
int component = 0;
|
||||
|
||||
if (operation.SourcesCount > 2 &&
|
||||
operation.GetSource(2).Type == OperandType.Constant &&
|
||||
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
|
||||
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||
{
|
||||
component = operation.GetSource(2).Value;
|
||||
}
|
||||
}
|
||||
location = operation.GetSource(1).Value;
|
||||
|
||||
context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
|
||||
if (operation.SourcesCount > 2 &&
|
||||
operation.GetSource(2).Type == OperandType.Constant &&
|
||||
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
|
||||
{
|
||||
component = operation.GetSource(2).Value;
|
||||
}
|
||||
}
|
||||
|
||||
context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
|
||||
}
|
||||
else if (storageKind == StorageKind.ConstantBuffer && operation.GetSource(0).Type == OperandType.Constant)
|
||||
{
|
||||
context.Config.ResourceManager.SetUsedConstantBufferBinding(operation.GetSource(0).Value);
|
||||
}
|
||||
}
|
||||
|
||||
bool vectorDest = IsVectorDestInst(inst);
|
||||
|
@ -105,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
sources[index] = context.GetOperand(operation.GetSource(index));
|
||||
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||
}
|
||||
|
||||
for (int index = 0; index < outDestsCount; index++)
|
||||
|
|
|
@ -298,6 +298,33 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
return newTemp;
|
||||
}
|
||||
|
||||
public IAstNode GetOperandOrCbLoad(Operand operand)
|
||||
{
|
||||
if (operand.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
int cbufSlot = operand.GetCbufSlot();
|
||||
int cbufOffset = operand.GetCbufOffset();
|
||||
|
||||
int binding = Config.ResourceManager.GetConstantBufferBinding(cbufSlot);
|
||||
int vecIndex = cbufOffset >> 2;
|
||||
int elemIndex = cbufOffset & 3;
|
||||
|
||||
Config.ResourceManager.SetUsedConstantBufferBinding(binding);
|
||||
|
||||
IAstNode[] sources = new IAstNode[]
|
||||
{
|
||||
new AstOperand(OperandType.Constant, binding),
|
||||
new AstOperand(OperandType.Constant, 0),
|
||||
new AstOperand(OperandType.Constant, vecIndex),
|
||||
new AstOperand(OperandType.Constant, elemIndex)
|
||||
};
|
||||
|
||||
return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, sources, sources.Length);
|
||||
}
|
||||
|
||||
return GetOperand(operand);
|
||||
}
|
||||
|
||||
public AstOperand GetOperand(Operand operand)
|
||||
{
|
||||
if (operand == null)
|
||||
|
@ -307,11 +334,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
if (operand.Type != OperandType.LocalVariable)
|
||||
{
|
||||
if (operand.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
Config.SetUsedConstantBuffer(operand.GetCbufSlot());
|
||||
}
|
||||
|
||||
return new AstOperand(operand);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
|
@ -11,8 +13,20 @@ namespace Ryujinx.Graphics.Shader
|
|||
public T W;
|
||||
}
|
||||
|
||||
enum SupportBufferField
|
||||
{
|
||||
// Must match the order of the fields on the struct.
|
||||
FragmentAlphaTest,
|
||||
FragmentIsBgra,
|
||||
ViewportInverse,
|
||||
FragmentRenderScaleCount,
|
||||
RenderScale
|
||||
}
|
||||
|
||||
public struct SupportBuffer
|
||||
{
|
||||
internal const int Binding = 0;
|
||||
|
||||
public static int FieldSize;
|
||||
public static int RequiredSize;
|
||||
|
||||
|
@ -47,6 +61,18 @@ namespace Ryujinx.Graphics.Shader
|
|||
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
|
||||
}
|
||||
|
||||
internal static StructureType GetStructureType()
|
||||
{
|
||||
return new StructureType(new[]
|
||||
{
|
||||
new StructureField(AggregateType.U32, "s_alpha_test"),
|
||||
new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount),
|
||||
new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"),
|
||||
new StructureField(AggregateType.S32, "s_frag_scale_count"),
|
||||
new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount)
|
||||
});
|
||||
}
|
||||
|
||||
public Vector4<int> FragmentAlphaTest;
|
||||
public Array8<Vector4<int>> FragmentIsBgra;
|
||||
public Vector4<float> ViewportInverse;
|
||||
|
|
|
@ -22,4 +22,35 @@
|
|||
|
||||
Array = 1 << 10
|
||||
}
|
||||
|
||||
static class AggregateTypeExtensions
|
||||
{
|
||||
public static int GetSizeInBytes(this AggregateType type)
|
||||
{
|
||||
int elementSize = (type & AggregateType.ElementTypeMask) switch
|
||||
{
|
||||
AggregateType.Bool or
|
||||
AggregateType.FP32 or
|
||||
AggregateType.S32 or
|
||||
AggregateType.U32 => 4,
|
||||
AggregateType.FP64 => 8,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
switch (type & AggregateType.ElementCountMask)
|
||||
{
|
||||
case AggregateType.Vector2:
|
||||
elementSize *= 2;
|
||||
break;
|
||||
case AggregateType.Vector3:
|
||||
elementSize *= 3;
|
||||
break;
|
||||
case AggregateType.Vector4:
|
||||
elementSize *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
return elementSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,8 +234,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
|
||||
Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
|
||||
Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0));
|
||||
Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1));
|
||||
Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0));
|
||||
Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1));
|
||||
Operand negativeOne = ConstF(-1.0f);
|
||||
|
||||
this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
|
||||
|
@ -420,7 +420,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
// Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
|
||||
if (!supportsBgra && (component == 0 || component == 2))
|
||||
{
|
||||
Operand isBgra = this.Load(StorageKind.Input, IoVariable.FragmentOutputIsBgra, null, Const(rtIndex));
|
||||
Operand isBgra = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.FragmentIsBgra), Const(rtIndex));
|
||||
|
||||
Operand lblIsBgra = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
|
|
@ -549,11 +549,31 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return context.Add(fpType | Instruction.IsNan, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding)
|
||||
{
|
||||
return context.Add(Instruction.Load, storageKind, Local(), Const(binding));
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0)
|
||||
{
|
||||
return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0);
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1)
|
||||
{
|
||||
return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0, e1);
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand e2)
|
||||
{
|
||||
return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0, e1, e2);
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null)
|
||||
{
|
||||
return primVertex != null
|
||||
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex)
|
||||
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable));
|
||||
? context.Load(storageKind, (int)ioVariable, primVertex)
|
||||
: context.Load(storageKind, (int)ioVariable);
|
||||
}
|
||||
|
||||
public static Operand Load(
|
||||
|
@ -564,8 +584,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Operand elemIndex)
|
||||
{
|
||||
return primVertex != null
|
||||
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex)
|
||||
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex);
|
||||
? context.Load(storageKind, (int)ioVariable, primVertex, elemIndex)
|
||||
: context.Load(storageKind, (int)ioVariable, elemIndex);
|
||||
}
|
||||
|
||||
public static Operand Load(
|
||||
|
@ -577,22 +597,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Operand elemIndex)
|
||||
{
|
||||
return primVertex != null
|
||||
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex)
|
||||
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex);
|
||||
}
|
||||
|
||||
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
if (a.Type == OperandType.Constant)
|
||||
{
|
||||
context.Config.SetUsedConstantBuffer(a.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Config.SetUsedFeature(FeatureFlags.CbIndexing);
|
||||
}
|
||||
|
||||
return context.Add(Instruction.LoadConstant, Local(), a, b);
|
||||
? context.Load(storageKind, (int)ioVariable, primVertex, arrayIndex, elemIndex)
|
||||
: context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex);
|
||||
}
|
||||
|
||||
public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b)
|
||||
|
|
|
@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
InstanceId = 1 << 3,
|
||||
DrawParameters = 1 << 4,
|
||||
RtLayer = 1 << 5,
|
||||
CbIndexing = 1 << 6,
|
||||
IaIndexing = 1 << 7,
|
||||
OaIndexing = 1 << 8,
|
||||
FixedFuncAttr = 1 << 9
|
||||
|
|
|
@ -32,25 +32,49 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
continue;
|
||||
}
|
||||
|
||||
if (handleAsgOp.Inst != Instruction.LoadConstant)
|
||||
if (handleAsgOp.Inst != Instruction.Load ||
|
||||
handleAsgOp.StorageKind != StorageKind.ConstantBuffer ||
|
||||
handleAsgOp.SourcesCount != 4)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Operand ldcSrc0 = handleAsgOp.GetSource(0);
|
||||
|
||||
if (ldcSrc0.Type != OperandType.Constant ||
|
||||
!config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) ||
|
||||
src0CbufSlot != 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Operand ldcSrc1 = handleAsgOp.GetSource(1);
|
||||
|
||||
if (ldcSrc0.Type != OperandType.Constant || ldcSrc0.Value != 2)
|
||||
// We expect field index 0 to be accessed.
|
||||
if (ldcSrc1.Type != OperandType.Constant || ldcSrc1.Value != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ldcSrc1.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32)
|
||||
Operand ldcSrc2 = handleAsgOp.GetSource(2);
|
||||
|
||||
// FIXME: This is missing some checks, for example, a check to ensure that the shift value is 2.
|
||||
// Might be not worth fixing since if that doesn't kick in, the result will be no texture
|
||||
// to access anyway which is also wrong.
|
||||
// Plus this whole transform is fundamentally flawed as-is since we have no way to know the array size.
|
||||
// Eventually, this should be entirely removed in favor of a implementation that supports true bindless
|
||||
// texture access.
|
||||
if (!(ldcSrc2.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(shrOp.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add)
|
||||
if (!(shrOp.GetSource(0).AsgOp is Operation shrOp2) || shrOp2.Inst != Instruction.ShiftRightU32)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(shrOp2.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
{
|
||||
static class ConstantFolding
|
||||
{
|
||||
public static void RunPass(Operation operation)
|
||||
public static void RunPass(ShaderConfig config, Operation operation)
|
||||
{
|
||||
if (!AreAllSourcesConstant(operation))
|
||||
{
|
||||
|
@ -153,8 +153,21 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
|
||||
break;
|
||||
|
||||
case Instruction.LoadConstant:
|
||||
operation.TurnIntoCopy(Cbuf(operation.GetSource(0).Value, operation.GetSource(1).Value));
|
||||
case Instruction.Load:
|
||||
if (operation.StorageKind == StorageKind.ConstantBuffer && operation.SourcesCount == 4)
|
||||
{
|
||||
int binding = operation.GetSource(0).Value;
|
||||
int fieldIndex = operation.GetSource(1).Value;
|
||||
|
||||
if (config.ResourceManager.TryGetConstantBufferSlot(binding, out int cbufSlot) && fieldIndex == 0)
|
||||
{
|
||||
int vecIndex = operation.GetSource(2).Value;
|
||||
int elemIndex = operation.GetSource(3).Value;
|
||||
int cbufOffset = vecIndex * 4 + elemIndex;
|
||||
|
||||
operation.TurnIntoCopy(Cbuf(cbufSlot, cbufOffset));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Maximum:
|
||||
|
|
|
@ -347,21 +347,23 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
return wordOffset;
|
||||
}
|
||||
|
||||
Operand[] sources = new Operand[operation.SourcesCount];
|
||||
Operand cbufOffset = GetCbufOffset();
|
||||
Operand vecIndex = Local();
|
||||
Operand elemIndex = Local();
|
||||
|
||||
node.List.AddBefore(node, new Operation(Instruction.ShiftRightU32, 0, vecIndex, cbufOffset, Const(2)));
|
||||
node.List.AddBefore(node, new Operation(Instruction.BitwiseAnd, 0, elemIndex, cbufOffset, Const(3)));
|
||||
|
||||
Operand[] sources = new Operand[4];
|
||||
|
||||
int cbSlot = UbeFirstCbuf + storageIndex;
|
||||
|
||||
sources[0] = Const(cbSlot);
|
||||
sources[1] = GetCbufOffset();
|
||||
sources[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
|
||||
sources[1] = Const(0);
|
||||
sources[2] = vecIndex;
|
||||
sources[3] = elemIndex;
|
||||
|
||||
config.SetUsedConstantBuffer(cbSlot);
|
||||
|
||||
for (int index = 2; index < operation.SourcesCount; index++)
|
||||
{
|
||||
sources[index] = operation.GetSource(index);
|
||||
}
|
||||
|
||||
Operation ldcOp = new Operation(Instruction.LoadConstant, operation.Dest, sources);
|
||||
Operation ldcOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, operation.Dest, sources);
|
||||
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
{
|
||||
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
||||
{
|
||||
RunOptimizationPasses(blocks);
|
||||
RunOptimizationPasses(blocks, config);
|
||||
|
||||
int sbUseMask = 0;
|
||||
int ubeUseMask = 0;
|
||||
|
@ -31,10 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
|
||||
|
||||
// Run optimizations one last time to remove any code that is now optimizable after above passes.
|
||||
RunOptimizationPasses(blocks);
|
||||
RunOptimizationPasses(blocks, config);
|
||||
}
|
||||
|
||||
private static void RunOptimizationPasses(BasicBlock[] blocks)
|
||||
private static void RunOptimizationPasses(BasicBlock[] blocks, ShaderConfig config)
|
||||
{
|
||||
bool modified;
|
||||
|
||||
|
@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
continue;
|
||||
}
|
||||
|
||||
ConstantFolding.RunPass(operation);
|
||||
ConstantFolding.RunPass(config, operation);
|
||||
Simplification.RunPass(operation);
|
||||
|
||||
if (DestIsLocalVar(operation))
|
||||
|
|
126
src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
Normal file
126
src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
class ResourceManager
|
||||
{
|
||||
private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||
|
||||
private readonly IGpuAccessor _gpuAccessor;
|
||||
private readonly ShaderProperties _properties;
|
||||
private readonly string _stagePrefix;
|
||||
|
||||
private readonly int[] _cbSlotToBindingMap;
|
||||
|
||||
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||
|
||||
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
||||
{
|
||||
_gpuAccessor = gpuAccessor;
|
||||
_properties = properties;
|
||||
_stagePrefix = GetShaderStagePrefix(stage);
|
||||
|
||||
_cbSlotToBindingMap = new int[18];
|
||||
_cbSlotToBindingMap.AsSpan().Fill(-1);
|
||||
|
||||
_usedConstantBufferBindings = new HashSet<int>();
|
||||
|
||||
properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType()));
|
||||
}
|
||||
|
||||
public int GetConstantBufferBinding(int slot)
|
||||
{
|
||||
int binding = _cbSlotToBindingMap[slot];
|
||||
if (binding < 0)
|
||||
{
|
||||
binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
|
||||
_cbSlotToBindingMap[slot] = binding;
|
||||
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
|
||||
AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
public bool TryGetConstantBufferSlot(int binding, out int slot)
|
||||
{
|
||||
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
||||
{
|
||||
if (_cbSlotToBindingMap[slot] == binding)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
slot = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetUsedConstantBufferBinding(int binding)
|
||||
{
|
||||
_usedConstantBufferBindings.Add(binding);
|
||||
}
|
||||
|
||||
public BufferDescriptor[] GetConstantBufferDescriptors()
|
||||
{
|
||||
var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count];
|
||||
|
||||
int descriptorIndex = 0;
|
||||
|
||||
for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
||||
{
|
||||
int binding = _cbSlotToBindingMap[slot];
|
||||
|
||||
if (binding >= 0 && _usedConstantBufferBindings.Contains(binding))
|
||||
{
|
||||
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptors.Length != descriptorIndex)
|
||||
{
|
||||
Array.Resize(ref descriptors, descriptorIndex);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
private void AddNewConstantBuffer(int binding, string name)
|
||||
{
|
||||
StructureType type = new StructureType(new[]
|
||||
{
|
||||
new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16)
|
||||
});
|
||||
|
||||
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
|
||||
}
|
||||
|
||||
public void InheritFrom(ResourceManager other)
|
||||
{
|
||||
for (int i = 0; i < other._cbSlotToBindingMap.Length; i++)
|
||||
{
|
||||
int binding = other._cbSlotToBindingMap[i];
|
||||
|
||||
if (binding >= 0)
|
||||
{
|
||||
_cbSlotToBindingMap[i] = binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetShaderStagePrefix(ShaderStage stage)
|
||||
{
|
||||
uint index = (uint)stage;
|
||||
|
||||
if (index >= _stagePrefixes.Length)
|
||||
{
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
return _stagePrefixes[index];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
|
@ -16,6 +15,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
||||
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
||||
bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
||||
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
|
||||
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
|
@ -45,6 +45,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
if (hasVectorIndexingBug)
|
||||
{
|
||||
InsertVectorComponentSelect(node, config);
|
||||
}
|
||||
|
||||
LinkedListNode<INode> nextNode = node.Next;
|
||||
|
||||
if (operation is TextureOperation texOp)
|
||||
|
@ -71,6 +76,84 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
private static void InsertVectorComponentSelect(LinkedListNode<INode> node, ShaderConfig config)
|
||||
{
|
||||
Operation operation = (Operation)node.Value;
|
||||
|
||||
if (operation.Inst != Instruction.Load ||
|
||||
operation.StorageKind != StorageKind.ConstantBuffer ||
|
||||
operation.SourcesCount < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand bindingIndex = operation.GetSource(0);
|
||||
Operand fieldIndex = operation.GetSource(1);
|
||||
Operand elemIndex = operation.GetSource(operation.SourcesCount - 1);
|
||||
|
||||
if (bindingIndex.Type != OperandType.Constant ||
|
||||
fieldIndex.Type != OperandType.Constant ||
|
||||
elemIndex.Type == OperandType.Constant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BufferDefinition buffer = config.Properties.ConstantBuffers[bindingIndex.Value];
|
||||
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||
|
||||
int elemCount = (field.Type & AggregateType.ElementCountMask) switch
|
||||
{
|
||||
AggregateType.Vector2 => 2,
|
||||
AggregateType.Vector3 => 3,
|
||||
AggregateType.Vector4 => 4,
|
||||
_ => 1
|
||||
};
|
||||
|
||||
if (elemCount == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand result = null;
|
||||
|
||||
for (int i = 0; i < elemCount; i++)
|
||||
{
|
||||
Operand value = Local();
|
||||
Operand[] inputs = new Operand[operation.SourcesCount];
|
||||
|
||||
for (int srcIndex = 0; srcIndex < inputs.Length - 1; srcIndex++)
|
||||
{
|
||||
inputs[srcIndex] = operation.GetSource(srcIndex);
|
||||
}
|
||||
|
||||
inputs[inputs.Length - 1] = Const(i);
|
||||
|
||||
Operation loadOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, value, inputs);
|
||||
|
||||
node.List.AddBefore(node, loadOp);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand isCurrentIndex = Local();
|
||||
Operand selection = Local();
|
||||
|
||||
Operation compareOp = new Operation(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) });
|
||||
Operation selectOp = new Operation(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result });
|
||||
|
||||
node.List.AddBefore(node, compareOp);
|
||||
node.List.AddBefore(node, selectOp);
|
||||
|
||||
result = selection;
|
||||
}
|
||||
}
|
||||
|
||||
operation.TurnIntoCopy(result);
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config)
|
||||
{
|
||||
Operation operation = (Operation)node.Value;
|
||||
|
@ -90,6 +173,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return local;
|
||||
}
|
||||
|
||||
Operand PrependStorageOperation(Instruction inst, StorageKind storageKind, params Operand[] sources)
|
||||
{
|
||||
Operand local = Local();
|
||||
|
||||
node.List.AddBefore(node, new Operation(inst, storageKind, local, sources));
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
Operand PrependExistingOperation(Operation operation)
|
||||
{
|
||||
Operand local = Local();
|
||||
|
@ -204,8 +296,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
cbeUseMask &= ~(1 << slot);
|
||||
|
||||
config.SetUsedConstantBuffer(cbSlot);
|
||||
|
||||
Operand previousResult = PrependExistingOperation(storageOp);
|
||||
|
||||
int cbOffset = GetConstantUbeOffset(slot);
|
||||
|
@ -216,18 +306,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
|
||||
|
||||
Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2));
|
||||
Operand vecIndex = PrependOperation(Instruction.ShiftRightU32, cbIndex, Const(2));
|
||||
Operand elemIndex = PrependOperation(Instruction.BitwiseAnd, cbIndex, Const(3));
|
||||
|
||||
Operand[] sourcesCb = new Operand[operation.SourcesCount];
|
||||
Operand[] sourcesCb = new Operand[4];
|
||||
|
||||
sourcesCb[0] = Const(cbSlot);
|
||||
sourcesCb[1] = cbIndex;
|
||||
sourcesCb[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
|
||||
sourcesCb[1] = Const(0);
|
||||
sourcesCb[2] = vecIndex;
|
||||
sourcesCb[3] = elemIndex;
|
||||
|
||||
for (int index = 2; index < operation.SourcesCount; index++)
|
||||
{
|
||||
sourcesCb[index] = operation.GetSource(index);
|
||||
}
|
||||
|
||||
Operand ldcResult = PrependOperation(Instruction.LoadConstant, sourcesCb);
|
||||
Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb);
|
||||
|
||||
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public TranslationOptions Options { get; }
|
||||
|
||||
public ShaderProperties Properties { get; }
|
||||
|
||||
public ResourceManager ResourceManager { get; }
|
||||
|
||||
public bool TransformFeedbackEnabled { get; }
|
||||
|
||||
private TransformFeedbackOutput[] _transformFeedbackOutputs;
|
||||
|
@ -109,7 +113,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public int AccessibleStorageBuffersMask { get; private set; }
|
||||
public int AccessibleConstantBuffersMask { get; private set; }
|
||||
|
||||
private int _usedConstantBuffers;
|
||||
private int _usedStorageBuffers;
|
||||
private int _usedStorageBuffersWrite;
|
||||
|
||||
|
@ -128,20 +131,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
private readonly Dictionary<int, int> _sbSlots;
|
||||
private readonly Dictionary<int, int> _sbSlotsReverse;
|
||||
|
||||
private BufferDescriptor[] _cachedConstantBufferDescriptors;
|
||||
private BufferDescriptor[] _cachedStorageBufferDescriptors;
|
||||
private TextureDescriptor[] _cachedTextureDescriptors;
|
||||
private TextureDescriptor[] _cachedImageDescriptors;
|
||||
|
||||
private int _firstConstantBufferBinding;
|
||||
private int _firstStorageBufferBinding;
|
||||
|
||||
public int FirstConstantBufferBinding => _firstConstantBufferBinding;
|
||||
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
|
||||
|
||||
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||
public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||
{
|
||||
Stage = ShaderStage.Compute;
|
||||
Stage = stage;
|
||||
GpuAccessor = gpuAccessor;
|
||||
Options = options;
|
||||
|
||||
|
@ -158,6 +158,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
_sbSlots = new Dictionary<int, int>();
|
||||
_sbSlotsReverse = new Dictionary<int, int>();
|
||||
|
||||
Properties = new ShaderProperties();
|
||||
ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
|
||||
}
|
||||
|
||||
public ShaderConfig(
|
||||
|
@ -165,9 +168,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
OutputTopology outputTopology,
|
||||
int maxOutputVertices,
|
||||
IGpuAccessor gpuAccessor,
|
||||
TranslationOptions options) : this(gpuAccessor, options)
|
||||
TranslationOptions options) : this(stage, gpuAccessor, options)
|
||||
{
|
||||
Stage = stage;
|
||||
ThreadsPerInputPrimitive = 1;
|
||||
OutputTopology = outputTopology;
|
||||
MaxOutputVertices = maxOutputVertices;
|
||||
|
@ -179,9 +181,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options)
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options)
|
||||
{
|
||||
Stage = header.Stage;
|
||||
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||
ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
|
||||
OutputTopology = header.OutputTopology;
|
||||
|
@ -428,12 +429,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public void InheritFrom(ShaderConfig other)
|
||||
{
|
||||
ResourceManager.InheritFrom(other.ResourceManager);
|
||||
|
||||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||
UsedFeatures |= other.UsedFeatures;
|
||||
|
||||
UsedInputAttributes |= other.UsedInputAttributes;
|
||||
UsedOutputAttributes |= other.UsedOutputAttributes;
|
||||
_usedConstantBuffers |= other._usedConstantBuffers;
|
||||
_usedStorageBuffers |= other._usedStorageBuffers;
|
||||
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
|
||||
|
||||
|
@ -641,11 +643,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
AccessibleConstantBuffersMask = ubeMask;
|
||||
}
|
||||
|
||||
public void SetUsedConstantBuffer(int slot)
|
||||
{
|
||||
_usedConstantBuffers |= 1 << slot;
|
||||
}
|
||||
|
||||
public void SetUsedStorageBuffer(int slot, bool write)
|
||||
{
|
||||
int mask = 1 << slot;
|
||||
|
@ -762,27 +759,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return meta;
|
||||
}
|
||||
|
||||
public BufferDescriptor[] GetConstantBufferDescriptors()
|
||||
{
|
||||
if (_cachedConstantBufferDescriptors != null)
|
||||
{
|
||||
return _cachedConstantBufferDescriptors;
|
||||
}
|
||||
|
||||
int usedMask = _usedConstantBuffers;
|
||||
|
||||
if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
|
||||
{
|
||||
usedMask |= (int)GpuAccessor.QueryConstantBufferUse();
|
||||
}
|
||||
|
||||
return _cachedConstantBufferDescriptors = GetUniformBufferDescriptors(
|
||||
usedMask,
|
||||
UsedFeatures.HasFlag(FeatureFlags.CbIndexing),
|
||||
out _firstConstantBufferBinding,
|
||||
GpuAccessor.QueryBindingConstantBuffer);
|
||||
}
|
||||
|
||||
public BufferDescriptor[] GetStorageBufferDescriptors()
|
||||
{
|
||||
if (_cachedStorageBufferDescriptors != null)
|
||||
|
@ -798,47 +774,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
GpuAccessor.QueryBindingStorageBuffer);
|
||||
}
|
||||
|
||||
private static BufferDescriptor[] GetUniformBufferDescriptors(int usedMask, bool isArray, out int firstBinding, Func<int, int> getBindingCallback)
|
||||
{
|
||||
firstBinding = 0;
|
||||
int lastSlot = -1;
|
||||
bool hasFirstBinding = false;
|
||||
var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
|
||||
|
||||
for (int i = 0; i < descriptors.Length; i++)
|
||||
{
|
||||
int slot = BitOperations.TrailingZeroCount(usedMask);
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
// The next array entries also consumes bindings, even if they are unused.
|
||||
for (int j = lastSlot + 1; j < slot; j++)
|
||||
{
|
||||
int binding = getBindingCallback(j);
|
||||
|
||||
if (!hasFirstBinding)
|
||||
{
|
||||
firstBinding = binding;
|
||||
hasFirstBinding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastSlot = slot;
|
||||
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot);
|
||||
|
||||
if (!hasFirstBinding)
|
||||
{
|
||||
firstBinding = descriptors[i].Binding;
|
||||
hasFirstBinding = true;
|
||||
}
|
||||
|
||||
usedMask &= ~(1 << slot);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
private BufferDescriptor[] GetStorageBufferDescriptors(
|
||||
int usedMask,
|
||||
int writtenMask,
|
||||
|
@ -1009,7 +944,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
|
||||
{
|
||||
return new ShaderProgramInfo(
|
||||
GetConstantBufferDescriptors(),
|
||||
ResourceManager.GetConstantBufferDescriptors(),
|
||||
GetStorageBufferDescriptors(),
|
||||
GetTextureDescriptors(),
|
||||
GetImageDescriptors(),
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
if (options.Flags.HasFlag(TranslationFlags.Compute))
|
||||
{
|
||||
config = new ShaderConfig(gpuAccessor, options);
|
||||
config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options);
|
||||
|
||||
program = Decoder.Decode(config, address);
|
||||
}
|
||||
|
|
Reference in a new issue