Support shared color mask, implement more shader instructions
Support shared color masks (used by Nouveau and maybe the NVIDIA driver). Support draw buffers (also required by OpenGL). Support viewport transform disable (disabled for now as it breaks some games). Fix instanced rendering draw being ignored for multi draw. Fix IADD and IADD3 immediate shader encodings, that was not matching some ops. Implement FFMA32I shader instruction. Implement IMAD shader instruction.
This commit is contained in:
parent
e25b7c9848
commit
cb171f6ebf
32 changed files with 313 additions and 92 deletions
|
@ -142,6 +142,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
context.AppendLine();
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighU32) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl");
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.Shuffle) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl");
|
||||
|
@ -170,6 +180,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
public static void DeclareLocals(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
context.AppendLine(GetVarTypeName(VariableType.S32) + " " + DefaultNames.DummyIntName + ";");
|
||||
context.AppendLine(GetVarTypeName(VariableType.U32) + " " + DefaultNames.DummyUintName + ";");
|
||||
|
||||
foreach (AstOperand decl in info.Locals)
|
||||
{
|
||||
string name = context.OperandManager.DeclareLocal(decl);
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
public const string LocalMemoryName = "local_mem";
|
||||
public const string SharedMemoryName = "shared_mem";
|
||||
|
||||
public const string DummyIntName = "dummyInt";
|
||||
public const string DummyUintName = "dummyUint";
|
||||
|
||||
public const string UndefinedName = "undef";
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
static class HelperFunctionNames
|
||||
{
|
||||
public static string MultiplyHighS32 = "Helper_MultiplyHighS32";
|
||||
public static string MultiplyHighU32 = "Helper_MultiplyHighU32";
|
||||
|
||||
public static string Shuffle = "Helper_Shuffle";
|
||||
public static string ShuffleDown = "Helper_ShuffleDown";
|
||||
public static string ShuffleUp = "Helper_ShuffleUp";
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
int Helper_MultiplyHighS32(int x, int y)
|
||||
{
|
||||
int msb;
|
||||
int lsb;
|
||||
imulExtended(x, y, msb, lsb);
|
||||
return msb;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
uint Helper_MultiplyHighU32(uint x, uint y)
|
||||
{
|
||||
uint msb;
|
||||
uint lsb;
|
||||
umulExtended(x, y, msb, lsb);
|
||||
return msb;
|
||||
}
|
|
@ -93,6 +93,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
Add(Instruction.Minimum, InstType.CallBinary, "min");
|
||||
Add(Instruction.MinimumU32, InstType.CallBinary, "min");
|
||||
Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1);
|
||||
Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32);
|
||||
Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32);
|
||||
Add(Instruction.Negate, InstType.OpUnary, "-", 0);
|
||||
Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "inversesqrt");
|
||||
Add(Instruction.Return, InstType.OpNullary, "return");
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||
|
|
|
@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
Set("0101110001011x", InstEmit.Fadd, typeof(OpCodeFArithReg));
|
||||
Set("010010011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithCbuf));
|
||||
Set("0011001x1xxxxx", InstEmit.Ffma, typeof(OpCodeFArithImm));
|
||||
Set("000011xxxxxxxx", InstEmit.Ffma32i, typeof(OpCodeFArithImm32));
|
||||
Set("010100011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithRegCbuf));
|
||||
Set("010110011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithReg));
|
||||
Set("0100110000110x", InstEmit.Flo, typeof(OpCodeAluCbuf));
|
||||
|
@ -102,12 +103,16 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
Set("0011100x11100x", InstEmit.I2I, typeof(OpCodeAluImm));
|
||||
Set("0101110011100x", InstEmit.I2I, typeof(OpCodeAluReg));
|
||||
Set("0100110000010x", InstEmit.Iadd, typeof(OpCodeAluCbuf));
|
||||
Set("0011100000010x", InstEmit.Iadd, typeof(OpCodeAluImm));
|
||||
Set("0011100x00010x", InstEmit.Iadd, typeof(OpCodeAluImm));
|
||||
Set("0001110x0xxxxx", InstEmit.Iadd, typeof(OpCodeAluImm32));
|
||||
Set("0101110000010x", InstEmit.Iadd, typeof(OpCodeAluReg));
|
||||
Set("010011001100xx", InstEmit.Iadd3, typeof(OpCodeAluCbuf));
|
||||
Set("001110001100xx", InstEmit.Iadd3, typeof(OpCodeAluImm));
|
||||
Set("0011100x1100xx", InstEmit.Iadd3, typeof(OpCodeAluImm));
|
||||
Set("010111001100xx", InstEmit.Iadd3, typeof(OpCodeAluReg));
|
||||
Set("010010100xxxxx", InstEmit.Imad, typeof(OpCodeAluCbuf));
|
||||
Set("0011010x0xxxxx", InstEmit.Imad, typeof(OpCodeAluImm));
|
||||
Set("010110100xxxxx", InstEmit.Imad, typeof(OpCodeAluReg));
|
||||
Set("010100100xxxxx", InstEmit.Imad, typeof(OpCodeAluRegCbuf));
|
||||
Set("0100110000100x", InstEmit.Imnmx, typeof(OpCodeAluCbuf));
|
||||
Set("0011100x00100x", InstEmit.Imnmx, typeof(OpCodeAluImm));
|
||||
Set("0101110000100x", InstEmit.Imnmx, typeof(OpCodeAluReg));
|
||||
|
|
|
@ -200,6 +200,50 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
// TODO: CC, X, corner cases
|
||||
}
|
||||
|
||||
public static void Imad(EmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
bool signedA = context.CurrOp.RawOpCode.Extract(48);
|
||||
bool signedB = context.CurrOp.RawOpCode.Extract(53);
|
||||
bool high = context.CurrOp.RawOpCode.Extract(54);
|
||||
|
||||
Operand srcA = GetSrcA(context);
|
||||
Operand srcB = GetSrcB(context);
|
||||
Operand srcC = GetSrcC(context);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (high)
|
||||
{
|
||||
if (signedA && signedB)
|
||||
{
|
||||
res = context.MultiplyHighS32(srcA, srcB);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.MultiplyHighU32(srcA, srcB);
|
||||
|
||||
if (signedA)
|
||||
{
|
||||
res = context.IAdd(res, context.IMultiply(srcB, context.ShiftRightS32(srcA, Const(31))));
|
||||
}
|
||||
else if (signedB)
|
||||
{
|
||||
res = context.IAdd(res, context.IMultiply(srcA, context.ShiftRightS32(srcB, Const(31))));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.IMultiply(srcA, srcB);
|
||||
}
|
||||
|
||||
res = context.IAdd(res, srcC);
|
||||
|
||||
context.Copy(GetDest(context), res);
|
||||
}
|
||||
|
||||
public static void Imnmx(EmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
|
|
@ -59,6 +59,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
SetFPZnFlags(context, dest, op.SetCondCode);
|
||||
}
|
||||
|
||||
public static void Ffma32i(EmitterContext context)
|
||||
{
|
||||
IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
|
||||
|
||||
bool saturate = op.RawOpCode.Extract(55);
|
||||
bool negateA = op.RawOpCode.Extract(56);
|
||||
bool negateC = op.RawOpCode.Extract(57);
|
||||
|
||||
Operand srcA = context.FPNegate(GetSrcA(context), negateA);
|
||||
Operand srcC = context.FPNegate(GetDest(context), negateC);
|
||||
|
||||
Operand srcB = GetSrcB(context);
|
||||
|
||||
Operand dest = GetDest(context);
|
||||
|
||||
context.Copy(dest, context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC), saturate));
|
||||
|
||||
SetFPZnFlags(context, dest, op.SetCondCode);
|
||||
}
|
||||
|
||||
public static void Fmnmx(EmitterContext context)
|
||||
{
|
||||
IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
|
||||
|
|
|
@ -84,6 +84,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
Minimum,
|
||||
MinimumU32,
|
||||
Multiply,
|
||||
MultiplyHighS32,
|
||||
MultiplyHighU32,
|
||||
Negate,
|
||||
PackDouble2x32,
|
||||
PackHalf2x16,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
||||
|
|
|
@ -2,14 +2,18 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
public struct ShaderCapabilities
|
||||
{
|
||||
private static readonly ShaderCapabilities _default = new ShaderCapabilities(16);
|
||||
private static readonly ShaderCapabilities _default = new ShaderCapabilities(32768, 16);
|
||||
|
||||
public static ShaderCapabilities Default => _default;
|
||||
|
||||
public int MaximumViewportDimensions { get; }
|
||||
public int StorageBufferOffsetAlignment { get; }
|
||||
|
||||
public ShaderCapabilities(int storageBufferOffsetAlignment)
|
||||
public ShaderCapabilities(
|
||||
int maximumViewportDimensions,
|
||||
int storageBufferOffsetAlignment)
|
||||
{
|
||||
MaximumViewportDimensions = maximumViewportDimensions;
|
||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
[Flags]
|
||||
enum HelperFunctionsMask
|
||||
{
|
||||
Shuffle = 1 << 0,
|
||||
ShuffleDown = 1 << 1,
|
||||
ShuffleUp = 1 << 2,
|
||||
ShuffleXor = 1 << 3,
|
||||
SwizzleAdd = 1 << 4
|
||||
MultiplyHighS32 = 1 << 0,
|
||||
MultiplyHighU32 = 1 << 1,
|
||||
Shuffle = 1 << 2,
|
||||
ShuffleDown = 1 << 3,
|
||||
ShuffleUp = 1 << 4,
|
||||
ShuffleXor = 1 << 5,
|
||||
SwizzleAdd = 1 << 6
|
||||
}
|
||||
}
|
|
@ -102,6 +102,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
Add(Instruction.Minimum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
|
||||
Add(Instruction.MinimumU32, VariableType.U32, VariableType.U32, VariableType.U32);
|
||||
Add(Instruction.Multiply, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
|
||||
Add(Instruction.MultiplyHighS32, VariableType.S32, VariableType.S32, VariableType.S32);
|
||||
Add(Instruction.MultiplyHighU32, VariableType.U32, VariableType.U32, VariableType.U32);
|
||||
Add(Instruction.Negate, VariableType.Scalar, VariableType.Scalar);
|
||||
Add(Instruction.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32);
|
||||
Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar);
|
||||
|
|
|
@ -171,6 +171,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
// decide which helper functions are needed on the final generated code.
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.MultiplyHighS32:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
|
||||
break;
|
||||
case Instruction.MultiplyHighU32:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32;
|
||||
break;
|
||||
case Instruction.Shuffle:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.Shuffle;
|
||||
break;
|
||||
|
|
|
@ -11,18 +11,25 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public Block CurrBlock { get; set; }
|
||||
public OpCode CurrOp { get; set; }
|
||||
|
||||
private ShaderStage _stage;
|
||||
|
||||
private ShaderHeader _header;
|
||||
private ShaderStage _stage;
|
||||
private ShaderHeader _header;
|
||||
private ShaderCapabilities _capabilities;
|
||||
private TranslationFlags _flags;
|
||||
|
||||
private List<Operation> _operations;
|
||||
|
||||
private Dictionary<ulong, Operand> _labels;
|
||||
|
||||
public EmitterContext(ShaderStage stage, ShaderHeader header)
|
||||
public EmitterContext(
|
||||
ShaderStage stage,
|
||||
ShaderHeader header,
|
||||
ShaderCapabilities capabilities,
|
||||
TranslationFlags flags)
|
||||
{
|
||||
_stage = stage;
|
||||
_header = header;
|
||||
_stage = stage;
|
||||
_header = header;
|
||||
_capabilities = capabilities;
|
||||
_flags = flags;
|
||||
|
||||
_operations = new List<Operation>();
|
||||
|
||||
|
@ -62,7 +69,18 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public void PrepareForReturn()
|
||||
{
|
||||
if (_stage == ShaderStage.Fragment)
|
||||
if (_stage == ShaderStage.Vertex)
|
||||
{
|
||||
if ((_flags & TranslationFlags.DividePosXY) != 0)
|
||||
{
|
||||
Operand posX = Attribute(AttributeConsts.PositionX);
|
||||
Operand posY = Attribute(AttributeConsts.PositionY);
|
||||
|
||||
this.Copy(posX, this.FPDivide(posX, ConstF(_capabilities.MaximumViewportDimensions / 2)));
|
||||
this.Copy(posY, this.FPDivide(posY, ConstF(_capabilities.MaximumViewportDimensions / 2)));
|
||||
}
|
||||
}
|
||||
else if (_stage == ShaderStage.Fragment)
|
||||
{
|
||||
if (_header.OmapDepth)
|
||||
{
|
||||
|
|
|
@ -476,6 +476,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return context.Add(Instruction.LoadShared, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand MultiplyHighS32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.MultiplyHighS32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand MultiplyHighU32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.MultiplyHighU32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.PackHalf2x16, Local(), a, b);
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
Compute = 1 << 0,
|
||||
DebugMode = 1 << 1,
|
||||
Unspecialized = 1 << 2
|
||||
Unspecialized = 1 << 2,
|
||||
DividePosXY = 1 << 3
|
||||
}
|
||||
}
|
|
@ -49,15 +49,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public static ShaderProgram Translate(Span<byte> code, ShaderCapabilities capabilities, TranslationFlags flags)
|
||||
{
|
||||
bool compute = (flags & TranslationFlags.Compute) != 0;
|
||||
bool debugMode = (flags & TranslationFlags.DebugMode) != 0;
|
||||
bool compute = (flags & TranslationFlags.Compute) != 0;
|
||||
|
||||
Operation[] ops = DecodeShader(
|
||||
code,
|
||||
compute,
|
||||
debugMode,
|
||||
out ShaderHeader header,
|
||||
out int size);
|
||||
Operation[] ops = DecodeShader(code, capabilities, flags, out ShaderHeader header, out int size);
|
||||
|
||||
ShaderStage stage;
|
||||
|
||||
|
@ -94,8 +88,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
bool debugMode = (flags & TranslationFlags.DebugMode) != 0;
|
||||
|
||||
Operation[] vpAOps = DecodeShader(vpACode, compute: false, debugMode, out _, out _);
|
||||
Operation[] vpBOps = DecodeShader(vpBCode, compute: false, debugMode, out ShaderHeader header, out int sizeB);
|
||||
Operation[] vpAOps = DecodeShader(vpACode, capabilities, flags, out _, out _);
|
||||
Operation[] vpBOps = DecodeShader(vpBCode, capabilities, flags, out ShaderHeader header, out int sizeB);
|
||||
|
||||
ShaderConfig config = new ShaderConfig(
|
||||
header.Stage,
|
||||
|
@ -142,23 +136,23 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
|
||||
private static Operation[] DecodeShader(
|
||||
Span<byte> code,
|
||||
bool compute,
|
||||
bool debugMode,
|
||||
out ShaderHeader header,
|
||||
out int size)
|
||||
Span<byte> code,
|
||||
ShaderCapabilities capabilities,
|
||||
TranslationFlags flags,
|
||||
out ShaderHeader header,
|
||||
out int size)
|
||||
{
|
||||
Block[] cfg;
|
||||
|
||||
EmitterContext context;
|
||||
|
||||
if (compute)
|
||||
if ((flags & TranslationFlags.Compute) != 0)
|
||||
{
|
||||
header = null;
|
||||
|
||||
cfg = Decoder.Decode(code, 0);
|
||||
|
||||
context = new EmitterContext(ShaderStage.Compute, header);
|
||||
context = new EmitterContext(ShaderStage.Compute, header, capabilities, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
cfg = Decoder.Decode(code, HeaderSize);
|
||||
|
||||
context = new EmitterContext(header.Stage, header);
|
||||
context = new EmitterContext(header.Stage, header, capabilities, flags);
|
||||
}
|
||||
|
||||
if (cfg == null)
|
||||
|
@ -197,7 +191,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
OpCode op = block.OpCodes[opIndex];
|
||||
|
||||
if (debugMode)
|
||||
if ((flags & TranslationFlags.DebugMode) != 0)
|
||||
{
|
||||
string instName;
|
||||
|
||||
|
@ -274,7 +268,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
size = (int)maxEndAddress + (compute ? 0 : HeaderSize);
|
||||
size = (int)maxEndAddress + (((flags & TranslationFlags.Compute) != 0) ? 0 : HeaderSize);
|
||||
|
||||
return context.GetOperations();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue