Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
552c15739c | |||
0137c9e635 | |||
23fa5f4c9c | |||
4f75e26ec7 | |||
8d8983049e | |||
7969fb6bba | |||
4a4b11871e | |||
e85ee673b1 | |||
42f22fe5d7 | |||
263eb97f79 | |||
3004902257 | |||
59ddb26628 | |||
83fda10f6e | |||
d97e995e59 | |||
56b2f84702 | |||
698e36bbd2 | |||
6ce49a2dc7 | |||
ccd330ba0f | |||
95d252b7b8 | |||
add681144b | |||
c6dc00815a | |||
99f04ac1a6 | |||
ce09450743 | |||
2cb80f37d4 | |||
827069e784 | |||
1a919e99b2 | |||
f77bebac80 | |||
6fbf279fac | |||
344f4f52c1 | |||
eb212aa91b | |||
a6dbb2ad2b | |||
595e514f18 | |||
07435ad844 | |||
1668ba913f | |||
a830eb666b | |||
cfc75d7e78 | |||
c525d7d9a9 | |||
1a0a351a15 | |||
bd3335c143 | |||
a94445b23e | |||
0c3421973c | |||
0afa8f2c14 | |||
d25a084858 | |||
311ca3c3f1 | |||
3193ef1083 | |||
5a878ae9af | |||
1828bc949e | |||
c0f2491eae | |||
d7c6474729 | |||
1ecc8fbc3b | |||
888402ecaf | |||
971d24aef0 | |||
c41fddd25e | |||
2ebe929fa5 |
@ -13,16 +13,16 @@
|
|||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
<PackageVersion Include="Concentus" Version="2.2.0" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="8.4.1" />
|
<PackageVersion Include="DynamicData" Version="9.0.1" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
<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.5.2" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
|
||||||
<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.0" />
|
<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" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.9" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
|
@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
long originalPosition = _stream.Position;
|
long originalPosition = _stream.Position;
|
||||||
|
|
||||||
_stream.Seek(0, SeekOrigin.Begin);
|
_stream.Seek(0, SeekOrigin.Begin);
|
||||||
_stream.Read(code, 0, code.Length);
|
_stream.ReadExactly(code, 0, code.Length);
|
||||||
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
RelocInfo relocInfo;
|
RelocInfo relocInfo;
|
||||||
|
@ -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;
|
||||||
|
@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||||
|
|
||||||
_stream.Read(buffer);
|
_stream.ReadExactly(buffer);
|
||||||
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
||||||
|
|
||||||
codeStream.Write(buffer);
|
codeStream.Write(buffer);
|
||||||
|
@ -822,6 +822,10 @@ namespace ARMeilleure.Decoders
|
|||||||
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
|
||||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
|
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
|
||||||
|
SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
|
||||||
|
SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
|
||||||
|
SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
|
||||||
|
SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
|
||||||
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
|
||||||
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
|
||||||
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
|
||||||
@ -1007,6 +1011,8 @@ namespace ARMeilleure.Decoders
|
|||||||
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
|
||||||
|
SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
@ -1030,6 +1036,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
|
||||||
|
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
|
||||||
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||||
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
|
||||||
@ -1054,6 +1061,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
|
||||||
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
|
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
|
||||||
|
SetAsimd("111100111x110010xxxx00000xx0xxxx", InstName.Vswp, InstEmit32.Vswp, OpCode32Simd.Create, OpCode32Simd.CreateT32);
|
||||||
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
|
||||||
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
@ -2,6 +2,8 @@ using ARMeilleure.Decoders;
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||||
@ -558,6 +560,46 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitHsub8(context, unsigned: true);
|
EmitHsub8(context, unsigned: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Uqadd16(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||||
|
|
||||||
|
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||||
|
{
|
||||||
|
EmitSaturateUqadd(context, d, context.Add(n, m), 16);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Uqadd8(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||||
|
|
||||||
|
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||||
|
{
|
||||||
|
EmitSaturateUqadd(context, d, context.Add(n, m), 8);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Uqsub16(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||||
|
|
||||||
|
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||||
|
{
|
||||||
|
EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Uqsub8(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||||
|
|
||||||
|
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
|
||||||
|
{
|
||||||
|
EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Usat(ArmEmitterContext context)
|
public static void Usat(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||||
@ -934,6 +976,148 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||||
|
{
|
||||||
|
Debug.Assert(saturateTo <= 32);
|
||||||
|
|
||||||
|
if (saturateTo == 32)
|
||||||
|
{
|
||||||
|
// No saturation possible for this case.
|
||||||
|
|
||||||
|
context.Copy(result, value);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (saturateTo == 0)
|
||||||
|
{
|
||||||
|
// Result is always zero if we saturate 0 bits.
|
||||||
|
|
||||||
|
context.Copy(result, Const(0));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the result is 0, the values are equal and we don't need saturation.
|
||||||
|
Operand lblNoSat = Label();
|
||||||
|
context.BranchIfFalse(lblNoSat, context.ShiftRightUI(value, Const((int)saturateTo)));
|
||||||
|
|
||||||
|
// Saturate.
|
||||||
|
context.Copy(result, Const(uint.MaxValue >> (32 - (int)saturateTo)));
|
||||||
|
|
||||||
|
Operand lblExit = Label();
|
||||||
|
context.Branch(lblExit);
|
||||||
|
|
||||||
|
context.MarkLabel(lblNoSat);
|
||||||
|
|
||||||
|
context.Copy(result, value);
|
||||||
|
|
||||||
|
context.MarkLabel(lblExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSaturateUqsub(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
|
||||||
|
{
|
||||||
|
Debug.Assert(saturateTo <= 32);
|
||||||
|
|
||||||
|
if (saturateTo == 32)
|
||||||
|
{
|
||||||
|
// No saturation possible for this case.
|
||||||
|
|
||||||
|
context.Copy(result, value);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (saturateTo == 0)
|
||||||
|
{
|
||||||
|
// Result is always zero if we saturate 0 bits.
|
||||||
|
|
||||||
|
context.Copy(result, Const(0));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the result is 0, the values are equal and we don't need saturation.
|
||||||
|
Operand lblNoSat = Label();
|
||||||
|
context.BranchIf(lblNoSat, value, Const(0), Comparison.GreaterOrEqual);
|
||||||
|
|
||||||
|
// Saturate.
|
||||||
|
// Assumes that the value can only underflow, since this is only used for unsigned subtraction.
|
||||||
|
context.Copy(result, Const(0));
|
||||||
|
|
||||||
|
Operand lblExit = Label();
|
||||||
|
context.Branch(lblExit);
|
||||||
|
|
||||||
|
context.MarkLabel(lblNoSat);
|
||||||
|
|
||||||
|
context.Copy(result, value);
|
||||||
|
|
||||||
|
context.MarkLabel(lblExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||||
|
{
|
||||||
|
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||||
|
|
||||||
|
Operand tempN = context.ZeroExtend16(OperandType.I32, rn);
|
||||||
|
Operand tempM = context.ZeroExtend16(OperandType.I32, rm);
|
||||||
|
elementAction(tempD, tempN, tempM);
|
||||||
|
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
|
||||||
|
|
||||||
|
tempN = context.ShiftRightUI(rn, Const(16));
|
||||||
|
tempM = context.ShiftRightUI(rm, Const(16));
|
||||||
|
elementAction(tempD, tempN, tempM);
|
||||||
|
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitSigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||||
|
{
|
||||||
|
return Emit8BitPair(context, rn, rm, elementAction, unsigned: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
|
||||||
|
{
|
||||||
|
return Emit8BitPair(context, rn, rm, elementAction, unsigned: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction, bool unsigned)
|
||||||
|
{
|
||||||
|
Operand tempD = context.AllocateLocal(OperandType.I32);
|
||||||
|
Operand result = default;
|
||||||
|
|
||||||
|
for (int b = 0; b < 4; b++)
|
||||||
|
{
|
||||||
|
Operand nByte = b != 0 ? context.ShiftRightUI(rn, Const(b * 8)) : rn;
|
||||||
|
Operand mByte = b != 0 ? context.ShiftRightUI(rm, Const(b * 8)) : rm;
|
||||||
|
|
||||||
|
if (unsigned)
|
||||||
|
{
|
||||||
|
nByte = context.ZeroExtend8(OperandType.I32, nByte);
|
||||||
|
mByte = context.ZeroExtend8(OperandType.I32, mByte);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nByte = context.SignExtend8(OperandType.I32, nByte);
|
||||||
|
mByte = context.SignExtend8(OperandType.I32, mByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
elementAction(tempD, nByte, mByte);
|
||||||
|
|
||||||
|
if (b == 0)
|
||||||
|
{
|
||||||
|
result = context.ZeroExtend8(OperandType.I32, tempD);
|
||||||
|
}
|
||||||
|
else if (b < 3)
|
||||||
|
{
|
||||||
|
result = context.BitwiseOr(result, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, tempD), Const(b * 8)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = context.BitwiseOr(result, context.ShiftLeft(tempD, Const(24)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||||
{
|
{
|
||||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||||
|
@ -1246,6 +1246,33 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Vqrdmulh(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||||
|
int eSize = 8 << op.Size;
|
||||||
|
|
||||||
|
EmitVectorBinaryOpI32(context, (op1, op2) =>
|
||||||
|
{
|
||||||
|
if (op.Size == 2)
|
||||||
|
{
|
||||||
|
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||||
|
op2 = context.SignExtend32(OperandType.I64, op2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand res = context.Multiply(op1, op2);
|
||||||
|
res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
|
||||||
|
res = context.ShiftRightSI(res, Const(eSize - 1));
|
||||||
|
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
|
||||||
|
|
||||||
|
if (op.Size == 2)
|
||||||
|
{
|
||||||
|
res = context.ConvertI64ToI32(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}, signed: true);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Vqsub(ArmEmitterContext context)
|
public static void Vqsub(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||||
|
@ -191,6 +191,26 @@ namespace ARMeilleure.Instructions
|
|||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Vswp(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
|
||||||
|
if (op.Q)
|
||||||
|
{
|
||||||
|
Operand temp = context.Copy(GetVecA32(op.Qd));
|
||||||
|
|
||||||
|
context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
|
||||||
|
context.Copy(GetVecA32(op.Qm), temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
|
||||||
|
|
||||||
|
InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
|
||||||
|
InsertScalar(context, op.Vm, temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Vtbl(ArmEmitterContext context)
|
public static void Vtbl(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
||||||
|
@ -130,6 +130,36 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Vsli_I(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
int shift = op.Shift;
|
||||||
|
int eSize = 8 << op.Size;
|
||||||
|
|
||||||
|
ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
|
||||||
|
|
||||||
|
Operand res = GetVec(op.Qd);
|
||||||
|
|
||||||
|
int elems = op.GetBytesCount() >> op.Size;
|
||||||
|
|
||||||
|
for (int index = 0; index < elems; index++)
|
||||||
|
{
|
||||||
|
Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
|
||||||
|
|
||||||
|
Operand neShifted = context.ShiftLeft(me, Const(shift));
|
||||||
|
|
||||||
|
Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
|
||||||
|
|
||||||
|
Operand deMasked = context.BitwiseAnd(de, Const(mask));
|
||||||
|
|
||||||
|
Operand e = context.BitwiseOr(neShifted, deMasked);
|
||||||
|
|
||||||
|
res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVec(op.Qd), res);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Vsra(ArmEmitterContext context)
|
public static void Vsra(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||||
|
@ -571,6 +571,10 @@ namespace ARMeilleure.Instructions
|
|||||||
Umaal,
|
Umaal,
|
||||||
Umlal,
|
Umlal,
|
||||||
Umull,
|
Umull,
|
||||||
|
Uqadd16,
|
||||||
|
Uqadd8,
|
||||||
|
Uqsub16,
|
||||||
|
Uqsub8,
|
||||||
Usat,
|
Usat,
|
||||||
Usat16,
|
Usat16,
|
||||||
Usub8,
|
Usub8,
|
||||||
@ -645,6 +649,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Vqdmulh,
|
Vqdmulh,
|
||||||
Vqmovn,
|
Vqmovn,
|
||||||
Vqmovun,
|
Vqmovun,
|
||||||
|
Vqrdmulh,
|
||||||
Vqrshrn,
|
Vqrshrn,
|
||||||
Vqrshrun,
|
Vqrshrun,
|
||||||
Vqshrn,
|
Vqshrn,
|
||||||
@ -666,6 +671,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Vshll,
|
Vshll,
|
||||||
Vshr,
|
Vshr,
|
||||||
Vshrn,
|
Vshrn,
|
||||||
|
Vsli,
|
||||||
Vst1,
|
Vst1,
|
||||||
Vst2,
|
Vst2,
|
||||||
Vst3,
|
Vst3,
|
||||||
@ -682,6 +688,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Vsub,
|
Vsub,
|
||||||
Vsubl,
|
Vsubl,
|
||||||
Vsubw,
|
Vsubw,
|
||||||
|
Vswp,
|
||||||
Vtbl,
|
Vtbl,
|
||||||
Vtrn,
|
Vtrn,
|
||||||
Vtst,
|
Vtst,
|
||||||
|
@ -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;
|
||||||
|
@ -89,9 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * _bytesPerFrame);
|
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * _bytesPerFrame);
|
||||||
|
|
||||||
Span<byte> samples = samplesOwner.Memory.Span;
|
Span<byte> samples = samplesOwner.Span;
|
||||||
|
|
||||||
_ringBuffer.Read(samples, 0, samples.Length);
|
_ringBuffer.Read(samples, 0, samples.Length);
|
||||||
|
|
||||||
|
@ -122,9 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
|
|
||||||
int channelCount = areas.Length;
|
int channelCount = areas.Length;
|
||||||
|
|
||||||
using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * bytesPerFrame);
|
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * bytesPerFrame);
|
||||||
|
|
||||||
Span<byte> samples = samplesOwner.Memory.Span;
|
Span<byte> samples = samplesOwner.Span;
|
||||||
|
|
||||||
_ringBuffer.Read(samples, 0, samples.Length);
|
_ringBuffer.Read(samples, 0, samples.Length);
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Backends.Common
|
|||||||
|
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
|
|
||||||
private IMemoryOwner<byte> _bufferOwner;
|
private MemoryOwner<byte> _bufferOwner;
|
||||||
private Memory<byte> _buffer;
|
private Memory<byte> _buffer;
|
||||||
private int _size;
|
private int _size;
|
||||||
private int _headOffset;
|
private int _headOffset;
|
||||||
@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Backends.Common
|
|||||||
|
|
||||||
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
|
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
|
||||||
{
|
{
|
||||||
_bufferOwner = ByteMemoryPool.RentCleared(initialCapacity);
|
_bufferOwner = MemoryOwner<byte>.RentCleared(initialCapacity);
|
||||||
_buffer = _bufferOwner.Memory;
|
_buffer = _bufferOwner.Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Backends.Common
|
|||||||
|
|
||||||
private void SetCapacityLocked(int capacity)
|
private void SetCapacityLocked(int capacity)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> newBufferOwner = ByteMemoryPool.RentCleared(capacity);
|
MemoryOwner<byte> newBufferOwner = MemoryOwner<byte>.RentCleared(capacity);
|
||||||
Memory<byte> newBuffer = newBufferOwner.Memory;
|
Memory<byte> newBuffer = newBufferOwner.Memory;
|
||||||
|
|
||||||
if (_size > 0)
|
if (_size > 0)
|
||||||
|
@ -18,16 +18,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
|||||||
|
|
||||||
if (version == 2)
|
if (version == 2)
|
||||||
{
|
{
|
||||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2,
|
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||||
PerformanceEntryVersion2,
|
|
||||||
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == 1)
|
if (version == 1)
|
||||||
{
|
{
|
||||||
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1,
|
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
||||||
PerformanceEntryVersion1,
|
|
||||||
PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");
|
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");
|
||||||
|
@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
|||||||
{
|
{
|
||||||
performanceEntry = null;
|
performanceEntry = null;
|
||||||
|
|
||||||
if (_entryDetailIndex > MaxFrameDetailCount)
|
if (_entryDetailIndex >= MaxFrameDetailCount)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
|||||||
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
|
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
|
||||||
};
|
};
|
||||||
|
|
||||||
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex);
|
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
|
||||||
|
|
||||||
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];
|
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];
|
||||||
|
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Memory
|
|
||||||
{
|
|
||||||
public partial class ByteMemoryPool
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
|
|
||||||
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
|
|
||||||
/// with a length of the requested size.
|
|
||||||
/// </summary>
|
|
||||||
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
|
|
||||||
{
|
|
||||||
private byte[] _array;
|
|
||||||
private readonly int _length;
|
|
||||||
|
|
||||||
public ByteMemoryPoolBuffer(int length)
|
|
||||||
{
|
|
||||||
_array = ArrayPool<byte>.Shared.Rent(length);
|
|
||||||
_length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
|
|
||||||
/// </summary>
|
|
||||||
public Memory<byte> Memory
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
byte[] array = _array;
|
|
||||||
|
|
||||||
ObjectDisposedException.ThrowIf(array is null, this);
|
|
||||||
|
|
||||||
return new Memory<byte>(array, 0, _length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
var array = Interlocked.Exchange(ref _array, null);
|
|
||||||
|
|
||||||
if (array != null)
|
|
||||||
{
|
|
||||||
ArrayPool<byte>.Shared.Return(array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Memory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a pool of re-usable byte array instances.
|
|
||||||
/// </summary>
|
|
||||||
public static partial class ByteMemoryPool
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the maximum buffer size supported by this pool.
|
|
||||||
/// </summary>
|
|
||||||
public static int MaxBufferSize => Array.MaxLength;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
|
||||||
/// The buffer may contain data from a prior use.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The buffer's required length in bytes</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
||||||
public static IMemoryOwner<byte> Rent(long length)
|
|
||||||
=> RentImpl(checked((int)length));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
|
||||||
/// The buffer may contain data from a prior use.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The buffer's required length in bytes</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
||||||
public static IMemoryOwner<byte> Rent(ulong length)
|
|
||||||
=> RentImpl(checked((int)length));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
|
||||||
/// The buffer may contain data from a prior use.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The buffer's required length in bytes</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
||||||
public static IMemoryOwner<byte> Rent(int length)
|
|
||||||
=> RentImpl(length);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
|
||||||
/// The buffer's contents are cleared (set to all 0s) before returning.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The buffer's required length in bytes</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
||||||
public static IMemoryOwner<byte> RentCleared(long length)
|
|
||||||
=> RentCleared(checked((int)length));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
|
||||||
/// The buffer's contents are cleared (set to all 0s) before returning.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The buffer's required length in bytes</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
||||||
public static IMemoryOwner<byte> RentCleared(ulong length)
|
|
||||||
=> RentCleared(checked((int)length));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
|
||||||
/// The buffer's contents are cleared (set to all 0s) before returning.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The buffer's required length in bytes</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
||||||
public static IMemoryOwner<byte> RentCleared(int length)
|
|
||||||
{
|
|
||||||
var buffer = RentImpl(length);
|
|
||||||
|
|
||||||
buffer.Memory.Span.Clear();
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Copies <paramref name="buffer"/> into a newly rented byte memory buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The byte buffer to copy</param>
|
|
||||||
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory with <paramref name="buffer"/> copied to it</returns>
|
|
||||||
public static IMemoryOwner<byte> RentCopy(ReadOnlySpan<byte> buffer)
|
|
||||||
{
|
|
||||||
var copy = RentImpl(buffer.Length);
|
|
||||||
|
|
||||||
buffer.CopyTo(copy.Memory.Span);
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ByteMemoryPoolBuffer RentImpl(int length)
|
|
||||||
{
|
|
||||||
if ((uint)length > Array.MaxLength)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(length), length, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ByteMemoryPoolBuffer(length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
140
src/Ryujinx.Common/Memory/MemoryOwner.cs
Normal file
140
src/Ryujinx.Common/Memory/MemoryOwner.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and fast <see cref="Span{T}"/>
|
||||||
|
/// accessor, with memory allocated from <seealso cref="ArrayPool{T}.Shared"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of item to store.</typeparam>
|
||||||
|
public sealed class MemoryOwner<T> : IMemoryOwner<T>
|
||||||
|
{
|
||||||
|
private readonly int _length;
|
||||||
|
private T[]? _array;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MemoryOwner{T}"/> class with the specified parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the new memory buffer to use</param>
|
||||||
|
private MemoryOwner(int length)
|
||||||
|
{
|
||||||
|
_length = length;
|
||||||
|
_array = ArrayPool<T>.Shared.Rent(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the new memory buffer to use</param>
|
||||||
|
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static MemoryOwner<T> Rent(int length) => new(length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length and the content cleared.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the new memory buffer to use</param>
|
||||||
|
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length and the content cleared</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static MemoryOwner<T> RentCleared(int length)
|
||||||
|
{
|
||||||
|
MemoryOwner<T> result = new(length);
|
||||||
|
|
||||||
|
result._array.AsSpan(0, length).Clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the content copied from the specified buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The buffer to copy</param>
|
||||||
|
/// <returns>A <see cref="MemoryOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
|
||||||
|
public static MemoryOwner<T> RentCopy(ReadOnlySpan<T> buffer)
|
||||||
|
{
|
||||||
|
MemoryOwner<T> result = new(buffer.Length);
|
||||||
|
|
||||||
|
buffer.CopyTo(result._array);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of items in the current instance.
|
||||||
|
/// </summary>
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Memory<T> Memory
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
T[]? array = _array;
|
||||||
|
|
||||||
|
if (array is null)
|
||||||
|
{
|
||||||
|
ThrowObjectDisposedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(array, 0, _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Uses a trick made possible by the .NET 6+ runtime array layout.
|
||||||
|
/// </remarks>
|
||||||
|
public Span<T> Span
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
T[]? array = _array;
|
||||||
|
|
||||||
|
if (array is null)
|
||||||
|
{
|
||||||
|
ThrowObjectDisposedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(array);
|
||||||
|
|
||||||
|
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
T[]? array = Interlocked.Exchange(ref _array, null);
|
||||||
|
|
||||||
|
if (array is not null)
|
||||||
|
{
|
||||||
|
ArrayPool<T>.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Throws an <see cref="ObjectDisposedException"/> when <see cref="_array"/> is <see langword="null"/>.
|
||||||
|
/// </summary>
|
||||||
|
[DoesNotReturn]
|
||||||
|
private static void ThrowObjectDisposedException()
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(MemoryOwner<T>), "The buffer has already been disposed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
src/Ryujinx.Common/Memory/SpanOwner.cs
Normal file
114
src/Ryujinx.Common/Memory/SpanOwner.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A stack-only type that rents a buffer of a specified length from <seealso cref="ArrayPool{T}.Shared"/>.
|
||||||
|
/// It does not implement <see cref="IDisposable"/> to avoid being boxed, but should still be disposed. This
|
||||||
|
/// is easy since C# 8, which allows use of C# `using` constructs on any type that has a public Dispose() method.
|
||||||
|
/// To keep this type simple, fast, and read-only, it does not check or guard against multiple disposals.
|
||||||
|
/// For all these reasons, all usage should be with a `using` block or statement.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of item to store.</typeparam>
|
||||||
|
public readonly ref struct SpanOwner<T>
|
||||||
|
{
|
||||||
|
private readonly int _length;
|
||||||
|
private readonly T[] _array;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SpanOwner{T}"/> struct with the specified parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the new memory buffer to use</param>
|
||||||
|
private SpanOwner(int length)
|
||||||
|
{
|
||||||
|
_length = length;
|
||||||
|
_array = ArrayPool<T>.Shared.Rent(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an empty <see cref="SpanOwner{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public static SpanOwner<T> Empty
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="SpanOwner{T}"/> instance with the specified length.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the new memory buffer to use</param>
|
||||||
|
/// <returns>A <see cref="SpanOwner{T}"/> instance of the requested length</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static SpanOwner<T> Rent(int length) => new(length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="SpanOwner{T}"/> instance with the length and the content cleared.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the new memory buffer to use</param>
|
||||||
|
/// <returns>A <see cref="SpanOwner{T}"/> instance of the requested length and the content cleared</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static SpanOwner<T> RentCleared(int length)
|
||||||
|
{
|
||||||
|
SpanOwner<T> result = new(length);
|
||||||
|
|
||||||
|
result._array.AsSpan(0, length).Clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="SpanOwner{T}"/> instance with the content copied from the specified buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The buffer to copy</param>
|
||||||
|
/// <returns>A <see cref="SpanOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
|
||||||
|
public static SpanOwner<T> RentCopy(ReadOnlySpan<T> buffer)
|
||||||
|
{
|
||||||
|
SpanOwner<T> result = new(buffer.Length);
|
||||||
|
|
||||||
|
buffer.CopyTo(result._array);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of items in the current instance
|
||||||
|
/// </summary>
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Uses a trick made possible by the .NET 6+ runtime array layout.
|
||||||
|
/// </remarks>
|
||||||
|
public Span<T> Span
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(_array);
|
||||||
|
|
||||||
|
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements the duck-typed <see cref="IDisposable.Dispose"/> method.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ArrayPool<T>.Shared.Return(_array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -42,14 +42,14 @@ namespace Ryujinx.Common
|
|||||||
return StreamUtils.StreamToBytes(stream);
|
return StreamUtils.StreamToBytes(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
|
||||||
{
|
{
|
||||||
var (assembly, path) = ResolveManifestPath(filename);
|
var (assembly, path) = ResolveManifestPath(filename);
|
||||||
|
|
||||||
return ReadFileToRentedMemory(assembly, path);
|
return ReadFileToRentedMemory(assembly, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
using var stream = GetStream(assembly, filename);
|
using var stream = GetStream(assembly, filename);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Microsoft.IO;
|
using Microsoft.IO;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System.Buffers;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -16,7 +15,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
|
public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
|
||||||
{
|
{
|
||||||
if (input is MemoryStream inputMemoryStream)
|
if (input is MemoryStream inputMemoryStream)
|
||||||
{
|
{
|
||||||
@ -26,9 +25,9 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
long bytesExpected = input.Length;
|
long bytesExpected = input.Length;
|
||||||
|
|
||||||
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
|
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
|
||||||
|
|
||||||
var destSpan = ownedMemory.Memory.Span;
|
var destSpan = ownedMemory.Span;
|
||||||
|
|
||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
|
|
||||||
@ -66,14 +65,14 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return stream.ToArray();
|
return stream.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
||||||
{
|
{
|
||||||
input.Position = 0;
|
input.Position = 0;
|
||||||
|
|
||||||
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
|
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)input.Length));
|
||||||
|
|
||||||
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
|
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
|
||||||
_ = input.Read(ownedMemory.Memory.Span);
|
_ = input.Read(ownedMemory.Span);
|
||||||
|
|
||||||
return ownedMemory;
|
return ownedMemory;
|
||||||
}
|
}
|
||||||
|
@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
Read(va, memoryOwner.Memory.Span);
|
Read(va, memoryOwner.Span);
|
||||||
|
|
||||||
return new WritableRegion(this, va, memoryOwner);
|
return new WritableRegion(this, va, memoryOwner);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||||
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Add(d, n, m);
|
context.Arm64Assembler.Add(d, n, m);
|
||||||
EmitSaturateUnsignedRange(context, d, 16);
|
EmitSaturateUqadd(context, d, 16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Add(d, n, m);
|
context.Arm64Assembler.Add(d, n, m);
|
||||||
EmitSaturateUnsignedRange(context, d, 8);
|
EmitSaturateUqadd(context, d, 8);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
context.Arm64Assembler.Add(d, n, m);
|
context.Arm64Assembler.Add(d, n, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitSaturateUnsignedRange(context, d, 16);
|
EmitSaturateUq(context, d, 16, e == 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,25 +157,25 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
context.Arm64Assembler.Sub(d, n, m);
|
context.Arm64Assembler.Sub(d, n, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitSaturateUnsignedRange(context, d, 16);
|
EmitSaturateUq(context, d, 16, e != 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
|
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||||
{
|
{
|
||||||
InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Sub(d, n, m);
|
context.Arm64Assembler.Sub(d, n, m);
|
||||||
EmitSaturateUnsignedRange(context, d, 16);
|
EmitSaturateUqsub(context, d, 16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
|
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
|
||||||
{
|
{
|
||||||
InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
|
||||||
{
|
{
|
||||||
context.Arm64Assembler.Sub(d, n, m);
|
context.Arm64Assembler.Sub(d, n, m);
|
||||||
EmitSaturateUnsignedRange(context, d, 8);
|
EmitSaturateUqsub(context, d, 8);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +358,17 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo)
|
private static void EmitSaturateUqadd(CodeGenContext context, Operand value, uint saturateTo)
|
||||||
|
{
|
||||||
|
EmitSaturateUq(context, value, saturateTo, isSub: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSaturateUqsub(CodeGenContext context, Operand value, uint saturateTo)
|
||||||
|
{
|
||||||
|
EmitSaturateUq(context, value, saturateTo, isSub: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSaturateUq(CodeGenContext context, Operand value, uint saturateTo, bool isSub)
|
||||||
{
|
{
|
||||||
Debug.Assert(saturateTo <= 32);
|
Debug.Assert(saturateTo <= 32);
|
||||||
|
|
||||||
@ -379,7 +389,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo));
|
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const((int)saturateTo));
|
||||||
|
|
||||||
int branchIndex = context.CodeWriter.InstructionPointer;
|
int branchIndex = context.CodeWriter.InstructionPointer;
|
||||||
|
|
||||||
@ -387,7 +397,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
|
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
|
||||||
|
|
||||||
// Saturate.
|
// Saturate.
|
||||||
context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo));
|
context.Arm64Assembler.Mov(value, isSub ? 0u : uint.MaxValue >> (32 - (int)saturateTo));
|
||||||
|
|
||||||
int delta = context.CodeWriter.InstructionPointer - branchIndex;
|
int delta = context.CodeWriter.InstructionPointer - branchIndex;
|
||||||
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
|
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@ -145,9 +144,9 @@ namespace Ryujinx.Graphics.Device
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
|
ReadImpl(va, memoryOwner.Span);
|
||||||
|
|
||||||
return new WritableRegion(this, va, memoryOwner, tracked: true);
|
return new WritableRegion(this, va, memoryOwner, tracked: true);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public interface IImageArray
|
public interface IImageArray : IDisposable
|
||||||
{
|
{
|
||||||
void SetFormats(int index, Format[] imageFormats);
|
void SetFormats(int index, Format[] imageFormats);
|
||||||
void SetImages(int index, ITexture[] images);
|
void SetImages(int index, ITexture[] images);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public interface ITextureArray
|
public interface ITextureArray : IDisposable
|
||||||
{
|
{
|
||||||
void SetSamplers(int index, ISampler[] samplers);
|
void SetSamplers(int index, ISampler[] samplers);
|
||||||
void SetTextures(int index, ITexture[] textures);
|
void SetTextures(int index, ITexture[] textures);
|
||||||
|
@ -66,6 +66,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose);
|
Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose);
|
||||||
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
||||||
|
|
||||||
|
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
|
||||||
Register<ImageArraySetFormatsCommand>(CommandType.ImageArraySetFormats);
|
Register<ImageArraySetFormatsCommand>(CommandType.ImageArraySetFormats);
|
||||||
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
||||||
|
|
||||||
@ -88,6 +89,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
Register<TextureSetDataSliceRegionCommand>(CommandType.TextureSetDataSliceRegion);
|
Register<TextureSetDataSliceRegionCommand>(CommandType.TextureSetDataSliceRegion);
|
||||||
Register<TextureSetStorageCommand>(CommandType.TextureSetStorage);
|
Register<TextureSetStorageCommand>(CommandType.TextureSetStorage);
|
||||||
|
|
||||||
|
Register<TextureArrayDisposeCommand>(CommandType.TextureArrayDispose);
|
||||||
Register<TextureArraySetSamplersCommand>(CommandType.TextureArraySetSamplers);
|
Register<TextureArraySetSamplersCommand>(CommandType.TextureArraySetSamplers);
|
||||||
Register<TextureArraySetTexturesCommand>(CommandType.TextureArraySetTextures);
|
Register<TextureArraySetTexturesCommand>(CommandType.TextureArraySetTextures);
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
CounterEventDispose,
|
CounterEventDispose,
|
||||||
CounterEventFlush,
|
CounterEventFlush,
|
||||||
|
|
||||||
|
ImageArrayDispose,
|
||||||
ImageArraySetFormats,
|
ImageArraySetFormats,
|
||||||
ImageArraySetImages,
|
ImageArraySetImages,
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
TextureSetDataSliceRegion,
|
TextureSetDataSliceRegion,
|
||||||
TextureSetStorage,
|
TextureSetStorage,
|
||||||
|
|
||||||
|
TextureArrayDispose,
|
||||||
TextureArraySetSamplers,
|
TextureArraySetSamplers,
|
||||||
TextureArraySetTextures,
|
TextureArraySetTextures,
|
||||||
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray
|
||||||
|
{
|
||||||
|
struct ImageArrayDisposeCommand : IGALCommand, IGALCommand<ImageArrayDisposeCommand>
|
||||||
|
{
|
||||||
|
public readonly CommandType CommandType => CommandType.ImageArrayDispose;
|
||||||
|
private TableRef<ThreadedImageArray> _imageArray;
|
||||||
|
|
||||||
|
public void Set(TableRef<ThreadedImageArray> imageArray)
|
||||||
|
{
|
||||||
|
_imageArray = imageArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(ref ImageArrayDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
|
{
|
||||||
|
command._imageArray.Get(threaded).Base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.TextureArray
|
||||||
|
{
|
||||||
|
struct TextureArrayDisposeCommand : IGALCommand, IGALCommand<TextureArrayDisposeCommand>
|
||||||
|
{
|
||||||
|
public readonly CommandType CommandType => CommandType.TextureArrayDispose;
|
||||||
|
private TableRef<ThreadedTextureArray> _textureArray;
|
||||||
|
|
||||||
|
public void Set(TableRef<ThreadedTextureArray> textureArray)
|
||||||
|
{
|
||||||
|
_textureArray = textureArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(ref TextureArrayDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
|
{
|
||||||
|
command._textureArray.Get(threaded).Base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
return new TableRef<T>(_renderer, reference);
|
return new TableRef<T>(_renderer, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_renderer.New<ImageArrayDisposeCommand>().Set(Ref(this));
|
||||||
|
_renderer.QueueCommand();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetFormats(int index, Format[] imageFormats)
|
public void SetFormats(int index, Format[] imageFormats)
|
||||||
{
|
{
|
||||||
_renderer.New<ImageArraySetFormatsCommand>().Set(Ref(this), index, Ref(imageFormats));
|
_renderer.New<ImageArraySetFormatsCommand>().Set(Ref(this), index, Ref(imageFormats));
|
||||||
|
@ -22,6 +22,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
return new TableRef<T>(_renderer, reference);
|
return new TableRef<T>(_renderer, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_renderer.New<TextureArrayDisposeCommand>().Set(Ref(this));
|
||||||
|
_renderer.QueueCommand();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetSamplers(int index, ISampler[] samplers)
|
public void SetSamplers(int index, ISampler[] samplers)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureArraySetSamplersCommand>().Set(Ref(this), index, Ref(samplers.ToArray()));
|
_renderer.New<TextureArraySetSamplersCommand>().Set(Ref(this), index, Ref(samplers.ToArray()));
|
||||||
|
@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public int ArrayLength { get; }
|
public int ArrayLength { get; }
|
||||||
public ResourceType Type { get; }
|
public ResourceType Type { get; }
|
||||||
public ResourceStages Stages { get; }
|
public ResourceStages Stages { get; }
|
||||||
|
public bool Write { get; }
|
||||||
|
|
||||||
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages)
|
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write)
|
||||||
{
|
{
|
||||||
Binding = binding;
|
Binding = binding;
|
||||||
ArrayLength = arrayLength;
|
ArrayLength = arrayLength;
|
||||||
Type = type;
|
Type = type;
|
||||||
Stages = stages;
|
Stages = stages;
|
||||||
|
Write = write;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
@ -113,25 +112,6 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetLevelsClamped()
|
|
||||||
{
|
|
||||||
int maxSize = Width;
|
|
||||||
|
|
||||||
if (Target != Target.Texture1D &&
|
|
||||||
Target != Target.Texture1DArray)
|
|
||||||
{
|
|
||||||
maxSize = Math.Max(maxSize, Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
maxSize = Math.Max(maxSize, Depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
|
|
||||||
return Math.Min(Levels, maxLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetLevelSize(int size, int level)
|
private static int GetLevelSize(int size, int level)
|
||||||
{
|
{
|
||||||
return Math.Max(1, size >> level);
|
return Math.Max(1, size >> level);
|
||||||
|
@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
|||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
target.SynchronizeMemory();
|
target.SynchronizeMemory();
|
||||||
var dataCopy = ByteMemoryPool.RentCopy(data);
|
var dataCopy = MemoryOwner<byte>.RentCopy(data);
|
||||||
target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
|
target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
|
||||||
target.SignalModified();
|
target.SignalModified();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
@ -805,7 +806,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
sliceDepth,
|
sliceDepth,
|
||||||
levels,
|
levels,
|
||||||
layers,
|
layers,
|
||||||
out IMemoryOwner<byte> decoded))
|
out MemoryOwner<byte> decoded))
|
||||||
{
|
{
|
||||||
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
|
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
|
||||||
|
|
||||||
|
@ -1113,6 +1113,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
nextNode = nextNode.Next;
|
nextNode = nextNode.Next;
|
||||||
_cacheFromBuffer.Remove(toRemove.Value.Key);
|
_cacheFromBuffer.Remove(toRemove.Value.Key);
|
||||||
_lruCache.Remove(toRemove);
|
_lruCache.Remove(toRemove);
|
||||||
|
|
||||||
|
if (toRemove.Value.Key.IsImage)
|
||||||
|
{
|
||||||
|
toRemove.Value.ImageArray.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toRemove.Value.TextureArray.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1124,11 +1133,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
List<CacheEntryFromPoolKey> keysToRemove = null;
|
List<CacheEntryFromPoolKey> keysToRemove = null;
|
||||||
|
|
||||||
foreach (CacheEntryFromPoolKey key in _cacheFromPool.Keys)
|
foreach ((CacheEntryFromPoolKey key, CacheEntry entry) in _cacheFromPool)
|
||||||
{
|
{
|
||||||
if (key.MatchesPool(pool))
|
if (key.MatchesPool(pool))
|
||||||
{
|
{
|
||||||
(keysToRemove ??= new()).Add(key);
|
(keysToRemove ??= new()).Add(key);
|
||||||
|
|
||||||
|
if (key.IsImage)
|
||||||
|
{
|
||||||
|
entry.ImageArray.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.TextureArray.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using Ryujinx.Memory.Range;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
@ -490,6 +491,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
levels = (maxLod - minLod) + 1;
|
levels = (maxLod - minLod) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
levels = ClampLevels(target, width, height, depthOrLayers, levels);
|
||||||
|
|
||||||
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
|
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
|
||||||
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
|
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
|
||||||
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
|
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
|
||||||
@ -540,6 +543,34 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
swizzleA);
|
swizzleA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clamps the amount of mipmap levels to the maximum allowed for the given texture dimensions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">Number of texture dimensions (1D, 2D, 3D, Cube, etc)</param>
|
||||||
|
/// <param name="width">Width of the texture</param>
|
||||||
|
/// <param name="height">Height of the texture, ignored for 1D textures</param>
|
||||||
|
/// <param name="depthOrLayers">Depth of the texture for 3D textures, otherwise ignored</param>
|
||||||
|
/// <param name="levels">Original amount of mipmap levels</param>
|
||||||
|
/// <returns>Clamped mipmap levels</returns>
|
||||||
|
private static int ClampLevels(Target target, int width, int height, int depthOrLayers, int levels)
|
||||||
|
{
|
||||||
|
int maxSize = width;
|
||||||
|
|
||||||
|
if (target != Target.Texture1D &&
|
||||||
|
target != Target.Texture1DArray)
|
||||||
|
{
|
||||||
|
maxSize = Math.Max(maxSize, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
maxSize = Math.Max(maxSize, depthOrLayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
|
||||||
|
return Math.Min(levels, maxLevels);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
|
/// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
|
||||||
/// The depth-stencil mode is determined based on how the driver sets those parameters.
|
/// The depth-stencil mode is determined based on how the driver sets those parameters.
|
||||||
|
@ -2,7 +2,6 @@ using Ryujinx.Common.Memory;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -242,9 +241,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
|
ReadImpl(va, memoryOwner.Span, tracked);
|
||||||
|
|
||||||
return new WritableRegion(this, va, memoryOwner, tracked);
|
return new WritableRegion(this, va, memoryOwner, tracked);
|
||||||
}
|
}
|
||||||
|
@ -192,9 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(range.GetSize());
|
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(checked((int)range.GetSize()));
|
||||||
|
|
||||||
Memory<byte> memory = memoryOwner.Memory;
|
Span<byte> memorySpan = memoryOwner.Span;
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int i = 0; i < range.Count; i++)
|
for (int i = 0; i < range.Count; i++)
|
||||||
@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
int size = (int)currentRange.Size;
|
int size = (int)currentRange.Size;
|
||||||
if (currentRange.Address != MemoryManager.PteUnmapped)
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
GetSpan(currentRange.Address, size).CopyTo(memory.Span.Slice(offset, size));
|
GetSpan(currentRange.Address, size).CopyTo(memorySpan.Slice(offset, size));
|
||||||
}
|
}
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
@ -125,9 +125,18 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
CompressionAlgorithm algorithm = CompressionAlgorithm.None;
|
CompressionAlgorithm algorithm = CompressionAlgorithm.None;
|
||||||
Read(ref algorithm);
|
Read(ref algorithm);
|
||||||
|
|
||||||
if (algorithm == CompressionAlgorithm.Deflate)
|
switch (algorithm)
|
||||||
{
|
{
|
||||||
_activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true);
|
case CompressionAlgorithm.None:
|
||||||
|
break;
|
||||||
|
case CompressionAlgorithm.Deflate:
|
||||||
|
_activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true);
|
||||||
|
break;
|
||||||
|
case CompressionAlgorithm.Brotli:
|
||||||
|
_activeStream = new BrotliStream(_stream, CompressionMode.Decompress, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Invalid compression algorithm \"{algorithm}\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +148,18 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
Write(ref algorithm);
|
Write(ref algorithm);
|
||||||
|
|
||||||
if (algorithm == CompressionAlgorithm.Deflate)
|
switch (algorithm)
|
||||||
{
|
{
|
||||||
_activeStream = new DeflateStream(_stream, CompressionLevel.Fastest, true);
|
case CompressionAlgorithm.None:
|
||||||
|
break;
|
||||||
|
case CompressionAlgorithm.Deflate:
|
||||||
|
_activeStream = new DeflateStream(_stream, CompressionLevel.Fastest, true);
|
||||||
|
break;
|
||||||
|
case CompressionAlgorithm.Brotli:
|
||||||
|
_activeStream = new BrotliStream(_stream, CompressionLevel.Fastest, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Invalid compression algorithm \"{algorithm}\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
switch (algorithm)
|
switch (algorithm)
|
||||||
{
|
{
|
||||||
case CompressionAlgorithm.None:
|
case CompressionAlgorithm.None:
|
||||||
stream.Read(data);
|
stream.ReadExactly(data);
|
||||||
break;
|
break;
|
||||||
case CompressionAlgorithm.Deflate:
|
case CompressionAlgorithm.Deflate:
|
||||||
stream = new DeflateStream(stream, CompressionMode.Decompress, true);
|
stream = new DeflateStream(stream, CompressionMode.Decompress, true);
|
||||||
@ -187,6 +205,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
}
|
}
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
break;
|
break;
|
||||||
|
case CompressionAlgorithm.Brotli:
|
||||||
|
stream = new BrotliStream(stream, CompressionMode.Decompress, true);
|
||||||
|
for (int offset = 0; offset < data.Length;)
|
||||||
|
{
|
||||||
|
offset += stream.Read(data[offset..]);
|
||||||
|
}
|
||||||
|
stream.Dispose();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +236,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
stream.Write(data);
|
stream.Write(data);
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
break;
|
break;
|
||||||
|
case CompressionAlgorithm.Brotli:
|
||||||
|
stream = new BrotliStream(stream, CompressionLevel.Fastest, true);
|
||||||
|
stream.Write(data);
|
||||||
|
stream.Dispose();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,5 +14,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
/// Deflate compression (RFC 1951).
|
/// Deflate compression (RFC 1951).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Deflate,
|
Deflate,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Brotli compression (RFC 7932).
|
||||||
|
/// </summary>
|
||||||
|
Brotli,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
/// <returns>Compression algorithm</returns>
|
/// <returns>Compression algorithm</returns>
|
||||||
public static CompressionAlgorithm GetCompressionAlgorithm()
|
public static CompressionAlgorithm GetCompressionAlgorithm()
|
||||||
{
|
{
|
||||||
return CompressionAlgorithm.Deflate;
|
return CompressionAlgorithm.Brotli;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
}
|
}
|
||||||
|
|
||||||
dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin);
|
dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin);
|
||||||
dataFileStream.Read(cb1Data);
|
dataFileStream.ReadExactly(cb1Data);
|
||||||
BinarySerializer.ReadCompressed(dataFileStream, guestCode);
|
BinarySerializer.ReadCompressed(dataFileStream, guestCode);
|
||||||
|
|
||||||
_cache[index] = (guestCode, cb1Data);
|
_cache[index] = (guestCode, cb1Data);
|
||||||
@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin);
|
dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin);
|
||||||
byte[] cachedCode = new byte[entry.CodeSize];
|
byte[] cachedCode = new byte[entry.CodeSize];
|
||||||
byte[] cachedCb1Data = new byte[entry.Cb1DataSize];
|
byte[] cachedCb1Data = new byte[entry.Cb1DataSize];
|
||||||
dataFileStream.Read(cachedCb1Data);
|
dataFileStream.ReadExactly(cachedCb1Data);
|
||||||
BinarySerializer.ReadCompressed(dataFileStream, cachedCode);
|
BinarySerializer.ReadCompressed(dataFileStream, cachedCode);
|
||||||
|
|
||||||
if (data.SequenceEqual(cachedCode) && cb1Data.SequenceEqual(cachedCb1Data))
|
if (data.SequenceEqual(cachedCode) && cb1Data.SequenceEqual(cachedCb1Data))
|
||||||
|
@ -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 = 6870;
|
private const uint CodeGenVersion = 7131;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -78,9 +78,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
|
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
|
||||||
|
|
||||||
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
|
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
|
||||||
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers);
|
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true);
|
||||||
PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
|
PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
|
||||||
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages);
|
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="setIndex">Resource set index where the resources are used</param>
|
/// <param name="setIndex">Resource set index where the resources are used</param>
|
||||||
/// <param name="start">First binding number</param>
|
/// <param name="start">First binding number</param>
|
||||||
/// <param name="count">Amount of bindings</param>
|
/// <param name="count">Amount of bindings</param>
|
||||||
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
|
/// <param name="write">True if the binding is written from the shader, false otherwise</param>
|
||||||
|
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
|
||||||
{
|
{
|
||||||
AddDescriptor(stages, type, setIndex, start, count);
|
AddDescriptor(stages, type, setIndex, start, count);
|
||||||
AddUsage(stages, type, setIndex, start, count);
|
AddUsage(stages, type, setIndex, start, count, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -216,11 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||||
/// <param name="binding">Binding number where the resource will be bound</param>
|
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||||
/// <param name="count">Number of resources bound at the binding location</param>
|
/// <param name="count">Number of resources bound at the binding location</param>
|
||||||
private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
|
/// <param name="write">True if the binding is written from the shader, false otherwise</param>
|
||||||
|
private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < count; index++)
|
for (int index = 0; index < count; index++)
|
||||||
{
|
{
|
||||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages));
|
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +240,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
buffer.Binding,
|
buffer.Binding,
|
||||||
1,
|
1,
|
||||||
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
|
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
|
||||||
stages));
|
stages,
|
||||||
|
buffer.Flags.HasFlag(BufferUsageFlags.Write)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +257,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ResourceType type = GetTextureResourceType(texture, isImage);
|
ResourceType type = GetTextureResourceType(texture, isImage);
|
||||||
|
|
||||||
GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
|
GetUsages(texture.Set).Add(new ResourceUsage(
|
||||||
|
texture.Binding,
|
||||||
|
texture.ArrayLength,
|
||||||
|
type,
|
||||||
|
stages,
|
||||||
|
texture.Flags.HasFlag(TextureUsageFlags.ImageStore)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
@ -10,11 +9,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
{
|
{
|
||||||
static class FormatConverter
|
static class FormatConverter
|
||||||
{
|
{
|
||||||
public unsafe static IMemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
|
public unsafe static MemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> outputMemory = ByteMemoryPool.Rent(data.Length);
|
MemoryOwner<byte> outputMemory = MemoryOwner<byte>.Rent(data.Length);
|
||||||
|
|
||||||
Span<byte> output = outputMemory.Memory.Span;
|
Span<byte> output = outputMemory.Span;
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|
||||||
|
@ -63,5 +63,9 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,5 +48,9 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
|
internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.Levels;
|
||||||
|
|
||||||
switch (Info.Target)
|
switch (Info.Target)
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
pixelInternalFormat = format.PixelInternalFormat;
|
pixelInternalFormat = format.PixelInternalFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.Levels;
|
||||||
|
|
||||||
GL.TextureView(
|
GL.TextureView(
|
||||||
Handle,
|
Handle,
|
||||||
@ -267,7 +267,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
public unsafe PinnedSpan<byte> GetData()
|
public unsafe PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.Levels;
|
||||||
|
|
||||||
for (int level = 0; level < levels; level++)
|
for (int level = 0; level < levels; level++)
|
||||||
{
|
{
|
||||||
@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
faces = 6;
|
faces = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.Levels;
|
||||||
|
|
||||||
for (int level = 0; level < levels; level++)
|
for (int level = 0; level < levels; level++)
|
||||||
{
|
{
|
||||||
@ -716,7 +716,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
int width = Info.Width;
|
int width = Info.Width;
|
||||||
int height = Info.Height;
|
int height = Info.Height;
|
||||||
int depth = Info.Depth;
|
int depth = Info.Depth;
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.Levels;
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
|
@ -98,11 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Logger = parameters.Logger;
|
Logger = parameters.Logger;
|
||||||
TargetApi = parameters.TargetApi;
|
TargetApi = parameters.TargetApi;
|
||||||
|
|
||||||
AddCapability(Capability.Shader);
|
|
||||||
AddCapability(Capability.Float64);
|
|
||||||
|
|
||||||
SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450);
|
|
||||||
|
|
||||||
Delegates = new SpirvDelegates(this);
|
Delegates = new SpirvDelegates(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
CodeGenContext context = new(info, parameters, instPool, integerPool);
|
CodeGenContext context = new(info, parameters, instPool, integerPool);
|
||||||
|
|
||||||
|
context.AddCapability(Capability.Shader);
|
||||||
|
|
||||||
|
context.SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450);
|
||||||
|
|
||||||
context.AddCapability(Capability.GroupNonUniformBallot);
|
context.AddCapability(Capability.GroupNonUniformBallot);
|
||||||
context.AddCapability(Capability.GroupNonUniformShuffle);
|
context.AddCapability(Capability.GroupNonUniformShuffle);
|
||||||
context.AddCapability(Capability.GroupNonUniformVote);
|
context.AddCapability(Capability.GroupNonUniformVote);
|
||||||
@ -51,6 +55,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.AddCapability(Capability.ImageQuery);
|
context.AddCapability(Capability.ImageQuery);
|
||||||
context.AddCapability(Capability.SampledBuffer);
|
context.AddCapability(Capability.SampledBuffer);
|
||||||
|
|
||||||
|
if (parameters.HostCapabilities.SupportsShaderFloat64)
|
||||||
|
{
|
||||||
|
context.AddCapability(Capability.Float64);
|
||||||
|
}
|
||||||
|
|
||||||
if (parameters.Definitions.TransformFeedbackEnabled && parameters.Definitions.LastInVertexPipeline)
|
if (parameters.Definitions.TransformFeedbackEnabled && parameters.Definitions.LastInVertexPipeline)
|
||||||
{
|
{
|
||||||
context.AddCapability(Capability.TransformFeedback);
|
context.AddCapability(Capability.TransformFeedback);
|
||||||
@ -58,7 +67,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
if (parameters.Definitions.Stage == ShaderStage.Fragment)
|
if (parameters.Definitions.Stage == ShaderStage.Fragment)
|
||||||
{
|
{
|
||||||
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)))
|
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)) ||
|
||||||
|
context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.PrimitiveId)))
|
||||||
{
|
{
|
||||||
context.AddCapability(Capability.Geometry);
|
context.AddCapability(Capability.Geometry);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public readonly bool SupportsGeometryShaderPassthrough;
|
public readonly bool SupportsGeometryShaderPassthrough;
|
||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
public readonly bool SupportsShaderBarrierDivergence;
|
public readonly bool SupportsShaderBarrierDivergence;
|
||||||
|
public readonly bool SupportsShaderFloat64;
|
||||||
public readonly bool SupportsTextureShadowLod;
|
public readonly bool SupportsTextureShadowLod;
|
||||||
public readonly bool SupportsViewportMask;
|
public readonly bool SupportsViewportMask;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
bool supportsGeometryShaderPassthrough,
|
bool supportsGeometryShaderPassthrough,
|
||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
bool supportsShaderBarrierDivergence,
|
bool supportsShaderBarrierDivergence,
|
||||||
|
bool supportsShaderFloat64,
|
||||||
bool supportsTextureShadowLod,
|
bool supportsTextureShadowLod,
|
||||||
bool supportsViewportMask)
|
bool supportsViewportMask)
|
||||||
{
|
{
|
||||||
@ -27,6 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||||
|
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||||
SupportsViewportMask = supportsViewportMask;
|
SupportsViewportMask = supportsViewportMask;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// If we can't do bindless elimination, remove the texture operation.
|
// If we can't do bindless elimination, remove the texture operation.
|
||||||
// Set any destination variables to zero.
|
// Set any destination variables to zero.
|
||||||
|
|
||||||
|
string typeName = texOp.Inst.IsImage()
|
||||||
|
? texOp.Type.ToGlslImageType(texOp.Format.GetComponentType())
|
||||||
|
: texOp.Type.ToGlslTextureType();
|
||||||
|
|
||||||
|
gpuAccessor.Log($"Failed to find handle source for bindless access of type \"{typeName}\".");
|
||||||
|
|
||||||
for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++)
|
for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++)
|
||||||
{
|
{
|
||||||
block.Operations.AddBefore(node, new Operation(Instruction.Copy, texOp.GetDest(destIndex), OperandHelper.Const(0)));
|
block.Operations.AddBefore(node, new Operation(Instruction.Copy, texOp.GetDest(destIndex), OperandHelper.Const(0)));
|
||||||
@ -62,17 +68,22 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand nvHandle = texOp.GetSource(0);
|
Operand bindlessHandle = texOp.GetSource(0);
|
||||||
|
|
||||||
if (nvHandle.AsgOp is not Operation handleOp ||
|
if (bindlessHandle.AsgOp is PhiNode phi)
|
||||||
handleOp.Inst != Instruction.Load ||
|
|
||||||
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
|
|
||||||
{
|
{
|
||||||
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
|
for (int srcIndex = 0; srcIndex < phi.SourcesCount; srcIndex++)
|
||||||
// This is an artificial limitation to prevent it from being used in cases where it
|
{
|
||||||
// would have a large performance impact of loading all textures in the pool.
|
Operand phiSource = phi.GetSource(srcIndex);
|
||||||
// It might be removed in the future, if we can mitigate the performance impact.
|
|
||||||
|
|
||||||
|
if (phiSource.AsgOp is not PhiNode && !IsBindlessAccessAllowed(phiSource))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!IsBindlessAccessAllowed(bindlessHandle))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,8 +91,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
Operand samplerHandle = OperandHelper.Local();
|
Operand samplerHandle = OperandHelper.Local();
|
||||||
Operand textureIndex = OperandHelper.Local();
|
Operand textureIndex = OperandHelper.Local();
|
||||||
|
|
||||||
block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff)));
|
block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, bindlessHandle, OperandHelper.Const(0xfffff)));
|
||||||
block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20)));
|
block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, bindlessHandle, OperandHelper.Const(20)));
|
||||||
|
|
||||||
int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
|
int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
|
||||||
|
|
||||||
@ -130,6 +141,30 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsBindlessAccessAllowed(Operand bindlessHandle)
|
||||||
|
{
|
||||||
|
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||||
|
{
|
||||||
|
// Bindless access with handles from constant buffer is allowed.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindlessHandle.AsgOp is not Operation handleOp ||
|
||||||
|
handleOp.Inst != Instruction.Load ||
|
||||||
|
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
|
||||||
|
{
|
||||||
|
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
|
||||||
|
// This is an artificial limitation to prevent it from being used in cases where it
|
||||||
|
// would have a large performance impact of loading all textures in the pool.
|
||||||
|
// It might be removed in the future, if we can mitigate the performance impact.
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
|
private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
|
||||||
{
|
{
|
||||||
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
||||||
@ -265,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,9 +155,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
|
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operation.Dest != null && operation.Dest.Type == OperandType.Register)
|
for (int dstIndex = 0; dstIndex < operation.DestsCount; dstIndex++)
|
||||||
{
|
{
|
||||||
localOutputs[block.Index] |= GetMask(operation.Dest.GetRegister());
|
Operand dest = operation.GetDest(dstIndex);
|
||||||
|
|
||||||
|
if (dest != null && dest.Type == OperandType.Register)
|
||||||
|
{
|
||||||
|
localOutputs[block.Index] |= GetMask(dest.GetRegister());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,6 +363,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(),
|
GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(),
|
||||||
GpuAccessor.QueryHostSupportsShaderBallot(),
|
GpuAccessor.QueryHostSupportsShaderBallot(),
|
||||||
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
|
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
|
||||||
|
GpuAccessor.QueryHostSupportsShaderFloat64(),
|
||||||
GpuAccessor.QueryHostSupportsTextureShadowLod(),
|
GpuAccessor.QueryHostSupportsTextureShadowLod(),
|
||||||
GpuAccessor.QueryHostSupportsViewportMask());
|
GpuAccessor.QueryHostSupportsViewportMask());
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@ -293,9 +292,9 @@ namespace Ryujinx.Graphics.Texture.Astc
|
|||||||
int depth,
|
int depth,
|
||||||
int levels,
|
int levels,
|
||||||
int layers,
|
int layers,
|
||||||
out IMemoryOwner<byte> decoded)
|
out MemoryOwner<byte> decoded)
|
||||||
{
|
{
|
||||||
decoded = ByteMemoryPool.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
|
decoded = MemoryOwner<byte>.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
|
||||||
|
|
||||||
AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);
|
AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
private const int BlockWidth = 4;
|
private const int BlockWidth = 4;
|
||||||
private const int BlockHeight = 4;
|
private const int BlockHeight = 4;
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -23,12 +22,12 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
||||||
|
|
||||||
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
||||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
||||||
|
|
||||||
@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -111,12 +110,12 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
||||||
|
|
||||||
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
||||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
||||||
|
|
||||||
@ -197,7 +196,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -206,13 +205,13 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
|
||||||
Span<byte> rPal = stackalloc byte[8];
|
Span<byte> rPal = stackalloc byte[8];
|
||||||
|
|
||||||
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
|
||||||
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
|
||||||
|
|
||||||
@ -294,7 +293,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
public static MemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -306,8 +305,8 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
|
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
|
||||||
int alignedWidth = BitUtils.AlignUp(width, 4);
|
int alignedWidth = BitUtils.AlignUp(width, 4);
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
Span<byte> outputSpan = output.Memory.Span;
|
Span<byte> outputSpan = output.Span;
|
||||||
|
|
||||||
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
|
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
|
||||||
|
|
||||||
@ -402,7 +401,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
public static MemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -414,7 +413,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
|
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
|
||||||
int alignedWidth = BitUtils.AlignUp(width, 2);
|
int alignedWidth = BitUtils.AlignUp(width, 2);
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
|
|
||||||
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
|
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
|
||||||
|
|
||||||
@ -423,7 +422,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
Span<byte> rPal = stackalloc byte[8];
|
Span<byte> rPal = stackalloc byte[8];
|
||||||
Span<byte> gPal = stackalloc byte[8];
|
Span<byte> gPal = stackalloc byte[8];
|
||||||
|
|
||||||
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
|
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Span);
|
||||||
|
|
||||||
Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
|
Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
|
||||||
Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
|
Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
|
||||||
@ -527,7 +526,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
public static MemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -536,8 +535,8 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
|
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
Span<byte> outputSpan = output.Memory.Span;
|
Span<byte> outputSpan = output.Span;
|
||||||
|
|
||||||
int inputOffset = 0;
|
int inputOffset = 0;
|
||||||
int outputOffset = 0;
|
int outputOffset = 0;
|
||||||
@ -566,7 +565,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -575,8 +574,8 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
Span<byte> outputSpan = output.Memory.Span;
|
Span<byte> outputSpan = output.Span;
|
||||||
|
|
||||||
int inputOffset = 0;
|
int inputOffset = 0;
|
||||||
int outputOffset = 0;
|
int outputOffset = 0;
|
||||||
|
@ -2,7 +2,6 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Texture.Encoders;
|
using Ryujinx.Graphics.Texture.Encoders;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Texture
|
namespace Ryujinx.Graphics.Texture
|
||||||
{
|
{
|
||||||
@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
private const int BlockWidth = 4;
|
private const int BlockWidth = 4;
|
||||||
private const int BlockHeight = 4;
|
private const int BlockHeight = 4;
|
||||||
|
|
||||||
public static IMemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
size += w * h * 16 * Math.Max(1, depth >> l) * layers;
|
size += w * h * 16 * Math.Max(1, depth >> l) * layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
|
||||||
Memory<byte> outputMemory = output.Memory;
|
Memory<byte> outputMemory = output.Memory;
|
||||||
|
|
||||||
int imageBaseIOffs = 0;
|
int imageBaseIOffs = 0;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@ -51,15 +50,15 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
|
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
||||||
|
|
||||||
int inputOffset = 0;
|
int inputOffset = 0;
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||||
|
|
||||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
||||||
|
|
||||||
int imageBaseOOffs = 0;
|
int imageBaseOOffs = 0;
|
||||||
@ -113,15 +112,15 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
||||||
|
|
||||||
int inputOffset = 0;
|
int inputOffset = 0;
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||||
|
|
||||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
||||||
|
|
||||||
int imageBaseOOffs = 0;
|
int imageBaseOOffs = 0;
|
||||||
@ -170,15 +169,15 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
public static MemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
|
||||||
|
|
||||||
int inputOffset = 0;
|
int inputOffset = 0;
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
|
||||||
|
|
||||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
|
||||||
|
|
||||||
int imageBaseOOffs = 0;
|
int imageBaseOOffs = 0;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
|
||||||
|
|
||||||
@ -95,7 +94,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ConvertBlockLinearToLinear(
|
public static MemoryOwner<byte> ConvertBlockLinearToLinear(
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int depth,
|
int depth,
|
||||||
@ -121,8 +120,8 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
blockHeight,
|
blockHeight,
|
||||||
bytesPerPixel);
|
bytesPerPixel);
|
||||||
|
|
||||||
IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(outSize);
|
MemoryOwner<byte> outputOwner = MemoryOwner<byte>.Rent(outSize);
|
||||||
Span<byte> output = outputOwner.Memory.Span;
|
Span<byte> output = outputOwner.Span;
|
||||||
|
|
||||||
int outOffs = 0;
|
int outOffs = 0;
|
||||||
|
|
||||||
@ -249,7 +248,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return outputOwner;
|
return outputOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ConvertLinearStridedToLinear(
|
public static MemoryOwner<byte> ConvertLinearStridedToLinear(
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int blockWidth,
|
int blockWidth,
|
||||||
@ -265,8 +264,8 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
|
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
|
||||||
lineSize = Math.Min(lineSize, outStride);
|
lineSize = Math.Min(lineSize, outStride);
|
||||||
|
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(h * outStride);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(h * outStride);
|
||||||
Span<byte> outSpan = output.Memory.Span;
|
Span<byte> outSpan = output.Span;
|
||||||
|
|
||||||
int outOffs = 0;
|
int outOffs = 0;
|
||||||
int inOffs = 0;
|
int inOffs = 0;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
@ -21,13 +20,14 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return (remainder, outRemainder, length / stride);
|
return (remainder, outRemainder, length / stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static IMemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
|
public unsafe static MemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||||
|
Span<byte> outputSpan = output.Span;
|
||||||
|
|
||||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
|
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
|
||||||
|
|
||||||
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
|
Span<ushort> outputSpanUInt16 = MemoryMarshal.Cast<byte, ushort>(outputSpan);
|
||||||
|
|
||||||
if (remainder == 0)
|
if (remainder == 0)
|
||||||
{
|
{
|
||||||
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
int sizeTrunc = data.Length & ~7;
|
int sizeTrunc = data.Length & ~7;
|
||||||
start = sizeTrunc;
|
start = sizeTrunc;
|
||||||
|
|
||||||
fixed (byte* inputPtr = data, outputPtr = output.Memory.Span)
|
fixed (byte* inputPtr = data, outputPtr = outputSpan)
|
||||||
{
|
{
|
||||||
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
|
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
|
||||||
{
|
{
|
||||||
@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
|
|
||||||
for (int i = start; i < data.Length; i++)
|
for (int i = start; i < data.Length; i++)
|
||||||
{
|
{
|
||||||
outputSpan[i] = data[i];
|
outputSpanUInt16[i] = data[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
{
|
{
|
||||||
for (int x = 0; x < width; x++)
|
for (int x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
outputSpan[outOffset++] = data[offset++];
|
outputSpanUInt16[outOffset++] = data[offset++];
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += remainder;
|
offset += remainder;
|
||||||
@ -72,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
public static MemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int outOffset = 0;
|
int outOffset = 0;
|
||||||
|
|
||||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||||
|
|
||||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
@ -109,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
|
public static MemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int outOffset = 0;
|
int outOffset = 0;
|
||||||
|
|
||||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||||
|
|
||||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
@ -146,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
public static MemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int outOffset = 0;
|
int outOffset = 0;
|
||||||
|
|
||||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||||
|
|
||||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
@ -183,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
public static MemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
|
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int outOffset = 0;
|
int outOffset = 0;
|
||||||
|
|
||||||
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
|
||||||
|
|
||||||
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
|
||||||
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
|
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
|
@ -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.IsQualcommProprietary,
|
||||||
|
isLight: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@ -8,22 +9,64 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
private const int MaxBarriersPerCall = 16;
|
private const int MaxBarriersPerCall = 16;
|
||||||
|
|
||||||
|
private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit;
|
||||||
|
private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit;
|
||||||
|
private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit;
|
||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
|
|
||||||
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
|
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
|
||||||
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
|
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
|
||||||
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
|
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
|
||||||
|
|
||||||
private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new();
|
private readonly List<BarrierWithStageFlags<MemoryBarrier, int>> _memoryBarriers = new();
|
||||||
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new();
|
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier, int>> _bufferBarriers = new();
|
||||||
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new();
|
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage>> _imageBarriers = new();
|
||||||
private int _queuedBarrierCount;
|
private int _queuedBarrierCount;
|
||||||
|
|
||||||
|
private enum IncoherentBarrierType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Texture,
|
||||||
|
All,
|
||||||
|
CommandBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
private PipelineStageFlags _incoherentBufferWriteStages;
|
||||||
|
private PipelineStageFlags _incoherentTextureWriteStages;
|
||||||
|
private PipelineStageFlags _extraStages;
|
||||||
|
private IncoherentBarrierType _queuedIncoherentBarrier;
|
||||||
|
|
||||||
public BarrierBatch(VulkanRenderer gd)
|
public BarrierBatch(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
|
||||||
|
{
|
||||||
|
AccessFlags access = BufferAccess;
|
||||||
|
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
|
||||||
|
|
||||||
|
if (gd.TransformFeedbackApi != null)
|
||||||
|
{
|
||||||
|
access |= AccessFlags.TransformFeedbackWriteBitExt;
|
||||||
|
stages |= PipelineStageFlags.TransformFeedbackBitExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gd.IsTBDR)
|
||||||
|
{
|
||||||
|
// Desktop GPUs can transform image barriers into memory barriers.
|
||||||
|
|
||||||
|
access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
|
||||||
|
access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
|
||||||
|
|
||||||
|
stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
|
||||||
|
stages |= PipelineStageFlags.ColorAttachmentOutputBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (access, stages);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly record struct StageFlags : IEquatable<StageFlags>
|
private readonly record struct StageFlags : IEquatable<StageFlags>
|
||||||
{
|
{
|
||||||
public readonly PipelineStageFlags Source;
|
public readonly PipelineStageFlags Source;
|
||||||
@ -36,47 +79,130 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly struct BarrierWithStageFlags<T> where T : unmanaged
|
private readonly struct BarrierWithStageFlags<T, T2> where T : unmanaged
|
||||||
{
|
{
|
||||||
public readonly StageFlags Flags;
|
public readonly StageFlags Flags;
|
||||||
public readonly T Barrier;
|
public readonly T Barrier;
|
||||||
|
public readonly T2 Resource;
|
||||||
|
|
||||||
public BarrierWithStageFlags(StageFlags flags, T barrier)
|
public BarrierWithStageFlags(StageFlags flags, T barrier)
|
||||||
{
|
{
|
||||||
Flags = flags;
|
Flags = flags;
|
||||||
Barrier = barrier;
|
Barrier = barrier;
|
||||||
|
Resource = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
|
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource)
|
||||||
{
|
{
|
||||||
Flags = new StageFlags(srcStageFlags, dstStageFlags);
|
Flags = new StageFlags(srcStageFlags, dstStageFlags);
|
||||||
Barrier = barrier;
|
Barrier = barrier;
|
||||||
|
Resource = resource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
|
private void QueueBarrier<T, T2>(List<BarrierWithStageFlags<T, T2>> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
|
||||||
{
|
{
|
||||||
list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
|
list.Add(new BarrierWithStageFlags<T, T2>(srcStageFlags, dstStageFlags, barrier, resource));
|
||||||
_queuedBarrierCount++;
|
_queuedBarrierCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
||||||
{
|
{
|
||||||
QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
|
QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
||||||
{
|
{
|
||||||
QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
|
QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
||||||
{
|
{
|
||||||
QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
|
QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass)
|
||||||
{
|
{
|
||||||
|
if (_queuedIncoherentBarrier > IncoherentBarrierType.None)
|
||||||
|
{
|
||||||
|
// We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier)
|
||||||
|
bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None;
|
||||||
|
bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None;
|
||||||
|
bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture;
|
||||||
|
|
||||||
|
if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite))
|
||||||
|
{
|
||||||
|
AccessFlags access = BaseAccess;
|
||||||
|
|
||||||
|
PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit;
|
||||||
|
|
||||||
|
if (hasBufferBarrier && hasBufferWrite)
|
||||||
|
{
|
||||||
|
access |= BufferAccess;
|
||||||
|
|
||||||
|
if (_gd.TransformFeedbackApi != null)
|
||||||
|
{
|
||||||
|
access |= AccessFlags.TransformFeedbackWriteBitExt;
|
||||||
|
stages |= PipelineStageFlags.TransformFeedbackBitExt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer)
|
||||||
|
{
|
||||||
|
access |= CommandBufferAccess;
|
||||||
|
stages |= PipelineStageFlags.DrawIndirectBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBarrier barrier = new MemoryBarrier()
|
||||||
|
{
|
||||||
|
SType = StructureType.MemoryBarrier,
|
||||||
|
SrcAccessMask = access,
|
||||||
|
DstAccessMask = access
|
||||||
|
};
|
||||||
|
|
||||||
|
QueueBarrier(barrier, stages, stages);
|
||||||
|
|
||||||
|
_incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None;
|
||||||
|
|
||||||
|
if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture)
|
||||||
|
{
|
||||||
|
if (program != null)
|
||||||
|
{
|
||||||
|
_incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_incoherentBufferWriteStages = PipelineStageFlags.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_queuedIncoherentBarrier = IncoherentBarrierType.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||||
|
{
|
||||||
|
Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
|
||||||
|
{
|
||||||
|
if (program != null)
|
||||||
|
{
|
||||||
|
_incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages;
|
||||||
|
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlushMemoryBarrier(program, inRenderPass);
|
||||||
|
|
||||||
|
if (!inRenderPass && rpHolder != null)
|
||||||
|
{
|
||||||
|
// Render pass is about to begin. Queue any fences that normally interrupt the pass.
|
||||||
|
rpHolder.InsertForcedFences(cbs);
|
||||||
|
}
|
||||||
|
|
||||||
while (_queuedBarrierCount > 0)
|
while (_queuedBarrierCount > 0)
|
||||||
{
|
{
|
||||||
int memoryCount = 0;
|
int memoryCount = 0;
|
||||||
@ -86,20 +212,20 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool hasBarrier = false;
|
bool hasBarrier = false;
|
||||||
StageFlags flags = default;
|
StageFlags flags = default;
|
||||||
|
|
||||||
static void AddBarriers<T>(
|
static void AddBarriers<T, T2>(
|
||||||
Span<T> target,
|
Span<T> target,
|
||||||
ref int queuedBarrierCount,
|
ref int queuedBarrierCount,
|
||||||
ref bool hasBarrier,
|
ref bool hasBarrier,
|
||||||
ref StageFlags flags,
|
ref StageFlags flags,
|
||||||
ref int count,
|
ref int count,
|
||||||
List<BarrierWithStageFlags<T>> list) where T : unmanaged
|
List<BarrierWithStageFlags<T, T2>> list) where T : unmanaged
|
||||||
{
|
{
|
||||||
int firstMatch = -1;
|
int firstMatch = -1;
|
||||||
int end = list.Count;
|
int end = list.Count;
|
||||||
|
|
||||||
for (int i = 0; i < list.Count; i++)
|
for (int i = 0; i < list.Count; i++)
|
||||||
{
|
{
|
||||||
BarrierWithStageFlags<T> barrier = list[i];
|
BarrierWithStageFlags<T, T2> barrier = list[i];
|
||||||
|
|
||||||
if (!hasBarrier)
|
if (!hasBarrier)
|
||||||
{
|
{
|
||||||
@ -162,21 +288,60 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insideRenderPass)
|
if (inRenderPass && _imageBarriers.Count > 0)
|
||||||
{
|
{
|
||||||
// Image barriers queued in the batch are meant to be globally scoped,
|
// Image barriers queued in the batch are meant to be globally scoped,
|
||||||
// but inside a render pass they're scoped to just the range of the render pass.
|
// but inside a render pass they're scoped to just the range of the render pass.
|
||||||
|
|
||||||
// On MoltenVK, we just break the rules and always use image barrier.
|
// On MoltenVK, we just break the rules and always use image barrier.
|
||||||
// On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
|
// On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
|
||||||
// TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done,
|
// Generally, we want to avoid this from happening in the future, so flag the texture to immediately
|
||||||
// notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future.
|
// emit a barrier whenever the current render pass is bound again.
|
||||||
|
|
||||||
if (!_gd.IsMoltenVk)
|
bool anyIsNonAttachment = false;
|
||||||
|
|
||||||
|
foreach (BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage> barrier in _imageBarriers)
|
||||||
{
|
{
|
||||||
|
// If the binding is an attachment, don't add it as a forced fence.
|
||||||
|
bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource);
|
||||||
|
|
||||||
|
if (!isAttachment)
|
||||||
|
{
|
||||||
|
rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest);
|
||||||
|
anyIsNonAttachment = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_gd.IsTBDR)
|
||||||
|
{
|
||||||
|
if (!_gd.IsMoltenVk)
|
||||||
|
{
|
||||||
|
if (!anyIsNonAttachment)
|
||||||
|
{
|
||||||
|
// This case is a feedback loop. To prevent this from causing an absolute performance disaster,
|
||||||
|
// remove the barriers entirely.
|
||||||
|
// If this is not here, there will be a lot of single draw render passes.
|
||||||
|
// TODO: explicit handling for feedback loops, likely outside this class.
|
||||||
|
|
||||||
|
_queuedBarrierCount -= _imageBarriers.Count;
|
||||||
|
_imageBarriers.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
|
||||||
|
// Metal already has hazard tracking so MVK doesn't need this.
|
||||||
|
endRenderPass();
|
||||||
|
inRenderPass = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generic pipeline memory barriers will work for desktop GPUs.
|
||||||
|
// They do require a few more access flags on the subpass dependency, though.
|
||||||
foreach (var barrier in _imageBarriers)
|
foreach (var barrier in _imageBarriers)
|
||||||
{
|
{
|
||||||
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
|
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
|
||||||
barrier.Flags,
|
barrier.Flags,
|
||||||
new MemoryBarrier()
|
new MemoryBarrier()
|
||||||
{
|
{
|
||||||
@ -190,6 +355,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inRenderPass && _memoryBarriers.Count > 0)
|
||||||
|
{
|
||||||
|
PipelineStageFlags allFlags = PipelineStageFlags.None;
|
||||||
|
|
||||||
|
foreach (var barrier in _memoryBarriers)
|
||||||
|
{
|
||||||
|
allFlags |= barrier.Flags.Dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags))
|
||||||
|
{
|
||||||
|
endRenderPass();
|
||||||
|
inRenderPass = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
|
AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
|
||||||
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
|
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
|
||||||
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
|
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
|
||||||
@ -198,14 +379,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
PipelineStageFlags srcStageFlags = flags.Source;
|
PipelineStageFlags srcStageFlags = flags.Source;
|
||||||
|
|
||||||
if (insideRenderPass)
|
if (inRenderPass)
|
||||||
{
|
{
|
||||||
// Inside a render pass, barrier stages can only be from rasterization.
|
// Inside a render pass, barrier stages can only be from rasterization.
|
||||||
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
|
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
_gd.Api.CmdPipelineBarrier(
|
_gd.Api.CmdPipelineBarrier(
|
||||||
cb,
|
cbs.CommandBuffer,
|
||||||
srcStageFlags,
|
srcStageFlags,
|
||||||
flags.Dest,
|
flags.Dest,
|
||||||
0,
|
0,
|
||||||
@ -219,6 +400,41 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void QueueIncoherentBarrier(IncoherentBarrierType type)
|
||||||
|
{
|
||||||
|
if (type > _queuedIncoherentBarrier)
|
||||||
|
{
|
||||||
|
_queuedIncoherentBarrier = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueTextureBarrier()
|
||||||
|
{
|
||||||
|
QueueIncoherentBarrier(IncoherentBarrierType.Texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueMemoryBarrier()
|
||||||
|
{
|
||||||
|
QueueIncoherentBarrier(IncoherentBarrierType.All);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueCommandBufferBarrier()
|
||||||
|
{
|
||||||
|
QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnableTfbBarriers(bool enable)
|
||||||
|
{
|
||||||
|
if (enable)
|
||||||
|
{
|
||||||
|
_extraStages |= PipelineStageFlags.TransformFeedbackBitExt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_memoryBarrierBatch.Dispose();
|
_memoryBarrierBatch.Dispose();
|
||||||
|
@ -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;
|
||||||
|
|
||||||
@ -30,11 +31,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public int SubmissionCount;
|
public int SubmissionCount;
|
||||||
public CommandBuffer CommandBuffer;
|
public CommandBuffer CommandBuffer;
|
||||||
public FenceHolder Fence;
|
public FenceHolder Fence;
|
||||||
public SemaphoreHolder Semaphore;
|
|
||||||
|
|
||||||
public List<IAuto> Dependants;
|
public List<IAuto> Dependants;
|
||||||
public List<MultiFenceHolder> Waitables;
|
public List<MultiFenceHolder> Waitables;
|
||||||
public HashSet<SemaphoreHolder> Dependencies;
|
|
||||||
|
|
||||||
public void Initialize(Vk api, Device device, CommandPool pool)
|
public void Initialize(Vk api, Device device, CommandPool pool)
|
||||||
{
|
{
|
||||||
@ -50,7 +49,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Dependants = new List<IAuto>();
|
Dependants = new List<IAuto>();
|
||||||
Waitables = new List<MultiFenceHolder>();
|
Waitables = new List<MultiFenceHolder>();
|
||||||
Dependencies = new HashSet<SemaphoreHolder>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,12 +59,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
|
||||||
@ -134,14 +140,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
|
|
||||||
{
|
|
||||||
Debug.Assert(_commandBuffers[cbIndex].InUse);
|
|
||||||
var semaphoreHolder = _commandBuffers[dependencyCbs.CommandBufferIndex].Semaphore;
|
|
||||||
semaphoreHolder.Get();
|
|
||||||
_commandBuffers[cbIndex].Dependencies.Add(semaphoreHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
|
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
|
||||||
{
|
{
|
||||||
ref var entry = ref _commandBuffers[cbIndex];
|
ref var entry = ref _commandBuffers[cbIndex];
|
||||||
@ -345,19 +343,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
waitable.RemoveBufferUses(cbIndex);
|
waitable.RemoveBufferUses(cbIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var dependency in entry.Dependencies)
|
|
||||||
{
|
|
||||||
dependency.Put();
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Dependants.Clear();
|
entry.Dependants.Clear();
|
||||||
entry.Waitables.Clear();
|
entry.Waitables.Clear();
|
||||||
entry.Dependencies.Clear();
|
|
||||||
entry.Fence?.Dispose();
|
entry.Fence?.Dispose();
|
||||||
|
|
||||||
if (refreshFence)
|
if (refreshFence)
|
||||||
{
|
{
|
||||||
entry.Fence = new FenceHolder(_api, _device);
|
entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -26,11 +26,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_pool.AddWaitable(CommandBufferIndex, waitable);
|
_pool.AddWaitable(CommandBufferIndex, waitable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddDependency(CommandBufferScoped dependencyCbs)
|
|
||||||
{
|
|
||||||
_pool.AddDependency(CommandBufferIndex, dependencyCbs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FenceHolder GetFence()
|
public FenceHolder GetFence()
|
||||||
{
|
{
|
||||||
return _pool.GetFence(CommandBufferIndex);
|
return _pool.GetFence(CommandBufferIndex);
|
||||||
|
@ -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.
|
||||||
@ -213,7 +211,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
IMemoryOwner<byte> dummyTextureData = ByteMemoryPool.RentCleared(4);
|
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
||||||
_dummyTexture.SetData(dummyTextureData);
|
_dummyTexture.SetData(dummyTextureData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,8 +289,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PipelineStageFlags stageFlags = _textureArrayRefs[segment.Binding].Stage.ConvertToPipelineStageFlags();
|
ref var arrayRef = ref _textureArrayRefs[segment.Binding];
|
||||||
_textureArrayRefs[segment.Binding].Array?.QueueWriteToReadBarriers(cbs, stageFlags);
|
PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags();
|
||||||
|
arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,8 +310,40 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PipelineStageFlags stageFlags = _imageArrayRefs[segment.Binding].Stage.ConvertToPipelineStageFlags();
|
ref var arrayRef = ref _imageArrayRefs[segment.Binding];
|
||||||
_imageArrayRefs[segment.Binding].Array?.QueueWriteToReadBarriers(cbs, stageFlags);
|
PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags();
|
||||||
|
arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < _program.BindingSegments.Length; setIndex++)
|
||||||
|
{
|
||||||
|
var bindingSegments = _program.BindingSegments[setIndex];
|
||||||
|
|
||||||
|
if (bindingSegments.Length == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceBindingSegment segment = bindingSegments[0];
|
||||||
|
|
||||||
|
if (segment.IsArray)
|
||||||
|
{
|
||||||
|
if (segment.Type == ResourceType.Texture ||
|
||||||
|
segment.Type == ResourceType.Sampler ||
|
||||||
|
segment.Type == ResourceType.TextureAndSampler ||
|
||||||
|
segment.Type == ResourceType.BufferTexture)
|
||||||
|
{
|
||||||
|
ref var arrayRef = ref _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts];
|
||||||
|
PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags();
|
||||||
|
arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags);
|
||||||
|
}
|
||||||
|
else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage)
|
||||||
|
{
|
||||||
|
ref var arrayRef = ref _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts];
|
||||||
|
PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags();
|
||||||
|
arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,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))
|
||||||
@ -885,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)]
|
||||||
|
@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var scalingResourceLayout = new ResourceLayoutBuilder()
|
var scalingResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
var sharpeningResourceLayout = new ResourceLayoutBuilder()
|
var sharpeningResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var resourceLayout = new ResourceLayoutBuilder()
|
var resourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var edgeResourceLayout = new ResourceLayoutBuilder()
|
var edgeResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
var blendResourceLayout = new ResourceLayoutBuilder()
|
var blendResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
var neighbourResourceLayout = new ResourceLayoutBuilder()
|
var neighbourResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -286,10 +286,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
|
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
|
||||||
|
|
||||||
gd.Barriers.Flush(cbs.CommandBuffer, false, null);
|
gd.Barriers.Flush(cbs, false, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
public void AddStoreOpUsage()
|
||||||
|
{
|
||||||
|
if (_colors != null)
|
||||||
|
{
|
||||||
|
foreach (var color in _colors)
|
||||||
|
{
|
||||||
|
color.Storage?.AddStoreOpUsage(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_depthStencil?.Storage?.AddStoreOpUsage(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
CommandBufferScoped cbs)
|
CommandBufferScoped cbs)
|
||||||
|
@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
|
||||||
|
|
||||||
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var colorCopyResourceLayout = new ResourceLayoutBuilder()
|
var colorCopyResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
||||||
|
|
||||||
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
|
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
|
||||||
|
|
||||||
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
|
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
|
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
|
||||||
|
|
||||||
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
|
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
|
||||||
|
|
||||||
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
|
@ -2,11 +2,10 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
class ImageArray : IImageArray
|
class ImageArray : ResourceArray, IImageArray
|
||||||
{
|
{
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
|
|
||||||
@ -25,19 +24,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private HashSet<TextureStorage> _storages;
|
private HashSet<TextureStorage> _storages;
|
||||||
|
|
||||||
private DescriptorSet[] _cachedDescriptorSets;
|
|
||||||
|
|
||||||
private int _cachedCommandBufferIndex;
|
private int _cachedCommandBufferIndex;
|
||||||
private int _cachedSubmissionCount;
|
private int _cachedSubmissionCount;
|
||||||
|
|
||||||
private ShaderCollection _cachedDscProgram;
|
|
||||||
private int _cachedDscSetIndex;
|
|
||||||
private int _cachedDscIndex;
|
|
||||||
|
|
||||||
private readonly bool _isBuffer;
|
private readonly bool _isBuffer;
|
||||||
|
|
||||||
private int _bindCount;
|
|
||||||
|
|
||||||
public ImageArray(VulkanRenderer gd, int size, bool isBuffer)
|
public ImageArray(VulkanRenderer gd, int size, bool isBuffer)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
@ -104,12 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_cachedCommandBufferIndex = -1;
|
_cachedCommandBufferIndex = -1;
|
||||||
_storages = null;
|
_storages = null;
|
||||||
_cachedDescriptorSets = null;
|
SetDirty(_gd);
|
||||||
|
|
||||||
if (_bindCount != 0)
|
|
||||||
{
|
|
||||||
_gd.PipelineInternal.ForceImageDirty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
|
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
|
||||||
@ -195,7 +181,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int setIndex,
|
int setIndex,
|
||||||
TextureView dummyTexture)
|
TextureView dummyTexture)
|
||||||
{
|
{
|
||||||
if (_cachedDescriptorSets != null)
|
if (TryGetCachedDescriptorSets(cbs, program, setIndex, out DescriptorSet[] sets))
|
||||||
{
|
{
|
||||||
// We still need to ensure the current command buffer holds a reference to all used textures.
|
// We still need to ensure the current command buffer holds a reference to all used textures.
|
||||||
|
|
||||||
@ -208,12 +194,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
GetBufferViews(cbs);
|
GetBufferViews(cbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _cachedDescriptorSets;
|
return sets;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cachedDscProgram?.ReleaseManualDescriptorSetCollection(_cachedDscSetIndex, _cachedDscIndex);
|
|
||||||
var dsc = program.GetNewManualDescriptorSetCollection(cbs.CommandBufferIndex, setIndex, out _cachedDscIndex).Get(cbs);
|
|
||||||
|
|
||||||
DescriptorSetTemplate template = program.Templates[setIndex];
|
DescriptorSetTemplate template = program.Templates[setIndex];
|
||||||
|
|
||||||
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
|
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
|
||||||
@ -227,24 +210,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
tu.Push(GetBufferViews(cbs));
|
tu.Push(GetBufferViews(cbs));
|
||||||
}
|
}
|
||||||
|
|
||||||
var sets = dsc.GetSets();
|
|
||||||
templateUpdater.Commit(_gd, device, sets[0]);
|
templateUpdater.Commit(_gd, device, sets[0]);
|
||||||
_cachedDescriptorSets = sets;
|
|
||||||
_cachedDscProgram = program;
|
|
||||||
_cachedDscSetIndex = setIndex;
|
|
||||||
|
|
||||||
return sets;
|
return sets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void IncrementBindCount()
|
|
||||||
{
|
|
||||||
_bindCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DecrementBindCount()
|
|
||||||
{
|
|
||||||
int newBindCount = --_bindCount;
|
|
||||||
Debug.Assert(newBindCount >= 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -165,14 +166,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
|
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
|
||||||
private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
|
private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
|
||||||
{
|
{
|
||||||
Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
using SpanOwner<FenceHolder> fenceHoldersOwner = SpanOwner<FenceHolder>.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||||
|
Span<FenceHolder> fenceHolders = fenceHoldersOwner.Span;
|
||||||
|
|
||||||
int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
|
int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
|
||||||
Span<Fence> fences = stackalloc Fence[count];
|
Span<Fence> fences = stackalloc Fence[count];
|
||||||
|
|
||||||
int fenceCount = 0;
|
int fenceCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < fences.Length; i++)
|
||||||
{
|
{
|
||||||
if (fenceHolders[i].TryGet(out Fence fence))
|
if (fenceHolders[i].TryGet(out Fence fence))
|
||||||
{
|
{
|
||||||
@ -194,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;
|
||||||
|
@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
protected FramebufferParams FramebufferParams;
|
protected FramebufferParams FramebufferParams;
|
||||||
private Auto<DisposableFramebuffer> _framebuffer;
|
private Auto<DisposableFramebuffer> _framebuffer;
|
||||||
|
private RenderPassHolder _rpHolder;
|
||||||
private Auto<DisposableRenderPass> _renderPass;
|
private Auto<DisposableRenderPass> _renderPass;
|
||||||
private RenderPassHolder _nullRenderPass;
|
private RenderPassHolder _nullRenderPass;
|
||||||
private int _writtenAttachmentCount;
|
private int _writtenAttachmentCount;
|
||||||
@ -85,8 +86,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private bool _tfActive;
|
private bool _tfActive;
|
||||||
|
|
||||||
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
|
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
|
||||||
|
|
||||||
private ulong _drawCountSinceBarrier;
|
|
||||||
public ulong DrawCount { get; private set; }
|
public ulong DrawCount { get; private set; }
|
||||||
public bool RenderPassActive { get; private set; }
|
public bool RenderPassActive { get; private set; }
|
||||||
|
|
||||||
@ -105,7 +104,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];
|
||||||
@ -135,48 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public unsafe void Barrier()
|
public unsafe void Barrier()
|
||||||
{
|
{
|
||||||
if (_drawCountSinceBarrier != DrawCount)
|
Gd.Barriers.QueueMemoryBarrier();
|
||||||
{
|
|
||||||
_drawCountSinceBarrier = DrawCount;
|
|
||||||
|
|
||||||
// Barriers are not supported inside a render pass on Apple GPUs.
|
|
||||||
// As a workaround, end the render pass.
|
|
||||||
if (Gd.Vendor == Vendor.Apple)
|
|
||||||
{
|
|
||||||
EndRenderPass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryBarrier memoryBarrier = new()
|
|
||||||
{
|
|
||||||
SType = StructureType.MemoryBarrier,
|
|
||||||
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
|
||||||
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
|
||||||
};
|
|
||||||
|
|
||||||
PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit;
|
|
||||||
|
|
||||||
if (Gd.Capabilities.SupportsGeometryShader)
|
|
||||||
{
|
|
||||||
pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Gd.Capabilities.SupportsTessellationShader)
|
|
||||||
{
|
|
||||||
pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gd.Api.CmdPipelineBarrier(
|
|
||||||
CommandBuffer,
|
|
||||||
pipelineStageFlags,
|
|
||||||
pipelineStageFlags,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
memoryBarrier,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ComputeBarrier()
|
public void ComputeBarrier()
|
||||||
@ -203,6 +161,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void BeginTransformFeedback(PrimitiveTopology topology)
|
public void BeginTransformFeedback(PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
|
Gd.Barriers.EnableTfbBarriers(true);
|
||||||
_tfEnabled = true;
|
_tfEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
CreateRenderPass();
|
CreateRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||||
|
|
||||||
BeginRenderPass();
|
BeginRenderPass();
|
||||||
|
|
||||||
@ -287,7 +246,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
CreateRenderPass();
|
CreateRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||||
|
|
||||||
BeginRenderPass();
|
BeginRenderPass();
|
||||||
|
|
||||||
@ -299,24 +258,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public unsafe void CommandBufferBarrier()
|
public unsafe void CommandBufferBarrier()
|
||||||
{
|
{
|
||||||
MemoryBarrier memoryBarrier = new()
|
Gd.Barriers.QueueCommandBufferBarrier();
|
||||||
{
|
|
||||||
SType = StructureType.MemoryBarrier,
|
|
||||||
SrcAccessMask = BufferHolder.DefaultAccessFlags,
|
|
||||||
DstAccessMask = AccessFlags.IndirectCommandReadBit,
|
|
||||||
};
|
|
||||||
|
|
||||||
Gd.Api.CmdPipelineBarrier(
|
|
||||||
CommandBuffer,
|
|
||||||
PipelineStageFlags.AllCommandsBit,
|
|
||||||
PipelineStageFlags.DrawIndirectBit,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
memoryBarrier,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
|
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
|
||||||
@ -722,6 +664,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void EndTransformFeedback()
|
public void EndTransformFeedback()
|
||||||
{
|
{
|
||||||
|
Gd.Barriers.EnableTfbBarriers(false);
|
||||||
PauseTransformFeedbackInternal();
|
PauseTransformFeedbackInternal();
|
||||||
_tfEnabled = false;
|
_tfEnabled = false;
|
||||||
}
|
}
|
||||||
@ -1020,6 +963,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_newState.RasterizerDiscardEnable = discard;
|
_newState.RasterizerDiscardEnable = discard;
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
|
|
||||||
|
if (!discard && Gd.IsQualcommProprietary)
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
@ -1401,24 +1351,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public unsafe void TextureBarrier()
|
public unsafe void TextureBarrier()
|
||||||
{
|
{
|
||||||
MemoryBarrier memoryBarrier = new()
|
Gd.Barriers.QueueTextureBarrier();
|
||||||
{
|
|
||||||
SType = StructureType.MemoryBarrier,
|
|
||||||
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
|
||||||
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
|
||||||
};
|
|
||||||
|
|
||||||
Gd.Api.CmdPipelineBarrier(
|
|
||||||
CommandBuffer,
|
|
||||||
PipelineStageFlags.FragmentShaderBit,
|
|
||||||
PipelineStageFlags.FragmentShaderBit,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
memoryBarrier,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TextureBarrierTiled()
|
public void TextureBarrierTiled()
|
||||||
@ -1525,12 +1458,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
// Use the null framebuffer.
|
// Use the null framebuffer.
|
||||||
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
|
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
|
||||||
|
|
||||||
|
_rpHolder = _nullRenderPass;
|
||||||
_renderPass = _nullRenderPass.GetRenderPass();
|
_renderPass = _nullRenderPass.GetRenderPass();
|
||||||
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
|
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
|
(_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
|
||||||
|
|
||||||
|
_renderPass = _rpHolder.GetRenderPass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1557,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||||
|
|
||||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
|
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
|
||||||
}
|
}
|
||||||
@ -1622,7 +1558,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
|
||||||
|
|
||||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
||||||
|
|
||||||
@ -1701,6 +1637,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (RenderPassActive)
|
if (RenderPassActive)
|
||||||
{
|
{
|
||||||
|
FramebufferParams.AddStoreOpUsage();
|
||||||
|
|
||||||
PauseTransformFeedbackInternal();
|
PauseTransformFeedbackInternal();
|
||||||
Gd.Api.CmdEndRenderPass(CommandBuffer);
|
Gd.Api.CmdEndRenderPass(CommandBuffer);
|
||||||
SignalRenderPassEnd();
|
SignalRenderPassEnd();
|
||||||
|
@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
static class PipelineConverter
|
static class PipelineConverter
|
||||||
{
|
{
|
||||||
private const AccessFlags SubpassAccessMask =
|
|
||||||
AccessFlags.MemoryReadBit |
|
|
||||||
AccessFlags.MemoryWriteBit |
|
|
||||||
AccessFlags.ShaderReadBit |
|
|
||||||
AccessFlags.ColorAttachmentWriteBit |
|
|
||||||
AccessFlags.DepthStencilAttachmentWriteBit;
|
|
||||||
|
|
||||||
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
|
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
const int MaxAttachments = Constants.MaxRenderTargets + 1;
|
const int MaxAttachments = Constants.MaxRenderTargets + 1;
|
||||||
@ -108,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var subpassDependency = CreateSubpassDependency();
|
var subpassDependency = CreateSubpassDependency(gd);
|
||||||
|
|
||||||
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
|
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
|
||||||
{
|
{
|
||||||
@ -129,29 +122,33 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SubpassDependency CreateSubpassDependency()
|
public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
|
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
|
||||||
|
|
||||||
return new SubpassDependency(
|
return new SubpassDependency(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
PipelineStageFlags.AllGraphicsBit,
|
stages,
|
||||||
PipelineStageFlags.AllGraphicsBit,
|
stages,
|
||||||
SubpassAccessMask,
|
access,
|
||||||
SubpassAccessMask,
|
access,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static SubpassDependency2 CreateSubpassDependency2()
|
public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
|
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
|
||||||
|
|
||||||
return new SubpassDependency2(
|
return new SubpassDependency2(
|
||||||
StructureType.SubpassDependency2,
|
StructureType.SubpassDependency2,
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
PipelineStageFlags.AllGraphicsBit,
|
stages,
|
||||||
PipelineStageFlags.AllGraphicsBit,
|
stages,
|
||||||
SubpassAccessMask,
|
access,
|
||||||
SubpassAccessMask,
|
access,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,9 +177,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pipeline.LogicOpEnable = state.LogicOpEnable;
|
pipeline.LogicOpEnable = state.LogicOpEnable;
|
||||||
pipeline.LogicOp = state.LogicOp.Convert();
|
pipeline.LogicOp = state.LogicOp.Convert();
|
||||||
|
|
||||||
pipeline.MinDepthBounds = 0f; // Not implemented.
|
|
||||||
pipeline.MaxDepthBounds = 0f; // Not implemented.
|
|
||||||
|
|
||||||
pipeline.PatchControlPoints = state.PatchControlPoints;
|
pipeline.PatchControlPoints = state.PatchControlPoints;
|
||||||
pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
|
pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
|
||||||
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
|
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
|
||||||
@ -208,17 +202,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
|
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
|
||||||
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
|
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
|
||||||
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
|
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
|
||||||
pipeline.StencilFrontCompareMask = 0;
|
|
||||||
pipeline.StencilFrontWriteMask = 0;
|
|
||||||
pipeline.StencilFrontReference = 0;
|
|
||||||
|
|
||||||
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
|
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
|
||||||
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
|
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
|
||||||
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
|
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
|
||||||
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
|
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
|
||||||
pipeline.StencilBackCompareMask = 0;
|
|
||||||
pipeline.StencilBackWriteMask = 0;
|
|
||||||
pipeline.StencilBackReference = 0;
|
|
||||||
|
|
||||||
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
|
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
|
||||||
|
|
||||||
|
@ -47,10 +47,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (componentMask != 0xf)
|
if (componentMask != 0xf || Gd.IsQualcommProprietary)
|
||||||
{
|
{
|
||||||
// We can't use CmdClearAttachments if not writing all components,
|
// We can't use CmdClearAttachments if not writing all components,
|
||||||
// because on Vulkan, the pipeline state does not affect clears.
|
// because on Vulkan, the pipeline state does not affect clears.
|
||||||
|
// On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all.
|
||||||
var dstTexture = FramebufferParams.GetColorView(index);
|
var dstTexture = FramebufferParams.GetColorView(index);
|
||||||
if (dstTexture == null)
|
if (dstTexture == null)
|
||||||
{
|
{
|
||||||
@ -87,10 +88,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stencilMask != 0 && stencilMask != 0xff)
|
if ((stencilMask != 0 && stencilMask != 0xff) || Gd.IsQualcommProprietary)
|
||||||
{
|
{
|
||||||
// We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits,
|
// We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits,
|
||||||
// because on Vulkan, the pipeline state does not affect clears.
|
// because on Vulkan, the pipeline state does not affect clears.
|
||||||
|
// On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all.
|
||||||
var dstTexture = FramebufferParams.GetDepthStencilView();
|
var dstTexture = FramebufferParams.GetDepthStencilView();
|
||||||
if (dstTexture == null)
|
if (dstTexture == null)
|
||||||
{
|
{
|
||||||
@ -255,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PreloadCbs = null;
|
PreloadCbs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
|
Gd.Barriers.Flush(Cbs, false, null, null);
|
||||||
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||||
Gd.RegisterFlush();
|
Gd.RegisterFlush();
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
@ -15,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
|
|
||||||
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
|
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
|
||||||
|
public bool[] DescriptorSetLayoutsUpdateAfterBind { get; }
|
||||||
public PipelineLayout PipelineLayout { get; }
|
public PipelineLayout PipelineLayout { get; }
|
||||||
|
|
||||||
private readonly int[] _consumedDescriptorsPerSet;
|
private readonly int[] _consumedDescriptorsPerSet;
|
||||||
@ -31,20 +33,37 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private struct ManualDescriptorSetEntry
|
private struct ManualDescriptorSetEntry
|
||||||
{
|
{
|
||||||
public Auto<DescriptorSetCollection> DescriptorSet;
|
public Auto<DescriptorSetCollection> DescriptorSet;
|
||||||
public int CbIndex;
|
public uint CbRefMask;
|
||||||
public int CbSubmissionCount;
|
|
||||||
public bool InUse;
|
public bool InUse;
|
||||||
|
|
||||||
public ManualDescriptorSetEntry(Auto<DescriptorSetCollection> descriptorSet, int cbIndex, int cbSubmissionCount, bool inUse)
|
public ManualDescriptorSetEntry(Auto<DescriptorSetCollection> descriptorSet, int cbIndex)
|
||||||
{
|
{
|
||||||
DescriptorSet = descriptorSet;
|
DescriptorSet = descriptorSet;
|
||||||
CbIndex = cbIndex;
|
CbRefMask = 1u << cbIndex;
|
||||||
CbSubmissionCount = cbSubmissionCount;
|
InUse = true;
|
||||||
InUse = inUse;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct PendingManualDsConsumption
|
||||||
|
{
|
||||||
|
public FenceHolder Fence { get; }
|
||||||
|
public int CommandBufferIndex { get; }
|
||||||
|
public int SetIndex { get; }
|
||||||
|
public int CacheIndex { get; }
|
||||||
|
|
||||||
|
public PendingManualDsConsumption(FenceHolder fence, int commandBufferIndex, int setIndex, int cacheIndex)
|
||||||
|
{
|
||||||
|
Fence = fence;
|
||||||
|
CommandBufferIndex = commandBufferIndex;
|
||||||
|
SetIndex = setIndex;
|
||||||
|
CacheIndex = cacheIndex;
|
||||||
|
fence.Get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<ManualDescriptorSetEntry>[] _manualDsCache;
|
private readonly List<ManualDescriptorSetEntry>[] _manualDsCache;
|
||||||
|
private readonly Queue<PendingManualDsConsumption> _pendingManualDsConsumptions;
|
||||||
|
private readonly Queue<int>[] _freeManualDsCacheEntries;
|
||||||
|
|
||||||
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
|
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
|
||||||
private readonly ResourceDescriptorCollection _pdDescriptors;
|
private readonly ResourceDescriptorCollection _pdDescriptors;
|
||||||
@ -70,6 +89,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_dsCacheCursor = new int[setsCount];
|
_dsCacheCursor = new int[setsCount];
|
||||||
_manualDsCache = new List<ManualDescriptorSetEntry>[setsCount];
|
_manualDsCache = new List<ManualDescriptorSetEntry>[setsCount];
|
||||||
|
_pendingManualDsConsumptions = new Queue<PendingManualDsConsumption>();
|
||||||
|
_freeManualDsCacheEntries = new Queue<int>[setsCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry(
|
public PipelineLayoutCacheEntry(
|
||||||
@ -78,7 +99,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||||
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
||||||
{
|
{
|
||||||
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
ResourceLayouts layouts = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||||
|
|
||||||
|
DescriptorSetLayouts = layouts.DescriptorSetLayouts;
|
||||||
|
DescriptorSetLayoutsUpdateAfterBind = layouts.DescriptorSetLayoutsUpdateAfterBind;
|
||||||
|
PipelineLayout = layouts.PipelineLayout;
|
||||||
|
|
||||||
_consumedDescriptorsPerSet = new int[setDescriptors.Count];
|
_consumedDescriptorsPerSet = new int[setDescriptors.Count];
|
||||||
_poolSizes = new DescriptorPoolSize[setDescriptors.Count][];
|
_poolSizes = new DescriptorPoolSize[setDescriptors.Count][];
|
||||||
@ -133,7 +158,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_poolSizes[setIndex],
|
_poolSizes[setIndex],
|
||||||
setIndex,
|
setIndex,
|
||||||
_consumedDescriptorsPerSet[setIndex],
|
_consumedDescriptorsPerSet[setIndex],
|
||||||
false);
|
DescriptorSetLayoutsUpdateAfterBind[setIndex]);
|
||||||
|
|
||||||
list.Add(dsc);
|
list.Add(dsc);
|
||||||
isNew = true;
|
isNew = true;
|
||||||
@ -144,49 +169,99 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return list[index];
|
return list[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(int commandBufferIndex, int setIndex, out int cacheIndex)
|
public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(CommandBufferScoped cbs, int setIndex, out int cacheIndex)
|
||||||
{
|
{
|
||||||
int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex);
|
FreeCompletedManualDescriptorSets();
|
||||||
|
|
||||||
var list = _manualDsCache[setIndex] ??= new();
|
var list = _manualDsCache[setIndex] ??= new();
|
||||||
var span = CollectionsMarshal.AsSpan(list);
|
var span = CollectionsMarshal.AsSpan(list);
|
||||||
|
|
||||||
for (int index = 0; index < span.Length; index++)
|
Queue<int> freeQueue = _freeManualDsCacheEntries[setIndex];
|
||||||
|
|
||||||
|
// Do we have at least one freed descriptor set? If so, just use that.
|
||||||
|
if (freeQueue != null && freeQueue.TryDequeue(out int freeIndex))
|
||||||
{
|
{
|
||||||
ref ManualDescriptorSetEntry entry = ref span[index];
|
ref ManualDescriptorSetEntry entry = ref span[freeIndex];
|
||||||
|
|
||||||
if (!entry.InUse && (entry.CbIndex != commandBufferIndex || entry.CbSubmissionCount != submissionCount))
|
Debug.Assert(!entry.InUse && entry.CbRefMask == 0);
|
||||||
{
|
|
||||||
entry.InUse = true;
|
|
||||||
entry.CbIndex = commandBufferIndex;
|
|
||||||
entry.CbSubmissionCount = submissionCount;
|
|
||||||
|
|
||||||
cacheIndex = index;
|
entry.InUse = true;
|
||||||
|
entry.CbRefMask = 1u << cbs.CommandBufferIndex;
|
||||||
|
cacheIndex = freeIndex;
|
||||||
|
|
||||||
return entry.DescriptorSet;
|
_pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, freeIndex));
|
||||||
}
|
|
||||||
|
return entry.DescriptorSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise create a new descriptor set, and add to our pending queue for command buffer consumption tracking.
|
||||||
var dsc = _descriptorSetManager.AllocateDescriptorSet(
|
var dsc = _descriptorSetManager.AllocateDescriptorSet(
|
||||||
_gd.Api,
|
_gd.Api,
|
||||||
DescriptorSetLayouts[setIndex],
|
DescriptorSetLayouts[setIndex],
|
||||||
_poolSizes[setIndex],
|
_poolSizes[setIndex],
|
||||||
setIndex,
|
setIndex,
|
||||||
_consumedDescriptorsPerSet[setIndex],
|
_consumedDescriptorsPerSet[setIndex],
|
||||||
false);
|
DescriptorSetLayoutsUpdateAfterBind[setIndex]);
|
||||||
|
|
||||||
cacheIndex = list.Count;
|
cacheIndex = list.Count;
|
||||||
list.Add(new ManualDescriptorSetEntry(dsc, commandBufferIndex, submissionCount, inUse: true));
|
list.Add(new ManualDescriptorSetEntry(dsc, cbs.CommandBufferIndex));
|
||||||
|
_pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex));
|
||||||
|
|
||||||
return dsc;
|
return dsc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateManualDescriptorSetCollectionOwnership(CommandBufferScoped cbs, int setIndex, int cacheIndex)
|
||||||
|
{
|
||||||
|
FreeCompletedManualDescriptorSets();
|
||||||
|
|
||||||
|
var list = _manualDsCache[setIndex];
|
||||||
|
var span = CollectionsMarshal.AsSpan(list);
|
||||||
|
ref var entry = ref span[cacheIndex];
|
||||||
|
|
||||||
|
uint cbMask = 1u << cbs.CommandBufferIndex;
|
||||||
|
|
||||||
|
if ((entry.CbRefMask & cbMask) == 0)
|
||||||
|
{
|
||||||
|
entry.CbRefMask |= cbMask;
|
||||||
|
|
||||||
|
_pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FreeCompletedManualDescriptorSets()
|
||||||
|
{
|
||||||
|
FenceHolder signalledFence = null;
|
||||||
|
while (_pendingManualDsConsumptions.TryPeek(out var pds) && (pds.Fence == signalledFence || pds.Fence.IsSignaled()))
|
||||||
|
{
|
||||||
|
signalledFence = pds.Fence; // Already checked - don't need to do it again.
|
||||||
|
var dequeued = _pendingManualDsConsumptions.Dequeue();
|
||||||
|
Debug.Assert(dequeued.Fence == pds.Fence);
|
||||||
|
pds.Fence.Put();
|
||||||
|
|
||||||
|
var span = CollectionsMarshal.AsSpan(_manualDsCache[dequeued.SetIndex]);
|
||||||
|
ref var entry = ref span[dequeued.CacheIndex];
|
||||||
|
entry.CbRefMask &= ~(1u << dequeued.CommandBufferIndex);
|
||||||
|
|
||||||
|
if (!entry.InUse && entry.CbRefMask == 0)
|
||||||
|
{
|
||||||
|
// If not in use by any array, and not bound to any command buffer, the descriptor set can be re-used immediately.
|
||||||
|
(_freeManualDsCacheEntries[dequeued.SetIndex] ??= new()).Enqueue(dequeued.CacheIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex)
|
public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex)
|
||||||
{
|
{
|
||||||
var list = _manualDsCache[setIndex];
|
var list = _manualDsCache[setIndex];
|
||||||
var span = CollectionsMarshal.AsSpan(list);
|
var span = CollectionsMarshal.AsSpan(list);
|
||||||
|
|
||||||
span[cacheIndex].InUse = false;
|
span[cacheIndex].InUse = false;
|
||||||
|
|
||||||
|
if (span[cacheIndex].CbRefMask == 0)
|
||||||
|
{
|
||||||
|
// This is no longer in use by any array, so if not bound to any command buffer, the descriptor set can be re-used immediately.
|
||||||
|
(_freeManualDsCacheEntries[setIndex] ??= new()).Enqueue(cacheIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier)
|
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier)
|
||||||
@ -291,6 +366,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null);
|
_gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (_pendingManualDsConsumptions.TryDequeue(out var pds))
|
||||||
|
{
|
||||||
|
pds.Fence.Put();
|
||||||
|
}
|
||||||
|
|
||||||
_descriptorSetManager.Dispose();
|
_descriptorSetManager.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
record struct ResourceLayouts(DescriptorSetLayout[] DescriptorSetLayouts, bool[] DescriptorSetLayoutsUpdateAfterBind, PipelineLayout PipelineLayout);
|
||||||
|
|
||||||
static class PipelineLayoutFactory
|
static class PipelineLayoutFactory
|
||||||
{
|
{
|
||||||
public static unsafe (DescriptorSetLayout[], PipelineLayout) Create(
|
public static unsafe ResourceLayouts Create(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||||
bool usePushDescriptors)
|
bool usePushDescriptors)
|
||||||
{
|
{
|
||||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count];
|
DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count];
|
||||||
|
bool[] updateAfterBindFlags = new bool[setDescriptors.Count];
|
||||||
|
|
||||||
bool isMoltenVk = gd.IsMoltenVk;
|
bool isMoltenVk = gd.IsMoltenVk;
|
||||||
|
|
||||||
@ -32,10 +37,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
|
DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
|
||||||
|
|
||||||
|
bool hasArray = false;
|
||||||
|
|
||||||
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||||
{
|
{
|
||||||
ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
|
ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
|
||||||
|
|
||||||
ResourceStages stages = descriptor.Stages;
|
ResourceStages stages = descriptor.Stages;
|
||||||
|
|
||||||
if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
|
if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
|
||||||
@ -52,16 +58,37 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
DescriptorCount = (uint)descriptor.Count,
|
DescriptorCount = (uint)descriptor.Count,
|
||||||
StageFlags = stages.Convert(),
|
StageFlags = stages.Convert(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (descriptor.Count > 1)
|
||||||
|
{
|
||||||
|
hasArray = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
|
fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
|
||||||
{
|
{
|
||||||
|
DescriptorSetLayoutCreateFlags flags = DescriptorSetLayoutCreateFlags.None;
|
||||||
|
|
||||||
|
if (usePushDescriptors && setIndex == 0)
|
||||||
|
{
|
||||||
|
flags = DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gd.Vendor == Vendor.Intel && hasArray)
|
||||||
|
{
|
||||||
|
// Some vendors (like Intel) have low per-stage limits.
|
||||||
|
// We must set the flag if we exceed those limits.
|
||||||
|
flags |= DescriptorSetLayoutCreateFlags.UpdateAfterBindPoolBit;
|
||||||
|
|
||||||
|
updateAfterBindFlags[setIndex] = true;
|
||||||
|
}
|
||||||
|
|
||||||
var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo
|
var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||||
PBindings = pLayoutBindings,
|
PBindings = pLayoutBindings,
|
||||||
BindingCount = (uint)layoutBindings.Length,
|
BindingCount = (uint)layoutBindings.Length,
|
||||||
Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None,
|
Flags = flags,
|
||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
|
gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
|
||||||
@ -82,7 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (layouts, layout);
|
return new ResourceLayouts(layouts, updateAfterBindFlags, layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,244 +71,232 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32);
|
set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float MinDepthBounds
|
|
||||||
{
|
|
||||||
readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 0) & 0xFFFFFFFF));
|
|
||||||
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float MaxDepthBounds
|
|
||||||
{
|
|
||||||
readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id5 >> 32) & 0xFFFFFFFF));
|
|
||||||
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PolygonMode PolygonMode
|
public PolygonMode PolygonMode
|
||||||
{
|
{
|
||||||
readonly get => (PolygonMode)((Internal.Id6 >> 0) & 0x3FFFFFFF);
|
readonly get => (PolygonMode)((Internal.Id5 >> 0) & 0x3FFFFFFF);
|
||||||
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC0000000) | ((ulong)value << 0);
|
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFFC0000000) | ((ulong)value << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint StagesCount
|
public uint StagesCount
|
||||||
{
|
{
|
||||||
readonly get => (byte)((Internal.Id6 >> 30) & 0xFF);
|
readonly get => (byte)((Internal.Id5 >> 30) & 0xFF);
|
||||||
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30);
|
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint VertexAttributeDescriptionsCount
|
public uint VertexAttributeDescriptionsCount
|
||||||
{
|
{
|
||||||
readonly get => (byte)((Internal.Id6 >> 38) & 0xFF);
|
readonly get => (byte)((Internal.Id5 >> 38) & 0xFF);
|
||||||
set => Internal.Id6 = (Internal.Id6 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38);
|
set => Internal.Id5 = (Internal.Id5 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint VertexBindingDescriptionsCount
|
public uint VertexBindingDescriptionsCount
|
||||||
{
|
{
|
||||||
readonly get => (byte)((Internal.Id6 >> 46) & 0xFF);
|
readonly get => (byte)((Internal.Id5 >> 46) & 0xFF);
|
||||||
set => Internal.Id6 = (Internal.Id6 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46);
|
set => Internal.Id5 = (Internal.Id5 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint ViewportsCount
|
public uint ViewportsCount
|
||||||
{
|
{
|
||||||
readonly get => (byte)((Internal.Id6 >> 54) & 0xFF);
|
readonly get => (byte)((Internal.Id5 >> 54) & 0xFF);
|
||||||
set => Internal.Id6 = (Internal.Id6 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
|
set => Internal.Id5 = (Internal.Id5 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint ScissorsCount
|
public uint ScissorsCount
|
||||||
{
|
{
|
||||||
readonly get => (byte)((Internal.Id7 >> 0) & 0xFF);
|
readonly get => (byte)((Internal.Id6 >> 0) & 0xFF);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint ColorBlendAttachmentStateCount
|
public uint ColorBlendAttachmentStateCount
|
||||||
{
|
{
|
||||||
readonly get => (byte)((Internal.Id7 >> 8) & 0xFF);
|
readonly get => (byte)((Internal.Id6 >> 8) & 0xFF);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PrimitiveTopology Topology
|
public PrimitiveTopology Topology
|
||||||
{
|
{
|
||||||
readonly get => (PrimitiveTopology)((Internal.Id7 >> 16) & 0xF);
|
readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogicOp LogicOp
|
public LogicOp LogicOp
|
||||||
{
|
{
|
||||||
readonly get => (LogicOp)((Internal.Id7 >> 20) & 0xF);
|
readonly get => (LogicOp)((Internal.Id6 >> 20) & 0xF);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompareOp DepthCompareOp
|
public CompareOp DepthCompareOp
|
||||||
{
|
{
|
||||||
readonly get => (CompareOp)((Internal.Id7 >> 24) & 0x7);
|
readonly get => (CompareOp)((Internal.Id6 >> 24) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StencilOp StencilFrontFailOp
|
public StencilOp StencilFrontFailOp
|
||||||
{
|
{
|
||||||
readonly get => (StencilOp)((Internal.Id7 >> 27) & 0x7);
|
readonly get => (StencilOp)((Internal.Id6 >> 27) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StencilOp StencilFrontPassOp
|
public StencilOp StencilFrontPassOp
|
||||||
{
|
{
|
||||||
readonly get => (StencilOp)((Internal.Id7 >> 30) & 0x7);
|
readonly get => (StencilOp)((Internal.Id6 >> 30) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StencilOp StencilFrontDepthFailOp
|
public StencilOp StencilFrontDepthFailOp
|
||||||
{
|
{
|
||||||
readonly get => (StencilOp)((Internal.Id7 >> 33) & 0x7);
|
readonly get => (StencilOp)((Internal.Id6 >> 33) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompareOp StencilFrontCompareOp
|
public CompareOp StencilFrontCompareOp
|
||||||
{
|
{
|
||||||
readonly get => (CompareOp)((Internal.Id7 >> 36) & 0x7);
|
readonly get => (CompareOp)((Internal.Id6 >> 36) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StencilOp StencilBackFailOp
|
public StencilOp StencilBackFailOp
|
||||||
{
|
{
|
||||||
readonly get => (StencilOp)((Internal.Id7 >> 39) & 0x7);
|
readonly get => (StencilOp)((Internal.Id6 >> 39) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StencilOp StencilBackPassOp
|
public StencilOp StencilBackPassOp
|
||||||
{
|
{
|
||||||
readonly get => (StencilOp)((Internal.Id7 >> 42) & 0x7);
|
readonly get => (StencilOp)((Internal.Id6 >> 42) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StencilOp StencilBackDepthFailOp
|
public StencilOp StencilBackDepthFailOp
|
||||||
{
|
{
|
||||||
readonly get => (StencilOp)((Internal.Id7 >> 45) & 0x7);
|
readonly get => (StencilOp)((Internal.Id6 >> 45) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompareOp StencilBackCompareOp
|
public CompareOp StencilBackCompareOp
|
||||||
{
|
{
|
||||||
readonly get => (CompareOp)((Internal.Id7 >> 48) & 0x7);
|
readonly get => (CompareOp)((Internal.Id6 >> 48) & 0x7);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CullModeFlags CullMode
|
public CullModeFlags CullMode
|
||||||
{
|
{
|
||||||
readonly get => (CullModeFlags)((Internal.Id7 >> 51) & 0x3);
|
readonly get => (CullModeFlags)((Internal.Id6 >> 51) & 0x3);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PrimitiveRestartEnable
|
public bool PrimitiveRestartEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 53) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 53) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DepthClampEnable
|
public bool DepthClampEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 54) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 54) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
|
set => Internal.Id6 = (Internal.Id6 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RasterizerDiscardEnable
|
public bool RasterizerDiscardEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 55) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 55) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
|
set => Internal.Id6 = (Internal.Id6 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FrontFace FrontFace
|
public FrontFace FrontFace
|
||||||
{
|
{
|
||||||
readonly get => (FrontFace)((Internal.Id7 >> 56) & 0x1);
|
readonly get => (FrontFace)((Internal.Id6 >> 56) & 0x1);
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
|
set => Internal.Id6 = (Internal.Id6 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DepthBiasEnable
|
public bool DepthBiasEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 57) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 57) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
|
set => Internal.Id6 = (Internal.Id6 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DepthTestEnable
|
public bool DepthTestEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 58) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 58) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
|
set => Internal.Id6 = (Internal.Id6 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DepthWriteEnable
|
public bool DepthWriteEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 59) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 59) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
|
set => Internal.Id6 = (Internal.Id6 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DepthBoundsTestEnable
|
public bool DepthBoundsTestEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 60) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 60) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
|
set => Internal.Id6 = (Internal.Id6 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool StencilTestEnable
|
public bool StencilTestEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 61) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 61) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
|
set => Internal.Id6 = (Internal.Id6 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LogicOpEnable
|
public bool LogicOpEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 62) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 62) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
|
set => Internal.Id6 = (Internal.Id6 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasDepthStencil
|
public bool HasDepthStencil
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id7 >> 63) & 0x1) != 0UL;
|
readonly get => ((Internal.Id6 >> 63) & 0x1) != 0UL;
|
||||||
set => Internal.Id7 = (Internal.Id7 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
|
set => Internal.Id6 = (Internal.Id6 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint PatchControlPoints
|
public uint PatchControlPoints
|
||||||
{
|
{
|
||||||
readonly get => (uint)((Internal.Id8 >> 0) & 0xFFFFFFFF);
|
readonly get => (uint)((Internal.Id7 >> 0) & 0xFFFFFFFF);
|
||||||
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint SamplesCount
|
public uint SamplesCount
|
||||||
{
|
{
|
||||||
readonly get => (uint)((Internal.Id8 >> 32) & 0xFFFFFFFF);
|
readonly get => (uint)((Internal.Id7 >> 32) & 0xFFFFFFFF);
|
||||||
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFF) | ((ulong)value << 32);
|
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF) | ((ulong)value << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AlphaToCoverageEnable
|
public bool AlphaToCoverageEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id9 >> 0) & 0x1) != 0UL;
|
readonly get => ((Internal.Id8 >> 0) & 0x1) != 0UL;
|
||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0);
|
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AlphaToOneEnable
|
public bool AlphaToOneEnable
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id9 >> 1) & 0x1) != 0UL;
|
readonly get => ((Internal.Id8 >> 1) & 0x1) != 0UL;
|
||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1);
|
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AdvancedBlendSrcPreMultiplied
|
public bool AdvancedBlendSrcPreMultiplied
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id9 >> 2) & 0x1) != 0UL;
|
readonly get => ((Internal.Id8 >> 2) & 0x1) != 0UL;
|
||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2);
|
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AdvancedBlendDstPreMultiplied
|
public bool AdvancedBlendDstPreMultiplied
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id9 >> 3) & 0x1) != 0UL;
|
readonly get => ((Internal.Id8 >> 3) & 0x1) != 0UL;
|
||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3);
|
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlendOverlapEXT AdvancedBlendOverlap
|
public BlendOverlapEXT AdvancedBlendOverlap
|
||||||
{
|
{
|
||||||
readonly get => (BlendOverlapEXT)((Internal.Id9 >> 4) & 0x3);
|
readonly get => (BlendOverlapEXT)((Internal.Id8 >> 4) & 0x3);
|
||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
|
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DepthMode
|
public bool DepthMode
|
||||||
{
|
{
|
||||||
readonly get => ((Internal.Id9 >> 6) & 0x1) != 0UL;
|
readonly get => ((Internal.Id8 >> 6) & 0x1) != 0UL;
|
||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
|
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasTessellationControlShader;
|
public bool HasTessellationControlShader;
|
||||||
@ -408,8 +396,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
|
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
|
||||||
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
|
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
|
||||||
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
|
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
|
||||||
fixed (Viewport* pViewports = &Internal.Viewports[0])
|
|
||||||
fixed (Rect2D* pScissors = &Internal.Scissors[0])
|
|
||||||
fixed (PipelineColorBlendAttachmentState* pColorBlendAttachmentState = &Internal.ColorBlendAttachmentState[0])
|
fixed (PipelineColorBlendAttachmentState* pColorBlendAttachmentState = &Internal.ColorBlendAttachmentState[0])
|
||||||
{
|
{
|
||||||
var vertexInputState = new PipelineVertexInputStateCreateInfo
|
var vertexInputState = new PipelineVertexInputStateCreateInfo
|
||||||
@ -453,7 +439,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PipelineInputAssemblyStateCreateInfo,
|
SType = StructureType.PipelineInputAssemblyStateCreateInfo,
|
||||||
PrimitiveRestartEnable = primitiveRestartEnable,
|
PrimitiveRestartEnable = primitiveRestartEnable,
|
||||||
Topology = Topology,
|
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
|
||||||
};
|
};
|
||||||
|
|
||||||
var tessellationState = new PipelineTessellationStateCreateInfo
|
var tessellationState = new PipelineTessellationStateCreateInfo
|
||||||
@ -472,18 +458,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
CullMode = CullMode,
|
CullMode = CullMode,
|
||||||
FrontFace = FrontFace,
|
FrontFace = FrontFace,
|
||||||
DepthBiasEnable = DepthBiasEnable,
|
DepthBiasEnable = DepthBiasEnable,
|
||||||
DepthBiasClamp = DepthBiasClamp,
|
|
||||||
DepthBiasConstantFactor = DepthBiasConstantFactor,
|
|
||||||
DepthBiasSlopeFactor = DepthBiasSlopeFactor,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewportState = new PipelineViewportStateCreateInfo
|
var viewportState = new PipelineViewportStateCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineViewportStateCreateInfo,
|
SType = StructureType.PipelineViewportStateCreateInfo,
|
||||||
ViewportCount = ViewportsCount,
|
ViewportCount = ViewportsCount,
|
||||||
PViewports = pViewports,
|
|
||||||
ScissorCount = ScissorsCount,
|
ScissorCount = ScissorsCount,
|
||||||
PScissors = pScissors,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsDepthClipControl)
|
if (gd.Capabilities.SupportsDepthClipControl)
|
||||||
@ -511,19 +492,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
StencilFrontFailOp,
|
StencilFrontFailOp,
|
||||||
StencilFrontPassOp,
|
StencilFrontPassOp,
|
||||||
StencilFrontDepthFailOp,
|
StencilFrontDepthFailOp,
|
||||||
StencilFrontCompareOp,
|
StencilFrontCompareOp);
|
||||||
StencilFrontCompareMask,
|
|
||||||
StencilFrontWriteMask,
|
|
||||||
StencilFrontReference);
|
|
||||||
|
|
||||||
var stencilBack = new StencilOpState(
|
var stencilBack = new StencilOpState(
|
||||||
StencilBackFailOp,
|
StencilBackFailOp,
|
||||||
StencilBackPassOp,
|
StencilBackPassOp,
|
||||||
StencilBackDepthFailOp,
|
StencilBackDepthFailOp,
|
||||||
StencilBackCompareOp,
|
StencilBackCompareOp);
|
||||||
StencilBackCompareMask,
|
|
||||||
StencilBackWriteMask,
|
|
||||||
StencilBackReference);
|
|
||||||
|
|
||||||
var depthStencilState = new PipelineDepthStencilStateCreateInfo
|
var depthStencilState = new PipelineDepthStencilStateCreateInfo
|
||||||
{
|
{
|
||||||
@ -531,12 +506,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
DepthTestEnable = DepthTestEnable,
|
DepthTestEnable = DepthTestEnable,
|
||||||
DepthWriteEnable = DepthWriteEnable,
|
DepthWriteEnable = DepthWriteEnable,
|
||||||
DepthCompareOp = DepthCompareOp,
|
DepthCompareOp = DepthCompareOp,
|
||||||
DepthBoundsTestEnable = DepthBoundsTestEnable,
|
DepthBoundsTestEnable = false,
|
||||||
StencilTestEnable = StencilTestEnable,
|
StencilTestEnable = StencilTestEnable,
|
||||||
Front = stencilFront,
|
Front = stencilFront,
|
||||||
Back = stencilBack,
|
Back = stencilBack,
|
||||||
MinDepthBounds = MinDepthBounds,
|
|
||||||
MaxDepthBounds = MaxDepthBounds,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uint blendEnables = 0;
|
uint blendEnables = 0;
|
||||||
@ -591,22 +564,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
|
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
|
||||||
int dynamicStatesCount = supportsExtDynamicState ? 9 : 8;
|
int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
|
||||||
|
|
||||||
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
|
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
|
||||||
|
|
||||||
dynamicStates[0] = DynamicState.Viewport;
|
dynamicStates[0] = DynamicState.Viewport;
|
||||||
dynamicStates[1] = DynamicState.Scissor;
|
dynamicStates[1] = DynamicState.Scissor;
|
||||||
dynamicStates[2] = DynamicState.DepthBias;
|
dynamicStates[2] = DynamicState.DepthBias;
|
||||||
dynamicStates[3] = DynamicState.DepthBounds;
|
dynamicStates[3] = DynamicState.StencilCompareMask;
|
||||||
dynamicStates[4] = DynamicState.StencilCompareMask;
|
dynamicStates[4] = DynamicState.StencilWriteMask;
|
||||||
dynamicStates[5] = DynamicState.StencilWriteMask;
|
dynamicStates[5] = DynamicState.StencilReference;
|
||||||
dynamicStates[6] = DynamicState.StencilReference;
|
dynamicStates[6] = DynamicState.BlendConstants;
|
||||||
dynamicStates[7] = DynamicState.BlendConstants;
|
|
||||||
|
|
||||||
if (supportsExtDynamicState)
|
if (supportsExtDynamicState)
|
||||||
{
|
{
|
||||||
dynamicStates[8] = DynamicState.VertexInputBindingStrideExt;
|
dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
|
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
|
||||||
@ -632,7 +604,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PDynamicState = &pipelineDynamicStateCreateInfo,
|
PDynamicState = &pipelineDynamicStateCreateInfo,
|
||||||
Layout = PipelineLayout,
|
Layout = PipelineLayout,
|
||||||
RenderPass = renderPass,
|
RenderPass = renderPass,
|
||||||
BasePipelineIndex = -1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle);
|
Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle);
|
||||||
|
@ -17,20 +17,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public ulong Id4;
|
public ulong Id4;
|
||||||
public ulong Id5;
|
public ulong Id5;
|
||||||
public ulong Id6;
|
public ulong Id6;
|
||||||
|
|
||||||
public ulong Id7;
|
public ulong Id7;
|
||||||
|
|
||||||
public ulong Id8;
|
public ulong Id8;
|
||||||
public ulong Id9;
|
|
||||||
|
|
||||||
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id6 >> 38) & 0xFF);
|
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id5 >> 38) & 0xFF);
|
||||||
private readonly uint VertexBindingDescriptionsCount => (byte)((Id6 >> 46) & 0xFF);
|
private readonly uint VertexBindingDescriptionsCount => (byte)((Id5 >> 46) & 0xFF);
|
||||||
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id7 >> 8) & 0xFF);
|
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id6 >> 8) & 0xFF);
|
||||||
private readonly bool HasDepthStencil => ((Id7 >> 63) & 0x1) != 0UL;
|
private readonly bool HasDepthStencil => ((Id6 >> 63) & 0x1) != 0UL;
|
||||||
|
|
||||||
public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions;
|
public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions;
|
||||||
public Array33<VertexInputBindingDescription> VertexBindingDescriptions;
|
public Array33<VertexInputBindingDescription> VertexBindingDescriptions;
|
||||||
public Array16<Viewport> Viewports;
|
|
||||||
public Array16<Rect2D> Scissors;
|
|
||||||
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
|
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
|
||||||
public Array9<Format> AttachmentFormats;
|
public Array9<Format> AttachmentFormats;
|
||||||
public uint AttachmentIntegerFormatMask;
|
public uint AttachmentIntegerFormatMask;
|
||||||
@ -45,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (!Unsafe.As<ulong, Vector256<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id0)) ||
|
if (!Unsafe.As<ulong, Vector256<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id0)) ||
|
||||||
!Unsafe.As<ulong, Vector256<byte>>(ref Id4).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id4)) ||
|
!Unsafe.As<ulong, Vector256<byte>>(ref Id4).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id4)) ||
|
||||||
!Unsafe.As<ulong, Vector128<byte>>(ref Id8).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id8)))
|
!Unsafe.As<ulong, Vector128<byte>>(ref Id7).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id7)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -88,8 +85,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Id5 * 23 ^
|
Id5 * 23 ^
|
||||||
Id6 * 23 ^
|
Id6 * 23 ^
|
||||||
Id7 * 23 ^
|
Id7 * 23 ^
|
||||||
Id8 * 23 ^
|
Id8 * 23;
|
||||||
Id9 * 23;
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
|
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@ -29,10 +31,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags);
|
||||||
|
|
||||||
private readonly TextureView[] _textures;
|
private readonly TextureView[] _textures;
|
||||||
private readonly Auto<DisposableRenderPass> _renderPass;
|
private readonly Auto<DisposableRenderPass> _renderPass;
|
||||||
private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
|
private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
|
||||||
private readonly RenderPassCacheKey _key;
|
private readonly RenderPassCacheKey _key;
|
||||||
|
private readonly List<ForcedFence> _forcedFences;
|
||||||
|
|
||||||
public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
|
public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
|
||||||
{
|
{
|
||||||
@ -105,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var subpassDependency = PipelineConverter.CreateSubpassDependency();
|
var subpassDependency = PipelineConverter.CreateSubpassDependency(gd);
|
||||||
|
|
||||||
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
|
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
|
||||||
{
|
{
|
||||||
@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_textures = textures;
|
_textures = textures;
|
||||||
_key = key;
|
_key = key;
|
||||||
|
|
||||||
|
_forcedFences = new List<ForcedFence>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
|
public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
|
||||||
@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _renderPass;
|
return _renderPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags)
|
||||||
|
{
|
||||||
|
if (!_forcedFences.Any(fence => fence.Texture == storage))
|
||||||
|
{
|
||||||
|
_forcedFences.Add(new ForcedFence(storage, stageFlags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertForcedFences(CommandBufferScoped cbs)
|
||||||
|
{
|
||||||
|
if (_forcedFences.Count > 0)
|
||||||
|
{
|
||||||
|
_forcedFences.RemoveAll((entry) =>
|
||||||
|
{
|
||||||
|
if (entry.Texture.Disposed)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsAttachment(TextureStorage storage)
|
||||||
|
{
|
||||||
|
return _textures.Any(view => view.Storage == storage);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// Dispose all framebuffers.
|
// Dispose all framebuffers.
|
||||||
|
74
src/Ryujinx.Graphics.Vulkan/ResourceArray.cs
Normal file
74
src/Ryujinx.Graphics.Vulkan/ResourceArray.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
class ResourceArray : IDisposable
|
||||||
|
{
|
||||||
|
private DescriptorSet[] _cachedDescriptorSets;
|
||||||
|
|
||||||
|
private ShaderCollection _cachedDscProgram;
|
||||||
|
private int _cachedDscSetIndex;
|
||||||
|
private int _cachedDscIndex;
|
||||||
|
|
||||||
|
private int _bindCount;
|
||||||
|
|
||||||
|
protected void SetDirty(VulkanRenderer gd)
|
||||||
|
{
|
||||||
|
ReleaseDescriptorSet();
|
||||||
|
|
||||||
|
if (_bindCount != 0)
|
||||||
|
{
|
||||||
|
gd.PipelineInternal.ForceTextureDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetCachedDescriptorSets(CommandBufferScoped cbs, ShaderCollection program, int setIndex, out DescriptorSet[] sets)
|
||||||
|
{
|
||||||
|
if (_cachedDescriptorSets != null)
|
||||||
|
{
|
||||||
|
_cachedDscProgram.UpdateManualDescriptorSetCollectionOwnership(cbs, _cachedDscSetIndex, _cachedDscIndex);
|
||||||
|
|
||||||
|
sets = _cachedDescriptorSets;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dsc = program.GetNewManualDescriptorSetCollection(cbs, setIndex, out _cachedDscIndex).Get(cbs);
|
||||||
|
|
||||||
|
sets = dsc.GetSets();
|
||||||
|
|
||||||
|
_cachedDescriptorSets = sets;
|
||||||
|
_cachedDscProgram = program;
|
||||||
|
_cachedDscSetIndex = setIndex;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementBindCount()
|
||||||
|
{
|
||||||
|
_bindCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecrementBindCount()
|
||||||
|
{
|
||||||
|
int newBindCount = --_bindCount;
|
||||||
|
Debug.Assert(newBindCount >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseDescriptorSet()
|
||||||
|
{
|
||||||
|
if (_cachedDescriptorSets != null)
|
||||||
|
{
|
||||||
|
_cachedDscProgram.ReleaseManualDescriptorSetCollection(_cachedDscSetIndex, _cachedDscIndex);
|
||||||
|
_cachedDescriptorSets = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ReleaseDescriptorSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
|
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
|
||||||
{
|
{
|
||||||
int setIndex = type switch
|
int setIndex = type switch
|
||||||
{
|
{
|
||||||
@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
};
|
};
|
||||||
|
|
||||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
||||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages));
|
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user