Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
1668ba913f | |||
a830eb666b | |||
cfc75d7e78 | |||
c525d7d9a9 | |||
1a0a351a15 | |||
bd3335c143 | |||
a94445b23e | |||
0c3421973c | |||
0afa8f2c14 | |||
d25a084858 | |||
311ca3c3f1 | |||
3193ef1083 |
@ -20,7 +20,7 @@
|
|||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
|
@ -251,7 +251,20 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int selectedReg = GetHighestValueIndex(freePositions);
|
// If this is a copy destination variable, we prefer the register used for the copy source.
|
||||||
|
// If the register is available, then the copy can be eliminated later as both source
|
||||||
|
// and destination will use the same register.
|
||||||
|
int selectedReg;
|
||||||
|
|
||||||
|
if (current.TryGetCopySourceRegister(out int preferredReg) && freePositions[preferredReg] >= current.GetEnd())
|
||||||
|
{
|
||||||
|
selectedReg = preferredReg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedReg = GetHighestValueIndex(freePositions);
|
||||||
|
}
|
||||||
|
|
||||||
int selectedNextUse = freePositions[selectedReg];
|
int selectedNextUse = freePositions[selectedReg];
|
||||||
|
|
||||||
// Intervals starts and ends at odd positions, unless they span an entire
|
// Intervals starts and ends at odd positions, unless they span an entire
|
||||||
@ -431,7 +444,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetHighestValueIndex(Span<int> span)
|
private static int GetHighestValueIndex(ReadOnlySpan<int> span)
|
||||||
{
|
{
|
||||||
int highest = int.MinValue;
|
int highest = int.MinValue;
|
||||||
|
|
||||||
@ -798,12 +811,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
// The "visited" state is stored in the MSB of the local's value.
|
// The "visited" state is stored in the MSB of the local's value.
|
||||||
const ulong VisitedMask = 1ul << 63;
|
const ulong VisitedMask = 1ul << 63;
|
||||||
|
|
||||||
bool IsVisited(Operand local)
|
static bool IsVisited(Operand local)
|
||||||
{
|
{
|
||||||
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVisited(Operand local)
|
static void SetVisited(Operand local)
|
||||||
{
|
{
|
||||||
local.GetValueUnsafe() |= VisitedMask;
|
local.GetValueUnsafe() |= VisitedMask;
|
||||||
}
|
}
|
||||||
@ -826,9 +839,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
dest.NumberLocal(_intervals.Count);
|
dest.NumberLocal(_intervals.Count);
|
||||||
|
|
||||||
_intervals.Add(new LiveInterval(dest));
|
LiveInterval interval = new LiveInterval(dest);
|
||||||
|
_intervals.Add(interval);
|
||||||
|
|
||||||
SetVisited(dest);
|
SetVisited(dest);
|
||||||
|
|
||||||
|
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
||||||
|
// This is used for register preferencing later on, which allows the copy to be eliminated
|
||||||
|
// in some cases.
|
||||||
|
if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
||||||
|
{
|
||||||
|
Operand source = node.GetSource(0);
|
||||||
|
|
||||||
|
if (source.Kind == OperandKind.LocalVariable &&
|
||||||
|
source.GetLocalNumber() > 0 &&
|
||||||
|
(node.Instruction == Instruction.Copy || source.Type == OperandType.I32))
|
||||||
|
{
|
||||||
|
interval.SetCopySource(_intervals[source.GetLocalNumber()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
public LiveRange CurrRange;
|
public LiveRange CurrRange;
|
||||||
|
|
||||||
public LiveInterval Parent;
|
public LiveInterval Parent;
|
||||||
|
public LiveInterval CopySource;
|
||||||
|
|
||||||
public UseList Uses;
|
public UseList Uses;
|
||||||
public LiveIntervalList Children;
|
public LiveIntervalList Children;
|
||||||
@ -37,6 +38,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
private ref LiveRange CurrRange => ref _data->CurrRange;
|
private ref LiveRange CurrRange => ref _data->CurrRange;
|
||||||
private ref LiveRange PrevRange => ref _data->PrevRange;
|
private ref LiveRange PrevRange => ref _data->PrevRange;
|
||||||
private ref LiveInterval Parent => ref _data->Parent;
|
private ref LiveInterval Parent => ref _data->Parent;
|
||||||
|
private ref LiveInterval CopySource => ref _data->CopySource;
|
||||||
private ref UseList Uses => ref _data->Uses;
|
private ref UseList Uses => ref _data->Uses;
|
||||||
private ref LiveIntervalList Children => ref _data->Children;
|
private ref LiveIntervalList Children => ref _data->Children;
|
||||||
|
|
||||||
@ -78,6 +80,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
Register = register;
|
Register = register;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetCopySource(LiveInterval copySource)
|
||||||
|
{
|
||||||
|
CopySource = copySource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetCopySourceRegister(out int copySourceRegIndex)
|
||||||
|
{
|
||||||
|
if (CopySource._data != null)
|
||||||
|
{
|
||||||
|
copySourceRegIndex = CopySource.Register.Index;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
copySourceRegIndex = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
PrevRange = default;
|
PrevRange = default;
|
||||||
|
@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
|
|||||||
private int[] _postOrderMap;
|
private int[] _postOrderMap;
|
||||||
|
|
||||||
public int LocalsCount { get; private set; }
|
public int LocalsCount { get; private set; }
|
||||||
public BasicBlock Entry { get; }
|
public BasicBlock Entry { get; private set; }
|
||||||
public IntrusiveList<BasicBlock> Blocks { get; }
|
public IntrusiveList<BasicBlock> Blocks { get; }
|
||||||
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
||||||
public int[] PostOrderMap => _postOrderMap;
|
public int[] PostOrderMap => _postOrderMap;
|
||||||
@ -34,6 +34,15 @@ namespace ARMeilleure.Translation
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateEntry(BasicBlock newEntry)
|
||||||
|
{
|
||||||
|
newEntry.AddSuccessor(Entry);
|
||||||
|
|
||||||
|
Entry = newEntry;
|
||||||
|
Blocks.AddFirst(newEntry);
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
RemoveUnreachableBlocks(Blocks);
|
RemoveUnreachableBlocks(Blocks);
|
||||||
|
@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 6634; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -89,6 +89,17 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
||||||
{
|
{
|
||||||
|
if (cfg.Entry.Predecessors.Count != 0)
|
||||||
|
{
|
||||||
|
// We expect the entry block to have no predecessors.
|
||||||
|
// This is required because we have a implicit context load at the start of the function,
|
||||||
|
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
||||||
|
// Here we insert a new entry block that will jump to the existing entry block.
|
||||||
|
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
|
||||||
|
|
||||||
|
cfg.UpdateEntry(newEntry);
|
||||||
|
}
|
||||||
|
|
||||||
// Compute local register inputs and outputs used inside blocks.
|
// Compute local register inputs and outputs used inside blocks.
|
||||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
||||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||||
@ -201,7 +212,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
// The only block without any predecessor should be the entry block.
|
// The only block without any predecessor should be the entry block.
|
||||||
// It always needs a context load as it is the first block to run.
|
// It always needs a context load as it is the first block to run.
|
||||||
if (block.Predecessors.Count == 0 || hasContextLoad)
|
if (block == cfg.Entry || hasContextLoad)
|
||||||
{
|
{
|
||||||
long vecMask = globalInputs[block.Index].VecMask;
|
long vecMask = globalInputs[block.Index].VecMask;
|
||||||
long intMask = globalInputs[block.Index].IntMask;
|
long intMask = globalInputs[block.Index].IntMask;
|
||||||
|
@ -124,7 +124,7 @@ namespace Ryujinx.Common.Memory
|
|||||||
|
|
||||||
if (array is not null)
|
if (array is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<T>.Shared.Return(array);
|
ArrayPool<T>.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ namespace Ryujinx.Common.Memory
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
ArrayPool<T>.Shared.Return(_array);
|
ArrayPool<T>.Shared.Return(_array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 = 6852;
|
private const uint CodeGenVersion = 6921;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (op.BVal)
|
if (op.BVal)
|
||||||
{
|
{
|
||||||
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
|
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), ConstF(0)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -156,6 +156,26 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsComparison(this Instruction inst)
|
||||||
|
{
|
||||||
|
switch (inst & Instruction.Mask)
|
||||||
|
{
|
||||||
|
case Instruction.CompareEqual:
|
||||||
|
case Instruction.CompareGreater:
|
||||||
|
case Instruction.CompareGreaterOrEqual:
|
||||||
|
case Instruction.CompareGreaterOrEqualU32:
|
||||||
|
case Instruction.CompareGreaterU32:
|
||||||
|
case Instruction.CompareLess:
|
||||||
|
case Instruction.CompareLessOrEqual:
|
||||||
|
case Instruction.CompareLessOrEqualU32:
|
||||||
|
case Instruction.CompareLessU32:
|
||||||
|
case Instruction.CompareNotEqual:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsTextureQuery(this Instruction inst)
|
public static bool IsTextureQuery(this Instruction inst)
|
||||||
{
|
{
|
||||||
inst &= Instruction.Mask;
|
inst &= Instruction.Mask;
|
||||||
|
@ -141,16 +141,16 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsBindlessAccessAllowed(Operand nvHandle)
|
private static bool IsBindlessAccessAllowed(Operand bindlessHandle)
|
||||||
{
|
{
|
||||||
if (nvHandle.Type == OperandType.ConstantBuffer)
|
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
// Bindless access with handles from constant buffer is allowed.
|
// Bindless access with handles from constant buffer is allowed.
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nvHandle.AsgOp is not Operation handleOp ||
|
if (bindlessHandle.AsgOp is not Operation handleOp ||
|
||||||
handleOp.Inst != Instruction.Load ||
|
handleOp.Inst != Instruction.Load ||
|
||||||
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
|
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
|
||||||
{
|
{
|
||||||
@ -300,7 +300,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
resourceManager,
|
resourceManager,
|
||||||
gpuAccessor,
|
gpuAccessor,
|
||||||
texOp,
|
texOp,
|
||||||
TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType),
|
TextureHandle.PackOffsets(src0.GetCbufOffset(), (src1.Value >> 20) & 0xfff, handleType),
|
||||||
TextureHandle.PackSlots(src0.GetCbufSlot(), 0),
|
TextureHandle.PackSlots(src0.GetCbufSlot(), 0),
|
||||||
rewriteSamplerType,
|
rewriteSamplerType,
|
||||||
isImage: false);
|
isImage: false);
|
||||||
|
@ -126,7 +126,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp)
|
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
||||||
|
|
||||||
|
if (bindlessHandle.AsgOp is not Operation handleAsgOp)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -137,8 +139,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
|
|
||||||
if (handleAsgOp.Inst == Instruction.BitwiseOr)
|
if (handleAsgOp.Inst == Instruction.BitwiseOr)
|
||||||
{
|
{
|
||||||
Operand src0 = handleAsgOp.GetSource(0);
|
Operand src0 = Utils.FindLastOperation(handleAsgOp.GetSource(0), block);
|
||||||
Operand src1 = handleAsgOp.GetSource(1);
|
Operand src1 = Utils.FindLastOperation(handleAsgOp.GetSource(1), block);
|
||||||
|
|
||||||
if (src0.Type == OperandType.ConstantBuffer && src1.AsgOp is Operation)
|
if (src0.Type == OperandType.ConstantBuffer && src1.AsgOp is Operation)
|
||||||
{
|
{
|
||||||
|
@ -152,18 +152,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
{
|
{
|
||||||
// If all phi sources are the same, we can propagate it and remove the phi.
|
// If all phi sources are the same, we can propagate it and remove the phi.
|
||||||
|
|
||||||
Operand firstSrc = phi.GetSource(0);
|
if (!Utils.AreAllSourcesTheSameOperand(phi))
|
||||||
|
|
||||||
for (int index = 1; index < phi.SourcesCount; index++)
|
|
||||||
{
|
{
|
||||||
if (!IsSameOperand(firstSrc, phi.GetSource(index)))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All sources are equal, we can propagate the value.
|
// All sources are equal, we can propagate the value.
|
||||||
|
|
||||||
|
Operand firstSrc = phi.GetSource(0);
|
||||||
Operand dest = phi.Dest;
|
Operand dest = phi.Dest;
|
||||||
|
|
||||||
INode[] uses = dest.UseOps.ToArray();
|
INode[] uses = dest.UseOps.ToArray();
|
||||||
@ -182,17 +178,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsSameOperand(Operand x, Operand y)
|
|
||||||
{
|
|
||||||
if (x.Type != y.Type || x.Value != y.Value)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Handle Load operations with the same storage and the same constant parameters.
|
|
||||||
return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool PropagatePack(Operation packOp)
|
private static bool PropagatePack(Operation packOp)
|
||||||
{
|
{
|
||||||
// Propagate pack source operands to uses by unpack
|
// Propagate pack source operands to uses by unpack
|
||||||
|
@ -31,6 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
TryEliminateBitwiseOr(operation);
|
TryEliminateBitwiseOr(operation);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Instruction.CompareNotEqual:
|
||||||
|
TryEliminateCompareNotEqual(operation);
|
||||||
|
break;
|
||||||
|
|
||||||
case Instruction.ConditionalSelect:
|
case Instruction.ConditionalSelect:
|
||||||
TryEliminateConditionalSelect(operation);
|
TryEliminateConditionalSelect(operation);
|
||||||
break;
|
break;
|
||||||
@ -174,6 +178,32 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void TryEliminateCompareNotEqual(Operation operation)
|
||||||
|
{
|
||||||
|
// Comparison instruction returns 0 if the result is false, and -1 if true.
|
||||||
|
// Doing a not equal zero comparison on the result is redundant, so we can just copy the first result in this case.
|
||||||
|
|
||||||
|
Operand lhs = operation.GetSource(0);
|
||||||
|
Operand rhs = operation.GetSource(1);
|
||||||
|
|
||||||
|
if (lhs.Type == OperandType.Constant)
|
||||||
|
{
|
||||||
|
(lhs, rhs) = (rhs, lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs.Type != OperandType.Constant || rhs.Value != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs.AsgOp is not Operation compareOp || !compareOp.Inst.IsComparison())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.TurnIntoCopy(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
private static void TryEliminateConditionalSelect(Operation operation)
|
private static void TryEliminateConditionalSelect(Operation operation)
|
||||||
{
|
{
|
||||||
Operand cond = operation.GetSource(0);
|
Operand cond = operation.GetSource(0);
|
||||||
|
@ -34,6 +34,50 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
|
return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsSameOperand(Operand x, Operand y)
|
||||||
|
{
|
||||||
|
if (x.Type != y.Type || x.Value != y.Value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle Load operations with the same storage and the same constant parameters.
|
||||||
|
return x == y || x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AreAllSourcesEqual(INode node, INode otherNode)
|
||||||
|
{
|
||||||
|
if (node.SourcesCount != otherNode.SourcesCount)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < node.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
if (!IsSameOperand(node.GetSource(index), otherNode.GetSource(index)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AreAllSourcesTheSameOperand(INode node)
|
||||||
|
{
|
||||||
|
Operand firstSrc = node.GetSource(0);
|
||||||
|
|
||||||
|
for (int index = 1; index < node.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
if (!IsSameOperand(firstSrc, node.GetSource(index)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static Operation FindBranchSource(BasicBlock block)
|
private static Operation FindBranchSource(BasicBlock block)
|
||||||
{
|
{
|
||||||
foreach (BasicBlock sourceBlock in block.Predecessors)
|
foreach (BasicBlock sourceBlock in block.Predecessors)
|
||||||
@ -55,6 +99,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return inst == Instruction.BranchIfFalse || inst == Instruction.BranchIfTrue;
|
return inst == Instruction.BranchIfFalse || inst == Instruction.BranchIfTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsSameCondition(Operand currentCondition, Operand queryCondition)
|
||||||
|
{
|
||||||
|
if (currentCondition == queryCondition)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentCondition.AsgOp is Operation currentOperation &&
|
||||||
|
queryCondition.AsgOp is Operation queryOperation &&
|
||||||
|
currentOperation.Inst == queryOperation.Inst &&
|
||||||
|
AreAllSourcesEqual(currentOperation, queryOperation);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool BlockConditionsMatch(BasicBlock currentBlock, BasicBlock queryBlock)
|
private static bool BlockConditionsMatch(BasicBlock currentBlock, BasicBlock queryBlock)
|
||||||
{
|
{
|
||||||
// Check if all the conditions for the query block are satisfied by the current block.
|
// Check if all the conditions for the query block are satisfied by the current block.
|
||||||
@ -70,10 +127,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
|
|
||||||
return currentBranch != null && queryBranch != null &&
|
return currentBranch != null && queryBranch != null &&
|
||||||
currentBranch.Inst == queryBranch.Inst &&
|
currentBranch.Inst == queryBranch.Inst &&
|
||||||
currentCondition == queryCondition;
|
IsSameCondition(currentCondition, queryCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand FindLastOperation(Operand source, BasicBlock block)
|
public static Operand FindLastOperation(Operand source, BasicBlock block, bool recurse = true)
|
||||||
{
|
{
|
||||||
if (source.AsgOp is PhiNode phiNode)
|
if (source.AsgOp is PhiNode phiNode)
|
||||||
{
|
{
|
||||||
@ -84,10 +141,23 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
BasicBlock phiBlock = phiNode.GetBlock(i);
|
BasicBlock phiBlock = phiNode.GetBlock(i);
|
||||||
|
Operand phiSource = phiNode.GetSource(i);
|
||||||
|
|
||||||
if (BlockConditionsMatch(block, phiBlock))
|
if (BlockConditionsMatch(block, phiBlock))
|
||||||
{
|
{
|
||||||
return phiNode.GetSource(i);
|
return phiSource;
|
||||||
|
}
|
||||||
|
else if (recurse && phiSource.AsgOp is PhiNode)
|
||||||
|
{
|
||||||
|
// Phi source is another phi.
|
||||||
|
// Let's check if that phi has a block that matches our condition.
|
||||||
|
|
||||||
|
Operand match = FindLastOperation(phiSource, block, false);
|
||||||
|
|
||||||
|
if (match != phiSource)
|
||||||
|
{
|
||||||
|
return match;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
lock (queueLock)
|
lock (queueLock)
|
||||||
{
|
{
|
||||||
_pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, isLight: true);
|
_pool = new CommandBufferPool(
|
||||||
|
_gd.Api,
|
||||||
|
_device,
|
||||||
|
queue,
|
||||||
|
queueLock,
|
||||||
|
_gd.QueueFamilyIndex,
|
||||||
|
_gd.IsConcurrentFenceWaitUnsupported,
|
||||||
|
isLight: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,12 +103,19 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
usage |= BufferUsageFlags.IndirectBufferBit;
|
usage |= BufferUsageFlags.IndirectBufferBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var externalMemoryBuffer = new ExternalMemoryBufferCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.ExternalMemoryBufferCreateInfo,
|
||||||
|
HandleTypes = ExternalMemoryHandleTypeFlags.HostAllocationBitExt,
|
||||||
|
};
|
||||||
|
|
||||||
var bufferCreateInfo = new BufferCreateInfo
|
var bufferCreateInfo = new BufferCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.BufferCreateInfo,
|
SType = StructureType.BufferCreateInfo,
|
||||||
Size = (ulong)size,
|
Size = (ulong)size,
|
||||||
Usage = usage,
|
Usage = usage,
|
||||||
SharingMode = SharingMode.Exclusive,
|
SharingMode = SharingMode.Exclusive,
|
||||||
|
PNext = &externalMemoryBuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
||||||
|
@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly Queue _queue;
|
private readonly Queue _queue;
|
||||||
private readonly object _queueLock;
|
private readonly object _queueLock;
|
||||||
|
private readonly bool _concurrentFenceWaitUnsupported;
|
||||||
private readonly CommandPool _pool;
|
private readonly CommandPool _pool;
|
||||||
private readonly Thread _owner;
|
private readonly Thread _owner;
|
||||||
|
|
||||||
@ -61,12 +62,20 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private int _queuedCount;
|
private int _queuedCount;
|
||||||
private int _inUseCount;
|
private int _inUseCount;
|
||||||
|
|
||||||
public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false)
|
public unsafe CommandBufferPool(
|
||||||
|
Vk api,
|
||||||
|
Device device,
|
||||||
|
Queue queue,
|
||||||
|
object queueLock,
|
||||||
|
uint queueFamilyIndex,
|
||||||
|
bool concurrentFenceWaitUnsupported,
|
||||||
|
bool isLight = false)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
_queueLock = queueLock;
|
_queueLock = queueLock;
|
||||||
|
_concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported;
|
||||||
_owner = Thread.CurrentThread;
|
_owner = Thread.CurrentThread;
|
||||||
|
|
||||||
var commandPoolCreateInfo = new CommandPoolCreateInfo
|
var commandPoolCreateInfo = new CommandPoolCreateInfo
|
||||||
@ -357,7 +366,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (refreshFence)
|
if (refreshFence)
|
||||||
{
|
{
|
||||||
entry.Fence = new FenceHolder(_api, _device);
|
entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly PipelineBase _pipeline;
|
|
||||||
private ShaderCollection _program;
|
private ShaderCollection _program;
|
||||||
|
|
||||||
private readonly BufferRef[] _uniformBufferRefs;
|
private readonly BufferRef[] _uniformBufferRefs;
|
||||||
@ -125,11 +124,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly TextureView _dummyTexture;
|
private readonly TextureView _dummyTexture;
|
||||||
private readonly SamplerHolder _dummySampler;
|
private readonly SamplerHolder _dummySampler;
|
||||||
|
|
||||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device, PipelineBase pipeline)
|
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
_pipeline = pipeline;
|
|
||||||
|
|
||||||
// Some of the bindings counts needs to be multiplied by 2 because we have buffer and
|
// Some of the bindings counts needs to be multiplied by 2 because we have buffer and
|
||||||
// regular textures/images interleaved on the same descriptor set.
|
// regular textures/images interleaved on the same descriptor set.
|
||||||
@ -684,7 +682,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (_dirty.HasFlag(DirtyFlags.Texture))
|
if (_dirty.HasFlag(DirtyFlags.Texture))
|
||||||
{
|
{
|
||||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
if (program.UpdateTexturesWithoutTemplate)
|
||||||
|
{
|
||||||
|
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_dirty.HasFlag(DirtyFlags.Image))
|
if (_dirty.HasFlag(DirtyFlags.Image))
|
||||||
@ -918,31 +923,84 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void UpdateBuffers(
|
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
|
||||||
CommandBufferScoped cbs,
|
|
||||||
PipelineBindPoint pbp,
|
|
||||||
int baseBinding,
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> bufferInfo,
|
|
||||||
DescriptorType type)
|
|
||||||
{
|
{
|
||||||
if (bufferInfo.Length == 0)
|
int setIndex = PipelineBase.TextureSetIndex;
|
||||||
|
var bindingSegments = program.BindingSegments[setIndex];
|
||||||
|
|
||||||
|
if (bindingSegments.Length == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
|
if (_updateDescriptorCacheCbIndex)
|
||||||
{
|
{
|
||||||
var writeDescriptorSet = new WriteDescriptorSet
|
_updateDescriptorCacheCbIndex = false;
|
||||||
{
|
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
|
||||||
SType = StructureType.WriteDescriptorSet,
|
|
||||||
DstBinding = (uint)baseBinding,
|
|
||||||
DescriptorType = type,
|
|
||||||
DescriptorCount = (uint)bufferInfo.Length,
|
|
||||||
PBufferInfo = pBufferInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
_gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
|
||||||
|
|
||||||
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
|
{
|
||||||
|
int binding = segment.Binding;
|
||||||
|
int count = segment.Count;
|
||||||
|
|
||||||
|
if (!segment.IsArray)
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferTexture)
|
||||||
|
{
|
||||||
|
Span<DescriptorImageInfo> textures = _textures;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
ref var texture = ref textures[i];
|
||||||
|
ref var refs = ref _textureRefs[binding + i];
|
||||||
|
|
||||||
|
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
||||||
|
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
|
if (texture.ImageView.Handle == 0)
|
||||||
|
{
|
||||||
|
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture.Sampler.Handle == 0)
|
||||||
|
{
|
||||||
|
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span<BufferView> bufferTextures = _bufferTextures;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferTexture)
|
||||||
|
{
|
||||||
|
dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sets = dsc.GetSets();
|
||||||
|
|
||||||
|
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
@ -10,12 +10,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private Fence _fence;
|
private Fence _fence;
|
||||||
private int _referenceCount;
|
private int _referenceCount;
|
||||||
|
private int _lock;
|
||||||
|
private readonly bool _concurrentWaitUnsupported;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public unsafe FenceHolder(Vk api, Device device)
|
public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
|
_concurrentWaitUnsupported = concurrentWaitUnsupported;
|
||||||
|
|
||||||
var fenceCreateInfo = new FenceCreateInfo
|
var fenceCreateInfo = new FenceCreateInfo
|
||||||
{
|
{
|
||||||
@ -47,6 +50,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
|
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
|
||||||
|
|
||||||
|
if (_concurrentWaitUnsupported)
|
||||||
|
{
|
||||||
|
AcquireLock();
|
||||||
|
}
|
||||||
|
|
||||||
fence = _fence;
|
fence = _fence;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -57,6 +65,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _fence;
|
return _fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PutLock()
|
||||||
|
{
|
||||||
|
Put();
|
||||||
|
|
||||||
|
if (_concurrentWaitUnsupported)
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Put()
|
public void Put()
|
||||||
{
|
{
|
||||||
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||||
@ -66,24 +84,67 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AcquireLock()
|
||||||
|
{
|
||||||
|
while (!TryAcquireLock())
|
||||||
|
{
|
||||||
|
Thread.SpinWait(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryAcquireLock()
|
||||||
|
{
|
||||||
|
return Interlocked.Exchange(ref _lock, 1) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseLock()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _lock, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public void Wait()
|
public void Wait()
|
||||||
{
|
{
|
||||||
Span<Fence> fences = stackalloc Fence[]
|
if (_concurrentWaitUnsupported)
|
||||||
{
|
{
|
||||||
_fence,
|
AcquireLock();
|
||||||
};
|
|
||||||
|
|
||||||
FenceHelper.WaitAllIndefinitely(_api, _device, fences);
|
try
|
||||||
|
{
|
||||||
|
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsSignaled()
|
public bool IsSignaled()
|
||||||
{
|
{
|
||||||
Span<Fence> fences = stackalloc Fence[]
|
if (_concurrentWaitUnsupported)
|
||||||
{
|
{
|
||||||
_fence,
|
if (!TryAcquireLock())
|
||||||
};
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return FenceHelper.AllSignaled(_api, _device, fences);
|
try
|
||||||
|
{
|
||||||
|
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -196,18 +196,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
bool signaled = true;
|
bool signaled = true;
|
||||||
|
|
||||||
if (hasTimeout)
|
try
|
||||||
{
|
{
|
||||||
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
|
if (hasTimeout)
|
||||||
|
{
|
||||||
|
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
|
for (int i = 0; i < fenceCount; i++)
|
||||||
}
|
{
|
||||||
|
fenceHolders[i].PutLock();
|
||||||
for (int i = 0; i < fenceCount; i++)
|
}
|
||||||
{
|
|
||||||
fenceHolders[i].Put();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return signaled;
|
return signaled;
|
||||||
|
@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
|
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
|
||||||
|
|
||||||
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device, this);
|
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
|
||||||
_vertexBufferUpdater = new VertexBufferUpdater(gd);
|
_vertexBufferUpdater = new VertexBufferUpdater(gd);
|
||||||
|
|
||||||
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
|
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
|
||||||
@ -1020,6 +1020,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_newState.RasterizerDiscardEnable = discard;
|
_newState.RasterizerDiscardEnable = discard;
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
|
|
||||||
|
if (!discard && Gd.Vendor == Vendor.Qualcomm)
|
||||||
|
{
|
||||||
|
// On Adreno, enabling rasterizer discard somehow corrupts the viewport state.
|
||||||
|
// Force it to be updated on next use to work around this bug.
|
||||||
|
DynamicState.ForceAllDirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
||||||
|
@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public bool IsCompute { get; }
|
public bool IsCompute { get; }
|
||||||
public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
|
public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
|
||||||
|
|
||||||
|
public bool UpdateTexturesWithoutTemplate { get; }
|
||||||
|
|
||||||
public uint Stages { get; }
|
public uint Stages { get; }
|
||||||
|
|
||||||
public ResourceBindingSegment[][] ClearSegments { get; }
|
public ResourceBindingSegment[][] ClearSegments { get; }
|
||||||
@ -127,9 +129,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Stages = stages;
|
Stages = stages;
|
||||||
|
|
||||||
ClearSegments = BuildClearSegments(sets);
|
ClearSegments = BuildClearSegments(sets);
|
||||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
|
||||||
Templates = BuildTemplates(usePushDescriptors);
|
Templates = BuildTemplates(usePushDescriptors);
|
||||||
|
|
||||||
|
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
|
||||||
|
UpdateTexturesWithoutTemplate = gd.Vendor == Vendor.Qualcomm && usesBufferTextures;
|
||||||
|
|
||||||
_compileTask = Task.CompletedTask;
|
_compileTask = Task.CompletedTask;
|
||||||
_firstBackgroundUse = false;
|
_firstBackgroundUse = false;
|
||||||
}
|
}
|
||||||
@ -280,8 +285,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages, out bool usesBufferTextures)
|
||||||
{
|
{
|
||||||
|
usesBufferTextures = false;
|
||||||
|
|
||||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
||||||
|
|
||||||
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
||||||
@ -295,6 +302,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
||||||
|
|
||||||
|
if (usage.Type == ResourceType.BufferTexture)
|
||||||
|
{
|
||||||
|
usesBufferTextures = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentUsage.Binding + currentCount != usage.Binding ||
|
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||||
currentUsage.Type != usage.Type ||
|
currentUsage.Type != usage.Type ||
|
||||||
currentUsage.Stages != usage.Stages ||
|
currentUsage.Stages != usage.Stages ||
|
||||||
|
@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||||
|
|
||||||
var flags = ImageCreateFlags.CreateMutableFormatBit;
|
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
|
||||||
|
|
||||||
// This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
|
// This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
|
||||||
bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
|
bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
|
||||||
|
@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags)
|
unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags)
|
||||||
{
|
{
|
||||||
var usage = new ImageViewUsageCreateInfo
|
var imageViewUsage = new ImageViewUsageCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.ImageViewUsageCreateInfo,
|
SType = StructureType.ImageViewUsageCreateInfo,
|
||||||
Usage = usageFlags,
|
Usage = usageFlags,
|
||||||
@ -114,7 +114,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Format = format,
|
Format = format,
|
||||||
Components = cm,
|
Components = cm,
|
||||||
SubresourceRange = sr,
|
SubresourceRange = sr,
|
||||||
PNext = &usage,
|
PNext = &imageViewUsage,
|
||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
|
gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
|
||||||
@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit;
|
ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit;
|
||||||
|
|
||||||
if (info.Format.IsImageCompatible())
|
if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample()))
|
||||||
{
|
{
|
||||||
shaderUsage |= ImageUsageFlags.StorageBit;
|
shaderUsage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
|
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, 1, (uint)firstLayer, (uint)info.Depth);
|
||||||
|
|
||||||
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray, usage);
|
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray, usage);
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_EXT_depth_clip_control",
|
"VK_EXT_depth_clip_control",
|
||||||
"VK_KHR_portability_subset", // As per spec, we should enable this if present.
|
"VK_KHR_portability_subset", // As per spec, we should enable this if present.
|
||||||
"VK_EXT_4444_formats",
|
"VK_EXT_4444_formats",
|
||||||
|
"VK_KHR_8bit_storage",
|
||||||
|
"VK_KHR_maintenance2",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly string[] _requiredExtensions = {
|
private static readonly string[] _requiredExtensions = {
|
||||||
@ -355,6 +357,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &supportedFeaturesDepthClipControl;
|
features2.PNext = &supportedFeaturesDepthClipControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceVulkan12Features,
|
||||||
|
PNext = features2.PNext,
|
||||||
|
};
|
||||||
|
|
||||||
|
features2.PNext = &supportedPhysicalDeviceVulkan12Features;
|
||||||
|
|
||||||
api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
|
api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
|
||||||
|
|
||||||
var supportedFeatures = features2.Features;
|
var supportedFeatures = features2.Features;
|
||||||
@ -382,6 +392,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
TessellationShader = supportedFeatures.TessellationShader,
|
TessellationShader = supportedFeatures.TessellationShader,
|
||||||
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
|
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
|
||||||
RobustBufferAccess = useRobustBufferAccess,
|
RobustBufferAccess = useRobustBufferAccess,
|
||||||
|
SampleRateShading = supportedFeatures.SampleRateShading,
|
||||||
};
|
};
|
||||||
|
|
||||||
void* pExtendedFeatures = null;
|
void* pExtendedFeatures = null;
|
||||||
@ -451,9 +462,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceVulkan12Features,
|
SType = StructureType.PhysicalDeviceVulkan12Features,
|
||||||
PNext = pExtendedFeatures,
|
PNext = pExtendedFeatures,
|
||||||
DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"),
|
DescriptorIndexing = supportedPhysicalDeviceVulkan12Features.DescriptorIndexing,
|
||||||
DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
|
DrawIndirectCount = supportedPhysicalDeviceVulkan12Features.DrawIndirectCount,
|
||||||
UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout"),
|
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
|
||||||
|
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
|
||||||
|
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
|
||||||
};
|
};
|
||||||
|
|
||||||
pExtendedFeatures = &featuresVk12;
|
pExtendedFeatures = &featuresVk12;
|
||||||
|
@ -90,6 +90,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal bool IsMoltenVk { get; private set; }
|
internal bool IsMoltenVk { get; private set; }
|
||||||
internal bool IsTBDR { get; private set; }
|
internal bool IsTBDR { get; private set; }
|
||||||
internal bool IsSharedMemory { get; private set; }
|
internal bool IsSharedMemory { get; private set; }
|
||||||
|
internal bool IsConcurrentFenceWaitUnsupported { get; private set; }
|
||||||
|
|
||||||
public string GpuVendor { get; private set; }
|
public string GpuVendor { get; private set; }
|
||||||
public string GpuDriver { get; private set; }
|
public string GpuDriver { get; private set; }
|
||||||
public string GpuRenderer { get; private set; }
|
public string GpuRenderer { get; private set; }
|
||||||
@ -323,6 +325,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Vendor == Vendor.Broadcom ||
|
Vendor == Vendor.Broadcom ||
|
||||||
Vendor == Vendor.ImgTec;
|
Vendor == Vendor.ImgTec;
|
||||||
|
|
||||||
|
IsConcurrentFenceWaitUnsupported = Vendor == Vendor.Qualcomm;
|
||||||
|
|
||||||
GpuVendor = VendorUtils.GetNameFromId(properties.VendorID);
|
GpuVendor = VendorUtils.GetNameFromId(properties.VendorID);
|
||||||
GpuDriver = hasDriverProperties && !OperatingSystem.IsMacOS() ?
|
GpuDriver = hasDriverProperties && !OperatingSystem.IsMacOS() ?
|
||||||
VendorUtils.GetFriendlyDriverName(driverProperties.DriverID) : GpuVendor; // Fallback to vendor name if driver is unavailable or on MacOS where vendor is preferred.
|
VendorUtils.GetFriendlyDriverName(driverProperties.DriverID) : GpuVendor; // Fallback to vendor name if driver is unavailable or on MacOS where vendor is preferred.
|
||||||
@ -411,7 +415,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
|
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
|
||||||
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
|
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
|
||||||
|
|
||||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsConcurrentFenceWaitUnsupported);
|
||||||
|
|
||||||
PipelineLayoutCache = new PipelineLayoutCache();
|
PipelineLayoutCache = new PipelineLayoutCache();
|
||||||
|
|
||||||
|
@ -623,7 +623,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public override void SetSize(int width, int height)
|
public override void SetSize(int width, int height)
|
||||||
{
|
{
|
||||||
// Not needed as we can get the size from the surface.
|
// We don't need to use width and height as we can get the size from the surface.
|
||||||
|
_swapchainIsDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ChangeVSyncMode(bool vsyncEnabled)
|
public override void ChangeVSyncMode(bool vsyncEnabled)
|
||||||
|
@ -616,7 +616,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray);
|
ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray, true);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1546,8 +1546,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
#pragma warning disable CA1822 // Mark member as static
|
#pragma warning disable CA1822 // Mark member as static
|
||||||
public Result SetProcessMemoryPermission(
|
public Result SetProcessMemoryPermission(
|
||||||
int handle,
|
int handle,
|
||||||
[PointerSized] ulong src,
|
ulong src,
|
||||||
[PointerSized] ulong size,
|
ulong size,
|
||||||
KMemoryPermission permission)
|
KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
if (!PageAligned(src))
|
if (!PageAligned(src))
|
||||||
|
@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray);
|
ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.CriticalSection.Leave();
|
_context.CriticalSection.Leave();
|
||||||
|
@ -104,8 +104,13 @@ namespace Ryujinx.UI.Common
|
|||||||
// Find the length to trim the string to guarantee we have space for the trailing ellipsis.
|
// Find the length to trim the string to guarantee we have space for the trailing ellipsis.
|
||||||
int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
|
int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
|
||||||
|
|
||||||
// Basic trim to best case scenario of 1 byte characters.
|
// Make sure the string is long enough to perform the basic trim.
|
||||||
input = input[..trimLimit];
|
// Amount of bytes != Length of the string
|
||||||
|
if (input.Length > trimLimit)
|
||||||
|
{
|
||||||
|
// Basic trim to best case scenario of 1 byte characters.
|
||||||
|
input = input[..trimLimit];
|
||||||
|
}
|
||||||
|
|
||||||
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
|
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user