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 FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 5027;
|
private const uint CodeGenVersion = 4646;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Shader
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
|
@ -16,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
private readonly ResourceCounts _resourceCounts;
|
private readonly ResourceCounts _resourceCounts;
|
||||||
private readonly int _stageIndex;
|
private readonly int _stageIndex;
|
||||||
|
|
||||||
|
private readonly int[] _constantBufferBindings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU accessor.
|
/// Creates a new GPU accessor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -25,6 +28,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
_context = context;
|
_context = context;
|
||||||
_resourceCounts = resourceCounts;
|
_resourceCounts = resourceCounts;
|
||||||
_stageIndex = stageIndex;
|
_stageIndex = stageIndex;
|
||||||
|
|
||||||
|
if (context.Capabilities.Api != TargetApi.Vulkan)
|
||||||
|
{
|
||||||
|
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
|
||||||
|
_constantBufferBindings.AsSpan().Fill(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int QueryBindingConstantBuffer(int index)
|
public int QueryBindingConstantBuffer(int index)
|
||||||
|
@ -36,7 +45,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
}
|
}
|
||||||
else
|
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 Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
|
@ -102,13 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
var cBufferDescriptors = context.Config.GetConstantBufferDescriptors();
|
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||||
if (cBufferDescriptors.Length != 0)
|
|
||||||
{
|
|
||||||
DeclareUniforms(context, cBufferDescriptors);
|
|
||||||
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
|
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
|
||||||
if (sBufferDescriptors.Length != 0)
|
if (sBufferDescriptors.Length != 0)
|
||||||
|
@ -265,18 +260,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
scaleElements++; // Also includes render target scale, for gl_FragCoord.
|
scaleElements++; // Also includes render target scale, for gl_FragCoord.
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements);
|
|
||||||
|
|
||||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
|
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
|
||||||
{
|
{
|
||||||
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
|
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
|
||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
DeclareSupportUniformBlock(context, context.Config.Stage, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 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) + "]";
|
foreach (BufferDefinition buffer in buffers)
|
||||||
|
|
||||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
|
|
||||||
{
|
{
|
||||||
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
string layout = buffer.Layout switch
|
||||||
|
|
||||||
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 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}");
|
foreach (StructureField field in buffer.Type.Fields)
|
||||||
context.EnterScope();
|
{
|
||||||
context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";");
|
if (field.Type.HasFlag(AggregateType.Array))
|
||||||
context.LeaveScope(";");
|
{
|
||||||
|
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};");
|
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)
|
private static void AppendHelperFunction(CodeGenContext context, string filename)
|
||||||
{
|
{
|
||||||
string code = EmbeddedResources.ReadAllText(filename);
|
string code = EmbeddedResources.ReadAllText(filename);
|
||||||
|
|
|
@ -15,18 +15,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
public const string DataName = "data";
|
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 BlockSuffix = "block";
|
||||||
|
|
||||||
public const string UniformNamePrefix = "c";
|
|
||||||
public const string UniformNameSuffix = "data";
|
|
||||||
|
|
||||||
public const string LocalMemoryName = "local_mem";
|
public const string LocalMemoryName = "local_mem";
|
||||||
public const string SharedMemoryName = "shared_mem";
|
public const string SharedMemoryName = "shared_mem";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
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)
|
if (scale == 1.0)
|
||||||
{
|
{
|
||||||
return inputVec;
|
return inputVec;
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
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)
|
if (scale == 1.0)
|
||||||
{
|
{
|
||||||
return size;
|
return size;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
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)
|
if (scale == 1.0)
|
||||||
{
|
{
|
||||||
return inputVec;
|
return inputVec;
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
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)
|
if (scale == 1.0)
|
||||||
{
|
{
|
||||||
return size;
|
return size;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
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)
|
if (scale == 1.0)
|
||||||
{
|
{
|
||||||
return inputVec;
|
return inputVec;
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
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)
|
if (scale == 1.0)
|
||||||
{
|
{
|
||||||
return size;
|
return size;
|
||||||
|
|
|
@ -167,9 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
case Instruction.Load:
|
case Instruction.Load:
|
||||||
return Load(context, operation);
|
return Load(context, operation);
|
||||||
|
|
||||||
case Instruction.LoadConstant:
|
|
||||||
return LoadConstant(context, operation);
|
|
||||||
|
|
||||||
case Instruction.LoadLocal:
|
case Instruction.LoadLocal:
|
||||||
return LoadLocal(context, operation);
|
return LoadLocal(context, operation);
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
Add(Instruction.ImageAtomic, InstType.Special);
|
Add(Instruction.ImageAtomic, InstType.Special);
|
||||||
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
|
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
|
||||||
Add(Instruction.Load, InstType.Special);
|
Add(Instruction.Load, InstType.Special);
|
||||||
Add(Instruction.LoadConstant, InstType.Special);
|
|
||||||
Add(Instruction.LoadLocal, InstType.Special);
|
Add(Instruction.LoadLocal, InstType.Special);
|
||||||
Add(Instruction.LoadShared, InstType.Special);
|
Add(Instruction.LoadShared, InstType.Special);
|
||||||
Add(Instruction.LoadStorage, InstType.Special);
|
Add(Instruction.LoadStorage, InstType.Special);
|
||||||
|
|
|
@ -215,29 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
return GenerateLoadOrStore(context, operation, isStore: false);
|
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)
|
public static string LoadLocal(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
|
return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
|
||||||
|
@ -809,9 +786,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
string varName;
|
string varName;
|
||||||
AggregateType varType;
|
AggregateType varType;
|
||||||
int srcIndex = 0;
|
int srcIndex = 0;
|
||||||
|
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
|
||||||
|
|
||||||
switch (storageKind)
|
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.Input:
|
||||||
case StorageKind.InputPerPatch:
|
case StorageKind.InputPerPatch:
|
||||||
case StorageKind.Output:
|
case StorageKind.Output:
|
||||||
|
@ -864,40 +861,39 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
varName = $"gl_out[{expr}].{varName}";
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
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)
|
if (isStore)
|
||||||
{
|
{
|
||||||
varType &= AggregateType.ElementTypeMask;
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
|
|
@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32),
|
IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32),
|
||||||
IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location),
|
IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location),
|
||||||
IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32),
|
IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32),
|
||||||
IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool),
|
|
||||||
IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
|
||||||
IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool),
|
IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool),
|
||||||
|
@ -46,8 +45,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config),
|
IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config),
|
||||||
IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"),
|
IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"),
|
||||||
IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"),
|
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.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32),
|
||||||
IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32),
|
IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32),
|
||||||
IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", 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.Argument => GetArgumentName(operand.Value),
|
||||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
|
|
||||||
OperandType.LocalVariable => _locals[operand],
|
OperandType.LocalVariable => _locals[operand],
|
||||||
OperandType.Undefined => DefaultNames.UndefinedName,
|
OperandType.Undefined => DefaultNames.UndefinedName,
|
||||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
|
||||||
{
|
{
|
||||||
return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), 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)
|
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.Input:
|
||||||
case StorageKind.InputPerPatch:
|
case StorageKind.InputPerPatch:
|
||||||
case StorageKind.Output:
|
case StorageKind.Output:
|
||||||
|
|
|
@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
|
|
||||||
public int InputVertices { get; }
|
public int InputVertices { get; }
|
||||||
|
|
||||||
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
|
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
|
||||||
public Instruction SupportBuffer { get; set; }
|
|
||||||
public Instruction UniformBuffersArray { get; set; }
|
|
||||||
public Instruction StorageBuffersArray { get; set; }
|
public Instruction StorageBuffersArray { get; set; }
|
||||||
public Instruction LocalMemory { get; set; }
|
public Instruction LocalMemory { get; set; }
|
||||||
public Instruction SharedMemory { get; set; }
|
public Instruction SharedMemory { get; set; }
|
||||||
|
@ -217,7 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
{
|
{
|
||||||
IrOperandType.Argument => GetArgument(type, operand),
|
IrOperandType.Argument => GetArgument(type, operand),
|
||||||
IrOperandType.Constant => GetConstant(type, operand),
|
IrOperandType.Constant => GetConstant(type, operand),
|
||||||
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
|
|
||||||
IrOperandType.LocalVariable => GetLocal(type, operand),
|
IrOperandType.LocalVariable => GetLocal(type, operand),
|
||||||
IrOperandType.Undefined => GetUndefined(type),
|
IrOperandType.Undefined => GetUndefined(type),
|
||||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.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)
|
public Instruction GetLocalPointer(AstOperand local)
|
||||||
{
|
{
|
||||||
return _locals[local];
|
return _locals[local];
|
||||||
|
|
|
@ -98,8 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
DeclareLocalMemory(context, localMemorySize);
|
DeclareLocalMemory(context, localMemorySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclareSupportBuffer(context);
|
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||||
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
|
|
||||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
||||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||||
|
@ -127,84 +126,63 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
return variable;
|
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));
|
SpvInstruction[] structFieldTypes = new SpvInstruction[buffer.Type.Fields.Length];
|
||||||
var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
|
int[] structFieldOffsets = new int[buffer.Type.Fields.Length];
|
||||||
var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
|
|
||||||
|
|
||||||
context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
|
for (int fieldIndex = 0; fieldIndex < buffer.Type.Fields.Length; fieldIndex++)
|
||||||
context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
|
{
|
||||||
|
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);
|
if (field.Type.HasFlag(AggregateType.Array))
|
||||||
context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
|
{
|
||||||
context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
|
// We can't decorate the type more than once.
|
||||||
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
|
if (decoratedTypes.Add(structFieldTypes[fieldIndex]))
|
||||||
context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
|
{
|
||||||
context.Decorate(supportBufferStructType, Decoration.Block);
|
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
|
||||||
|
}
|
||||||
|
|
||||||
var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
|
offset += fieldSize * field.ArrayLength;
|
||||||
var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset += fieldSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
var ubStructType = context.TypeStruct(false, structFieldTypes);
|
||||||
context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
|
|
||||||
|
|
||||||
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)
|
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
||||||
{
|
|
||||||
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 ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||||
|
|
||||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
|
context.Name(ubVariable, buffer.Name);
|
||||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
||||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
|
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
||||||
context.AddGlobalVariable(ubVariable);
|
context.AddGlobalVariable(ubVariable);
|
||||||
|
context.ConstantBuffers.Add(buffer.Binding, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,25 +372,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
{
|
{
|
||||||
foreach (var ioDefinition in info.IoDefinitions)
|
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;
|
PixelImap iq = PixelImap.Unused;
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
if (context.Config.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
|
var ioVariable = ioDefinition.IoVariable;
|
||||||
if (ioVariable == IoVariable.UserDefined)
|
if (ioVariable == IoVariable.UserDefined)
|
||||||
{
|
{
|
||||||
iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
|
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);
|
DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
Add(Instruction.ImageStore, GenerateImageStore);
|
Add(Instruction.ImageStore, GenerateImageStore);
|
||||||
Add(Instruction.IsNan, GenerateIsNan);
|
Add(Instruction.IsNan, GenerateIsNan);
|
||||||
Add(Instruction.Load, GenerateLoad);
|
Add(Instruction.Load, GenerateLoad);
|
||||||
Add(Instruction.LoadConstant, GenerateLoadConstant);
|
|
||||||
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
||||||
Add(Instruction.LoadShared, GenerateLoadShared);
|
Add(Instruction.LoadShared, GenerateLoadShared);
|
||||||
Add(Instruction.LoadStorage, GenerateLoadStorage);
|
Add(Instruction.LoadStorage, GenerateLoadStorage);
|
||||||
|
@ -313,10 +312,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
|
|
||||||
for (int i = 0; i < args.Length; i++)
|
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)
|
if (i >= function.InArguments.Length)
|
||||||
{
|
{
|
||||||
args[i] = context.GetLocalPointer(operand);
|
args[i] = context.GetLocalPointer((AstOperand)operand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -867,68 +867,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
return GenerateLoadOrStore(context, operation, isStore: false);
|
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)
|
private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
|
return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
|
||||||
|
@ -1990,12 +1928,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
{
|
{
|
||||||
StorageKind storageKind = operation.StorageKind;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
SpvInstruction pointer;
|
StorageClass storageClass;
|
||||||
|
SpvInstruction baseObj;
|
||||||
AggregateType varType;
|
AggregateType varType;
|
||||||
int srcIndex = 0;
|
int srcIndex = 0;
|
||||||
|
|
||||||
switch (storageKind)
|
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.Input:
|
||||||
case StorageKind.InputPerPatch:
|
case StorageKind.InputPerPatch:
|
||||||
case StorageKind.Output:
|
case StorageKind.Output:
|
||||||
|
@ -2038,33 +1996,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
{
|
{
|
||||||
varType = context.Config.GetFragmentOutputColorType(location);
|
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
|
else
|
||||||
{
|
{
|
||||||
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||||
|
@ -2072,55 +2003,57 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
|
|
||||||
varType &= AggregateType.ElementTypeMask;
|
varType &= AggregateType.ElementTypeMask;
|
||||||
|
|
||||||
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
|
storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
||||||
var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
|
||||||
|
|
||||||
var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component);
|
var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component);
|
||||||
var dict = isPerPatch
|
var dict = isPerPatch
|
||||||
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
|
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||||
: (isOutput ? context.Outputs : context.Inputs);
|
: (isOutput ? context.Outputs : context.Inputs);
|
||||||
|
|
||||||
SpvInstruction baseObj = dict[ioDefinition];
|
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
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)
|
if (isStore)
|
||||||
{
|
{
|
||||||
context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex)));
|
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)
|
if (context.Config.Stage == ShaderStage.Vertex)
|
||||||
{
|
{
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
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);
|
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
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));
|
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 scale = context.Load(context.TypeFP32(), scaleElemPointer);
|
||||||
|
|
||||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
||||||
|
@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
if (context.Config.Stage == ShaderStage.Vertex)
|
||||||
{
|
{
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
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);
|
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
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));
|
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 scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
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,
|
// 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.
|
// 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())
|
else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Graphics.Shader.Decoders;
|
using Ryujinx.Graphics.Shader.Decoders;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
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 addr = context.IAdd(srcA, Const(Imm16ToSInt(op.CbufOffset)));
|
||||||
Operand wordOffset = context.ShiftRightU32(addr, Const(2));
|
Operand wordOffset = context.ShiftRightU32(addr, Const(2));
|
||||||
Operand bitOffset = GetBitOffset(context, addr);
|
|
||||||
|
|
||||||
for (int index = 0; index < count; index++)
|
for (int index = 0; index < count; index++)
|
||||||
{
|
{
|
||||||
|
@ -92,11 +92,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand offset = context.IAdd(wordOffset, Const(index));
|
Operand offset = context.IAdd(wordOffset, Const(index));
|
||||||
Operand value = context.LoadConstant(slot, offset);
|
Operand value = EmitLoadConstant(context, slot, offset);
|
||||||
|
|
||||||
if (isSmallInt)
|
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);
|
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));
|
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(
|
private static Operand EmitAtomicOp(
|
||||||
EmitterContext context,
|
EmitterContext context,
|
||||||
StorageKind storageKind,
|
StorageKind storageKind,
|
||||||
|
|
|
@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
ImageAtomic,
|
ImageAtomic,
|
||||||
IsNan,
|
IsNan,
|
||||||
Load,
|
Load,
|
||||||
LoadConstant,
|
|
||||||
LoadGlobal,
|
LoadGlobal,
|
||||||
LoadLocal,
|
LoadLocal,
|
||||||
LoadShared,
|
LoadShared,
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
FragmentCoord,
|
FragmentCoord,
|
||||||
FragmentOutputColor,
|
FragmentOutputColor,
|
||||||
FragmentOutputDepth,
|
FragmentOutputDepth,
|
||||||
FragmentOutputIsBgra, // TODO: Remove and use constant buffer access.
|
|
||||||
FrontColorDiffuse,
|
FrontColorDiffuse,
|
||||||
FrontColorSpecular,
|
FrontColorSpecular,
|
||||||
FrontFacing,
|
FrontFacing,
|
||||||
|
@ -34,8 +33,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
SubgroupLaneId,
|
SubgroupLaneId,
|
||||||
SubgroupLeMask,
|
SubgroupLeMask,
|
||||||
SubgroupLtMask,
|
SubgroupLtMask,
|
||||||
SupportBlockViewInverse, // TODO: Remove and use constant buffer access.
|
|
||||||
SupportBlockRenderScale, // TODO: Remove and use constant buffer access.
|
|
||||||
TessellationCoord,
|
TessellationCoord,
|
||||||
TessellationLevelInner,
|
TessellationLevelInner,
|
||||||
TessellationLevelOuter,
|
TessellationLevelOuter,
|
||||||
|
|
|
@ -15,9 +15,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
public int Value { get; }
|
public int Value { get; }
|
||||||
|
|
||||||
public int CbufSlot { get; }
|
|
||||||
public int CbufOffset { get; }
|
|
||||||
|
|
||||||
private AstOperand()
|
private AstOperand()
|
||||||
{
|
{
|
||||||
Defs = new HashSet<IAstNode>();
|
Defs = new HashSet<IAstNode>();
|
||||||
|
@ -29,16 +26,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
public AstOperand(Operand operand) : this()
|
public AstOperand(Operand operand) : this()
|
||||||
{
|
{
|
||||||
Type = operand.Type;
|
Type = operand.Type;
|
||||||
|
Value = operand.Value;
|
||||||
if (Type == OperandType.ConstantBuffer)
|
|
||||||
{
|
|
||||||
CbufSlot = operand.GetCbufSlot();
|
|
||||||
CbufOffset = operand.GetCbufOffset();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Value = operand.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstOperand(OperandType type, int value = 0) : this()
|
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.ImageAtomic, AggregateType.S32);
|
||||||
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
||||||
Add(Instruction.Load, AggregateType.FP32);
|
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.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
||||||
Add(Instruction.LoadShared, 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.Argument => AggregateType.S32,
|
||||||
OperandType.Constant => AggregateType.S32,
|
OperandType.Constant => AggregateType.S32,
|
||||||
OperandType.ConstantBuffer => AggregateType.FP32,
|
|
||||||
OperandType.Undefined => AggregateType.S32,
|
OperandType.Undefined => AggregateType.S32,
|
||||||
_ => throw new ArgumentException($"Invalid operand type \"{type}\".")
|
_ => 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;
|
Instruction inst = operation.Inst;
|
||||||
StorageKind storageKind = operation.StorageKind;
|
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;
|
if (storageKind.IsInputOrOutput())
|
||||||
bool isOutput = storageKind.IsOutput();
|
|
||||||
bool perPatch = storageKind.IsPerPatch();
|
|
||||||
int location = 0;
|
|
||||||
int component = 0;
|
|
||||||
|
|
||||||
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
|
||||||
{
|
{
|
||||||
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 &&
|
if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
|
||||||
operation.GetSource(2).Type == OperandType.Constant &&
|
|
||||||
context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, 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);
|
bool vectorDest = IsVectorDestInst(inst);
|
||||||
|
@ -105,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
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++)
|
for (int index = 0; index < outDestsCount; index++)
|
||||||
|
|
|
@ -298,6 +298,33 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
return newTemp;
|
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)
|
public AstOperand GetOperand(Operand operand)
|
||||||
{
|
{
|
||||||
if (operand == null)
|
if (operand == null)
|
||||||
|
@ -307,11 +334,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
if (operand.Type != OperandType.LocalVariable)
|
if (operand.Type != OperandType.LocalVariable)
|
||||||
{
|
{
|
||||||
if (operand.Type == OperandType.ConstantBuffer)
|
|
||||||
{
|
|
||||||
Config.SetUsedConstantBuffer(operand.GetCbufSlot());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AstOperand(operand);
|
return new AstOperand(operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader
|
namespace Ryujinx.Graphics.Shader
|
||||||
|
@ -11,8 +13,20 @@ namespace Ryujinx.Graphics.Shader
|
||||||
public T W;
|
public T W;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SupportBufferField
|
||||||
|
{
|
||||||
|
// Must match the order of the fields on the struct.
|
||||||
|
FragmentAlphaTest,
|
||||||
|
FragmentIsBgra,
|
||||||
|
ViewportInverse,
|
||||||
|
FragmentRenderScaleCount,
|
||||||
|
RenderScale
|
||||||
|
}
|
||||||
|
|
||||||
public struct SupportBuffer
|
public struct SupportBuffer
|
||||||
{
|
{
|
||||||
|
internal const int Binding = 0;
|
||||||
|
|
||||||
public static int FieldSize;
|
public static int FieldSize;
|
||||||
public static int RequiredSize;
|
public static int RequiredSize;
|
||||||
|
|
||||||
|
@ -47,6 +61,18 @@ namespace Ryujinx.Graphics.Shader
|
||||||
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
|
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 Vector4<int> FragmentAlphaTest;
|
||||||
public Array8<Vector4<int>> FragmentIsBgra;
|
public Array8<Vector4<int>> FragmentIsBgra;
|
||||||
public Vector4<float> ViewportInverse;
|
public Vector4<float> ViewportInverse;
|
||||||
|
|
|
@ -22,4 +22,35 @@
|
||||||
|
|
||||||
Array = 1 << 10
|
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 x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
|
||||||
Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
|
Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
|
||||||
Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0));
|
Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0));
|
||||||
Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1));
|
Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1));
|
||||||
Operand negativeOne = ConstF(-1.0f);
|
Operand negativeOne = ConstF(-1.0f);
|
||||||
|
|
||||||
this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
|
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).
|
// Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
|
||||||
if (!supportsBgra && (component == 0 || component == 2))
|
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 lblIsBgra = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
|
@ -549,11 +549,31 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(fpType | Instruction.IsNan, Local(), a);
|
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)
|
public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null)
|
||||||
{
|
{
|
||||||
return primVertex != null
|
return primVertex != null
|
||||||
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex)
|
? context.Load(storageKind, (int)ioVariable, primVertex)
|
||||||
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable));
|
: context.Load(storageKind, (int)ioVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand Load(
|
public static Operand Load(
|
||||||
|
@ -564,8 +584,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
Operand elemIndex)
|
Operand elemIndex)
|
||||||
{
|
{
|
||||||
return primVertex != null
|
return primVertex != null
|
||||||
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex)
|
? context.Load(storageKind, (int)ioVariable, primVertex, elemIndex)
|
||||||
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex);
|
: context.Load(storageKind, (int)ioVariable, elemIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand Load(
|
public static Operand Load(
|
||||||
|
@ -577,22 +597,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
Operand elemIndex)
|
Operand elemIndex)
|
||||||
{
|
{
|
||||||
return primVertex != null
|
return primVertex != null
|
||||||
? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex)
|
? context.Load(storageKind, (int)ioVariable, primVertex, arrayIndex, elemIndex)
|
||||||
: context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex);
|
: context.Load(storageKind, (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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b)
|
public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b)
|
||||||
|
|
|
@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
InstanceId = 1 << 3,
|
InstanceId = 1 << 3,
|
||||||
DrawParameters = 1 << 4,
|
DrawParameters = 1 << 4,
|
||||||
RtLayer = 1 << 5,
|
RtLayer = 1 << 5,
|
||||||
CbIndexing = 1 << 6,
|
|
||||||
IaIndexing = 1 << 7,
|
IaIndexing = 1 << 7,
|
||||||
OaIndexing = 1 << 8,
|
OaIndexing = 1 << 8,
|
||||||
FixedFuncAttr = 1 << 9
|
FixedFuncAttr = 1 << 9
|
||||||
|
|
|
@ -32,25 +32,49 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleAsgOp.Inst != Instruction.LoadConstant)
|
if (handleAsgOp.Inst != Instruction.Load ||
|
||||||
|
handleAsgOp.StorageKind != StorageKind.ConstantBuffer ||
|
||||||
|
handleAsgOp.SourcesCount != 4)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand ldcSrc0 = handleAsgOp.GetSource(0);
|
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);
|
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;
|
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;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
{
|
{
|
||||||
static class ConstantFolding
|
static class ConstantFolding
|
||||||
{
|
{
|
||||||
public static void RunPass(Operation operation)
|
public static void RunPass(ShaderConfig config, Operation operation)
|
||||||
{
|
{
|
||||||
if (!AreAllSourcesConstant(operation))
|
if (!AreAllSourcesConstant(operation))
|
||||||
{
|
{
|
||||||
|
@ -153,8 +153,21 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
|
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Instruction.LoadConstant:
|
case Instruction.Load:
|
||||||
operation.TurnIntoCopy(Cbuf(operation.GetSource(0).Value, operation.GetSource(1).Value));
|
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;
|
break;
|
||||||
|
|
||||||
case Instruction.Maximum:
|
case Instruction.Maximum:
|
||||||
|
|
|
@ -347,21 +347,23 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
return wordOffset;
|
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;
|
int cbSlot = UbeFirstCbuf + storageIndex;
|
||||||
|
|
||||||
sources[0] = Const(cbSlot);
|
sources[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
|
||||||
sources[1] = GetCbufOffset();
|
sources[1] = Const(0);
|
||||||
|
sources[2] = vecIndex;
|
||||||
|
sources[3] = elemIndex;
|
||||||
|
|
||||||
config.SetUsedConstantBuffer(cbSlot);
|
Operation ldcOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, operation.Dest, sources);
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
sources[index] = operation.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operation ldcOp = new Operation(Instruction.LoadConstant, operation.Dest, sources);
|
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
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)
|
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
||||||
{
|
{
|
||||||
RunOptimizationPasses(blocks);
|
RunOptimizationPasses(blocks, config);
|
||||||
|
|
||||||
int sbUseMask = 0;
|
int sbUseMask = 0;
|
||||||
int ubeUseMask = 0;
|
int ubeUseMask = 0;
|
||||||
|
@ -31,10 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
|
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
|
||||||
|
|
||||||
// Run optimizations one last time to remove any code that is now optimizable after above passes.
|
// 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;
|
bool modified;
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstantFolding.RunPass(operation);
|
ConstantFolding.RunPass(config, operation);
|
||||||
Simplification.RunPass(operation);
|
Simplification.RunPass(operation);
|
||||||
|
|
||||||
if (DestIsLocalVar(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.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
|
@ -16,6 +15,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
||||||
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
||||||
|
bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
||||||
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
|
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
|
||||||
|
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
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;
|
LinkedListNode<INode> nextNode = node.Next;
|
||||||
|
|
||||||
if (operation is TextureOperation texOp)
|
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)
|
private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
{
|
{
|
||||||
Operation operation = (Operation)node.Value;
|
Operation operation = (Operation)node.Value;
|
||||||
|
@ -90,6 +173,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return local;
|
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 PrependExistingOperation(Operation operation)
|
||||||
{
|
{
|
||||||
Operand local = Local();
|
Operand local = Local();
|
||||||
|
@ -204,8 +296,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
cbeUseMask &= ~(1 << slot);
|
cbeUseMask &= ~(1 << slot);
|
||||||
|
|
||||||
config.SetUsedConstantBuffer(cbSlot);
|
|
||||||
|
|
||||||
Operand previousResult = PrependExistingOperation(storageOp);
|
Operand previousResult = PrependExistingOperation(storageOp);
|
||||||
|
|
||||||
int cbOffset = GetConstantUbeOffset(slot);
|
int cbOffset = GetConstantUbeOffset(slot);
|
||||||
|
@ -216,18 +306,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
|
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
|
||||||
|
|
||||||
Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2));
|
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[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
|
||||||
sourcesCb[1] = cbIndex;
|
sourcesCb[1] = Const(0);
|
||||||
|
sourcesCb[2] = vecIndex;
|
||||||
|
sourcesCb[3] = elemIndex;
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb);
|
||||||
{
|
|
||||||
sourcesCb[index] = operation.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand ldcResult = PrependOperation(Instruction.LoadConstant, sourcesCb);
|
|
||||||
|
|
||||||
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
|
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public TranslationOptions Options { get; }
|
public TranslationOptions Options { get; }
|
||||||
|
|
||||||
|
public ShaderProperties Properties { get; }
|
||||||
|
|
||||||
|
public ResourceManager ResourceManager { get; }
|
||||||
|
|
||||||
public bool TransformFeedbackEnabled { get; }
|
public bool TransformFeedbackEnabled { get; }
|
||||||
|
|
||||||
private TransformFeedbackOutput[] _transformFeedbackOutputs;
|
private TransformFeedbackOutput[] _transformFeedbackOutputs;
|
||||||
|
@ -109,7 +113,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public int AccessibleStorageBuffersMask { get; private set; }
|
public int AccessibleStorageBuffersMask { get; private set; }
|
||||||
public int AccessibleConstantBuffersMask { get; private set; }
|
public int AccessibleConstantBuffersMask { get; private set; }
|
||||||
|
|
||||||
private int _usedConstantBuffers;
|
|
||||||
private int _usedStorageBuffers;
|
private int _usedStorageBuffers;
|
||||||
private int _usedStorageBuffersWrite;
|
private int _usedStorageBuffersWrite;
|
||||||
|
|
||||||
|
@ -128,20 +131,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
private readonly Dictionary<int, int> _sbSlots;
|
private readonly Dictionary<int, int> _sbSlots;
|
||||||
private readonly Dictionary<int, int> _sbSlotsReverse;
|
private readonly Dictionary<int, int> _sbSlotsReverse;
|
||||||
|
|
||||||
private BufferDescriptor[] _cachedConstantBufferDescriptors;
|
|
||||||
private BufferDescriptor[] _cachedStorageBufferDescriptors;
|
private BufferDescriptor[] _cachedStorageBufferDescriptors;
|
||||||
private TextureDescriptor[] _cachedTextureDescriptors;
|
private TextureDescriptor[] _cachedTextureDescriptors;
|
||||||
private TextureDescriptor[] _cachedImageDescriptors;
|
private TextureDescriptor[] _cachedImageDescriptors;
|
||||||
|
|
||||||
private int _firstConstantBufferBinding;
|
|
||||||
private int _firstStorageBufferBinding;
|
private int _firstStorageBufferBinding;
|
||||||
|
|
||||||
public int FirstConstantBufferBinding => _firstConstantBufferBinding;
|
|
||||||
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
|
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;
|
GpuAccessor = gpuAccessor;
|
||||||
Options = options;
|
Options = options;
|
||||||
|
|
||||||
|
@ -158,6 +158,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
_sbSlots = new Dictionary<int, int>();
|
_sbSlots = new Dictionary<int, int>();
|
||||||
_sbSlotsReverse = new Dictionary<int, int>();
|
_sbSlotsReverse = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
Properties = new ShaderProperties();
|
||||||
|
ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConfig(
|
public ShaderConfig(
|
||||||
|
@ -165,9 +168,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
OutputTopology outputTopology,
|
OutputTopology outputTopology,
|
||||||
int maxOutputVertices,
|
int maxOutputVertices,
|
||||||
IGpuAccessor gpuAccessor,
|
IGpuAccessor gpuAccessor,
|
||||||
TranslationOptions options) : this(gpuAccessor, options)
|
TranslationOptions options) : this(stage, gpuAccessor, options)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
|
||||||
ThreadsPerInputPrimitive = 1;
|
ThreadsPerInputPrimitive = 1;
|
||||||
OutputTopology = outputTopology;
|
OutputTopology = outputTopology;
|
||||||
MaxOutputVertices = maxOutputVertices;
|
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;
|
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||||
ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
|
ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
|
||||||
OutputTopology = header.OutputTopology;
|
OutputTopology = header.OutputTopology;
|
||||||
|
@ -428,12 +429,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public void InheritFrom(ShaderConfig other)
|
public void InheritFrom(ShaderConfig other)
|
||||||
{
|
{
|
||||||
|
ResourceManager.InheritFrom(other.ResourceManager);
|
||||||
|
|
||||||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||||
UsedFeatures |= other.UsedFeatures;
|
UsedFeatures |= other.UsedFeatures;
|
||||||
|
|
||||||
UsedInputAttributes |= other.UsedInputAttributes;
|
UsedInputAttributes |= other.UsedInputAttributes;
|
||||||
UsedOutputAttributes |= other.UsedOutputAttributes;
|
UsedOutputAttributes |= other.UsedOutputAttributes;
|
||||||
_usedConstantBuffers |= other._usedConstantBuffers;
|
|
||||||
_usedStorageBuffers |= other._usedStorageBuffers;
|
_usedStorageBuffers |= other._usedStorageBuffers;
|
||||||
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
|
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
|
||||||
|
|
||||||
|
@ -641,11 +643,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
AccessibleConstantBuffersMask = ubeMask;
|
AccessibleConstantBuffersMask = ubeMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUsedConstantBuffer(int slot)
|
|
||||||
{
|
|
||||||
_usedConstantBuffers |= 1 << slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetUsedStorageBuffer(int slot, bool write)
|
public void SetUsedStorageBuffer(int slot, bool write)
|
||||||
{
|
{
|
||||||
int mask = 1 << slot;
|
int mask = 1 << slot;
|
||||||
|
@ -762,27 +759,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return meta;
|
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()
|
public BufferDescriptor[] GetStorageBufferDescriptors()
|
||||||
{
|
{
|
||||||
if (_cachedStorageBufferDescriptors != null)
|
if (_cachedStorageBufferDescriptors != null)
|
||||||
|
@ -798,47 +774,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
GpuAccessor.QueryBindingStorageBuffer);
|
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(
|
private BufferDescriptor[] GetStorageBufferDescriptors(
|
||||||
int usedMask,
|
int usedMask,
|
||||||
int writtenMask,
|
int writtenMask,
|
||||||
|
@ -1009,7 +944,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
|
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
|
||||||
{
|
{
|
||||||
return new ShaderProgramInfo(
|
return new ShaderProgramInfo(
|
||||||
GetConstantBufferDescriptors(),
|
ResourceManager.GetConstantBufferDescriptors(),
|
||||||
GetStorageBufferDescriptors(),
|
GetStorageBufferDescriptors(),
|
||||||
GetTextureDescriptors(),
|
GetTextureDescriptors(),
|
||||||
GetImageDescriptors(),
|
GetImageDescriptors(),
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
if (options.Flags.HasFlag(TranslationFlags.Compute))
|
if (options.Flags.HasFlag(TranslationFlags.Compute))
|
||||||
{
|
{
|
||||||
config = new ShaderConfig(gpuAccessor, options);
|
config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options);
|
||||||
|
|
||||||
program = Decoder.Decode(config, address);
|
program = Decoder.Decode(config, address);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue